Linux高性能服务器编程:网络编程基础API
1. socket地址API
大端字节序:一个整数的高位字节存储在内存的低地址处,低位字节存储在内存的高地址处。
小端字节序:整数的高位字节存储在内存的高地址处,而低位字节则存储在内存的低地址处。
现代PC机采用小端字节序,又称为主机字节序。
大端字节序主要在网络上传输,称为网络字节序。
2. 创建socket
socket是可读、可写、可控制、可关闭的文件描述符。
int socket(int domain, int type, int protocol);
domain: 表示使用哪个底层协议族,对TCP/IP而言,该参数应该设置为PF_INET。
type: 表示服务类型,流服务:SOCK_STREAM, 数据报服务:SOCK_DGRAM。
protocol:0
3.命名socket
采用bind函数。
int bind(int sockfd, const struct sockaddr* my_addr, socklen_t addrlen);
bind将my_addr所指的socket地址分配给未命名的sockfd文件描述符,addrlen指出该socket地址的长度。
绑定失败时,返回-1,设置errno。
常见errno:
EACCES:被绑定的地址是受保护的地址。
EADDRINUSE:被绑定的地址在使用中,如将socket绑定到一个处于TIME_WAIT状态的socket地址。
4. 监听socket
int listen(int sockfd, int backlog);
sockfd指定被监听的socket。backlog提示内核监听队列的最大长度。监听队列长度超过backlog,服务器将不受理新的客户连接。典型值为5..
5.接受连接
int accept(int sockfd, struct sockaddr* addr, socklen_t* addrlen);
sockfd是执行过listen系统调用的监听socket。addr参数用来获取被接受连接的远端socket地址,该socket
地址的长度由addrlen指出。accept成功时返回一个新的连接socket,该socket唯一地标识了
被接受的这个连接,服务器可通过读写该socket来与被接受连接的客户端通信。
accept只是从监听队列中取出连接,而不论连接处于何种状态,更不关心任何网络状况的变化。
6. 发起连接
int connect(int sockfd, const struct sockaddr* serv_addr, socklen_t addrlen);
connect成功时返回0,一旦成功建立连接,sockfd就唯一地标识了这个连接,客户端就可以通过读写sockfd来与服务器通信。
7.关闭连接
int close(int fd);
close将fd的引用计数减1,只有当fd的引用计数为0时,才真正关闭连接。多进程程序中,一次fork系统调用默认将使父进程中打开的socket的引用计数加1,
因此我们必须在父进程和子进程中都对socket执行close调用才能将连接关闭。
若要立即终止连接,可以使用:
int shutdown(int sockfd, int howto);
8. 数据读写
TCP读写:
ssize_t recv(int sockfd, void* buf, size_t len, int flags);
ssize_t send(int sockfd, const void* buf, size_t len, int flags);
recv读取sockfd上的数据,buf和len分别指定读缓冲区的位置和大小,falgs通常为0. recv成功时返回实际读取到的数据的长度。recv返回0,表示通信对方已经关闭连接。
send往sockfd上写入数据,buf和len分别指定写缓冲区的位置和大小。send成功时返回实际写入的数据的长度。
UDP读写:
ssize_t recvfrom(int sockfd, void* buf, size_t len, int flags, struct sockaddr* src_addr, socklen_t* addrlen);
ssize_t sendto(int sockfd, const void* buf, size_t len, int flags, const struct sockaddr* dest_addr, socklen_t addrlen);
recvfrom 读取sockfd上的数据,buf和len参数分别指定读缓冲区的位置和大小。每次读取数据时都需要获取发送端的socket地址。
sendto往sockfd上写入数据,buf和len分别指定写缓冲区的位置和大小。
recvfrom和sendto系统调用也可以用于面向连接的socket数据读写,只需要把最后两个参数都设置为NULL以忽略发送端/接收端的socket地址(因为已经建立了连接)。
通用数据读写:
ssize_t recvmsg(int sockfd, struct msghdr* msg, int flags);
ssize_t sendmsg(itn sockfd, struct msghdr* msg, int flags);
可用于TCP流数据和UDP数据报。
9. 地址信息函数
int getsockname(int sockfd, struct sockaddr* address, socklen_t* address_len); //获取sockfd对应的本端socket地址
int getpeername(int sockfd, struct sockaddr* address, socklen_t* address_len); //获取sockfd对应的远端socket地址
通过设置socket选项SO_REUSEADDR来强制使用被处于TIME_WAIT状态的连接占用的socket地址。