struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET 协议族*/
in_port_t sin_port; /* port in network byte order 端口号*/
struct in_addr sin_addr; /* internet address IP地址*/
/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
通常服务器在启动的时候都会绑定一个众所周知的地址(如ip地址+端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的ip地址组合。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。但是我认为客户端也可以绑定。
4.3 网络字节序和主机字节序
小端:小字节放前面,大字节放后面
大端:大字节放前面,小字节放后面
uint16_t htons(uint16_t hostshort); //主机字节序->网络字节序
uint16_t ntohs(uint16_t netshort); //网络字节序->主机字节序
htonl(uint32_t hostlong);//主机字节序->网络字节序
ntohl(uint32_t netlong);//网络字节序->主机字节序
4.4 listen监听fd
int listen(fd,size) // 无错返回0,错误返回-1
服务端调用listen()后,开始监听网络上发送给socket的连接请求。
也就是说,开始接收请求了。
4.5 connect发起连接请求
int connect(int sockfd,
const struct sockaddr *serv_addr,
int socklen_t addrlen);
4.6 accept()接收请求建立连接
accept()函数只做两件事,将连接请求从全连接队列中取出,给该连接分配一个fd并返回。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
4.7 消息的发送和接收
read()/write()
recv()/send()
readv()/writev()
recvmsg()/sendmsg()
recvfrom()/sendto()
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count); // 目的fd 消息 消息长度
ssize_t write(int fd, const void *buf, size_t count);
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
4.8 粘包问题
2个数据包同时被提出,但是由于数据是在一起的,没有办法分离
解决方法:
- 在包头添加一个数据包长度的字段,标明长度来确定数据包
- 在包结束后添加分割符,这里注意分割符要选择不经常使用的
注意: 1优于2,因为,添加分割符,需要遍历整个消息来找到分隔符,这样大大影响效率,但是2可以结合1使用。
4.9 close