1. 一些基本知识
1.1 大端存储和小端存储
- 大端:低地址存高位数据,高地址存低位数据
- 小端:低地址存低位数据,高地址存高位数据
- 目前,计算机本地存储一般都采用小端法,而网络流数据采用大端法。
相关函数
uint32_t htonl(uint32_t hostlong); // 将32位的小端字节序转换为大端字节数
uint16_t htons(uint16_t hostshort); // 将16为的小端字节序转换为大端字节序
uint32_t ntohl(uint32_t netlong); // 将32位的大端字节序转换为小端字节序
uint16_t ntohs(uint16_t netshort); // 将16位的大端字节序转换为小端字节序
- h表示host,n表示network,l表示long,s表示short
1.2 IP地址转换函数
int inet_pton(int af, const char *src, void *dst);
- 返回值
- 1:代表转换成功
- 0:
- -1:失败,会产生errno
- af:指明是IPv4还是IPv6
- AF_INET:代表IPv4
- AF_INET6:代表IPv6
- src:点分十进制的ip地址字符串
- dst:传出参数,转换为大端存储的ip地址(网络字节序)
const char *inet_ntop(int af, const void *src,
char *dst, socklen_t size);
- af:指明是IPv4还是IPv6
- src:传入参数,网络字节序
- dst:传出参数,点分十进制的ip地址字符串
- size:指定dst的大小。
1.3 sockaddr数据结构
- 里面整合了ip与port,不过已经被sockaddr_in所代替了。
struct sockaddr_in servaddr;
bind(listen_fd, (struct sockaddr *)&servaddr, sizeof(servaddr));
2. 网络套接字函数
2.1 socket函数:创建一个socket
int socket(int domain, int type, int protocol);
- 返回值:
- 成功:socket的文件描述符fd
- 失败:-1,设置errno
- domain:
- AF_INET:表示用IPv4
- AF_INET6:表示用IPv6
- AF_UNIX:本地协议。一般是客户端和服务端都在同一台机器上
- type
- SOCK_STREAM:基于按照顺序的、可靠的、数据完整的字节流连接
- SOCK_DGRAM:基于无连接、固定长度的传输调用。udp就使用这个
- protocol
- 0:代表使用type指定类型的默认协议
2.2 bind函数:端口绑定
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 返回值:是否成功
- sockfd:socket的文件描述符
- addr:结构体,表示ip地址加端口号
- addrlen:指明结构体大小
struct sockaddr_in servaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
// INADDR_ANY 这个宏表示本地的任意IP地址
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(6666);
2.3 listen函数:设置最大连接数
int listen(int sockfd, int backlog);
- backlog:这个值影响全连接队列的大小。
- socket全连接队列大小=min(somaxconn,backlog)。其中somaxconn是内核参数,表示每个socket最大连接数。
2.4 accept函数:接收连接
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
- sockfd:socket文件描述符
- addr:传出参数,返回客户端地址信息,含IP地址和端口号
- addrlen:传入传出参数,传入sizeof(addr)大小,传出时表示真正接收到到地址结构体大小
- 返回值:
- 成功则返回一个新的socket文件描述符
- 失败返回-1,设置errno
2.5 connect函数
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
- 返回值:是否成功
- sockfd:socket文件描述符
- addr:要连接的服务端地址信息
- addrlen:指明addr的大小
2.6 shutdown函数:关闭socket的文件描述符
- 使用close函数终止一个连接时,它只是减少描述符的引用计数,并不是直接关闭连接,只有当描述符的引用计数为0时才关闭连接
- 使用shutdown不考虑描述符的引用计数个数,直接关闭描述符。
int shutdown(int sockfd, int how);
- 返回值:是否成功
- sockfd:socket的文件描述符
- how
- SHUT_RD:关闭sockfd的读功能,读缓冲区的数据也会被丢弃
- SHUT_WR:关闭sockfd的写功能。
- SHUT_RDWR:关闭sockfd的读写功能
3. 端口复用
- 端口复用最常用的用途应该是防止服务器重启时之前绑定的端口还未释放或者程序突然退出而系统没有释放端口。这种情况下如果设定了端口复用,则新启动的服务器进程可以直接绑定端口。如果没有设定端口复用,绑定会失败。
//开启端口复用,应该在bind前对socket进行端口复用设置
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));