4. API解释 4.1 sockaddr_in
这是socket地址的基本数据结构:
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 */ }; /* Internet address. */ struct in_addr { uint32_t s_addr; /* address in network byte order */ }; 一个IP socket地址被定义为一个IP地址和16位的端口,基本IP协议不支持端口号,而是由上层协议udp/tcp来实现。 4.2 socket函数 int sockfd = socket(int socket_family, int socket_type, int protocol); 4.3 bind函数 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); struct sockaddr { sa_family_t sa_family; char sa_data[14]; }
当socket通过socket函数创建好之后,他存在在一个地址协议族的名字空间中,但是没有为他分配地址,bind就是将一个地址分配给socket描述符。
这里sa_data没有用,完全是为了将sockaddr_in强制转换时内存大小一样,不会报错。
错误码见文档。
4.4 listen函数 int listen(int sockfd, int backlog);
listen函数将这个socket标记为被动socket,意思是可以用accept来接收别人的连接请求。
backlog参数指定了等待连接队列的最大长度,当队列满了之后,新的客户端连接会收到一个ECONNREFUSED连接错误。
4.5 accept int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
accept函数用在面向连接的socket类型,例如SOCK_STREAM, SOCK_SEQPACKET,它会将请求等待队列的第一个连接请求提取出来,创建一个新的连接socket,返回这个socket的fd。
第一个参数sockfd就是刚刚使用socket函数创建、使用bind绑定本地地址、并使用listen标记为被动的那个sockfd。
第二个参数sockaddr指针指向一个客户端地址的结构体。
第三个参数addrlen是一个传出的参数,必须先用sizeof(sockaddr)初始化,函数返回时会将其赋值成客户端地址这个结构体的真实大小。如果客户端地址结构体的大小大于sizeof(sockaddr),那么会被阶段,并在addrlen中带回真正的大小。
如果连接等待队列中没有连接请求,默认情况下accept()会阻塞,直到有新的客户端连接出现;如果对socket指定了非阻塞,那么会返回错误码EAGAIN 或者EWOULDBLOCK
4.6 read/recv与write/send int recv(int sockfd,void *buf,int len,int flags); int send(int sockfd,void *buf,int len,int flags);注意到上边代码中使用了read,实际上也可以使用recv,后者提供了第四个参数flag,用来标记一些socket的状态:
MSG_DONTROUTE:不查找路由表 MSG_OOB:接受或发送带外数据 MSG_PEEK:查看数据,并不从系统缓冲区移走数据 MSG_WAITALL :等待任何数据其中:
MSG_DONTROUTE:是send函数使用的标志.这个标志告诉IP协议.目的主机在本地网络上面,没有必要查找路由表.这个标志一般用网络诊断和路由程式里面。
MSG_OOB:表示能够接收和发送带外的数据.关于带外数据我们以后会解释的.
MSG_PEEK:是recv函数的使用标志,表示只是从系统缓冲区中读取内容,而不清除系统缓冲区的内容。这样下次读的时候,仍然是相同的内容。一般在有多个进程读写数据时能够使用这个标志。
MSG_WAITALL:是recv函数的使用标志,表示等到任何的信息到达时才返回。使用这个标志的时候recv会一直阻塞,直到指定的条件满足,或是发生了错误。 1)当读到了指定的字节时,函数正常返回,返回值等于len 2)当读到了文档的结尾时,函数正常返回.返回值小于len 3)当操作发生错误时,返回-1,且配置错误为相应的错误号(errno).
write/send的区别同理。