socket相关的体系太庞杂了,这里就遇到一个记录一个。
1 getaddrinfo 解析主机名或服务,并为套接字分配地址信息
这个库函数有4个参数
1.1 解析提示 解析提示的作用是提供一个模板给getaddrinfo
解析提示的使用方式如下
c 1 2 3 4 5 6 memset (&hints,0 ,sizeof (hints)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM;
1.2 参数举例
2 fcntl 之前看过这个系统调用的文档Redis-0x11-库函数fcntl
通过该系统调用读取和设置文件描述符的标志位
2.1 设置socket的非阻塞模式 c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 int anetSetBlock (char *err, int fd, int non_block) { int flags; if ((flags = fcntl(fd, F_GETFL)) == -1 ) { anetSetError(err, "fcntl(F_GETFL): %s" , strerror(errno)); return ANET_ERR; } if (!!(flags & O_NONBLOCK) == !!non_block) return ANET_OK; if (non_block) flags |= O_NONBLOCK; else flags &= ~O_NONBLOCK; if (fcntl(fd, F_SETFL, flags) == -1 ) { anetSetError(err, "fcntl(F_SETFL,O_NONBLOCK): %s" , strerror(errno)); return ANET_ERR; } return ANET_OK; }
2.2 设置close-on-exec c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 int anetCloexec (int fd) { int r; int flags; do { r = fcntl(fd, F_GETFD); } while (r == -1 && errno == EINTR); if (r == -1 || (r & FD_CLOEXEC)) return r; flags = r | FD_CLOEXEC; do { r = fcntl(fd, F_SETFD, flags); } while (r == -1 && errno == EINTR); return r; }
3 setsockopt 设置socket
这个库函数5个形参
sockfd 是指向套接字实例的文件描述符
level 设置项是针对什么级别进行设置的
SOL_SOCKET 表示设置的是套接字级别
IPPROTO_TCP 标识设置的是TCP协议
optname 设置项名称
optval 设置的值
optval 指针指向变量的大小
3.1 设置SO_KEEPALIVE c 1 2 3 4 5 6 if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &val, sizeof (val)) == -1 ) { anetSetError(err, "setsockopt SO_KEEPALIVE: %s" , strerror(errno)); return ANET_ERR; }
3.2 TCP_KEEPIDLE c 1 2 3 4 5 6 7 8 9 if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPIDLE, &val, sizeof (val)) < 0 ) { anetSetError(err, "setsockopt TCP_KEEPIDLE: %s\n" , strerror(errno)); return ANET_ERR; }
3.3 TCP_KEEPINTVL c 1 2 3 4 5 6 7 8 9 if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPINTVL, &val, sizeof (val)) < 0 ) { anetSetError(err, "setsockopt TCP_KEEPINTVL: %s\n" , strerror(errno)); return ANET_ERR; }
3.4 TCP_KEEPCNT c 1 2 3 4 5 6 7 8 9 if (setsockopt(fd, IPPROTO_TCP, TCP_KEEPCNT, &val, sizeof (val)) < 0 ) { anetSetError(err, "setsockopt TCP_KEEPCNT: %s\n" , strerror(errno)); return ANET_ERR; }
3.5 TCP_NODELAY c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 static int anetSetTcpNoDelay (char *err, int fd, int val) { if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof (val)) == -1 ) { anetSetError(err, "setsockopt TCP_NODELAY: %s" , strerror(errno)); return ANET_ERR; } return ANET_OK; }
3.6 SO_SNDTIMEO c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int anetSendTimeout (char *err, int fd, long long ms) { struct timeval tv ; tv.tv_sec = ms/1000 ; tv.tv_usec = (ms%1000 )*1000 ; if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof (tv)) == -1 ) { anetSetError(err, "setsockopt SO_SNDTIMEO: %s" , strerror(errno)); return ANET_ERR; } return ANET_OK; }
3.7 SO_RCVTIMEO c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int anetRecvTimeout (char *err, int fd, long long ms) { struct timeval tv ; tv.tv_sec = ms/1000 ; tv.tv_usec = (ms%1000 )*1000 ; if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof (tv)) == -1 ) { anetSetError(err, "setsockopt SO_RCVTIMEO: %s" , strerror(errno)); return ANET_ERR; } return ANET_OK; }
3.8 SO_REUSEADDR c 1 2 3 4 5 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof (yes)) == -1 ) { anetSetError(err, "setsockopt SO_REUSEADDR: %s" , strerror(errno)); return ANET_ERR; }
4 inet_ntop 库函数原型为const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
作用是将套接字的二进制格式的地址解析成字符串格式
参数为
af 要解析的套接字地址的协议族
AF_INET表示IPv4
AF_INET6表示IPv6.
src 要解析的套接字地址
dst 解析结果是字符串 存到什么地方
size dst字符串的长度
c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 int anetResolve (char *err, char *host, char *ipbuf, size_t ipbuf_len, int flags) { struct addrinfo hints , *info ; int rv; memset (&hints,0 ,sizeof (hints)); if (flags & ANET_IP_ONLY) hints.ai_flags = AI_NUMERICHOST; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; if ((rv = getaddrinfo(host, NULL , &hints, &info)) != 0 ) { anetSetError(err, "%s" , gai_strerror(rv)); return ANET_ERR; } if (info->ai_family == AF_INET) { struct sockaddr_in *sa = (struct sockaddr_in *)info->ai_addr; inet_ntop(AF_INET, &(sa->sin_addr), ipbuf, ipbuf_len); } else { struct sockaddr_in6 *sa = (struct sockaddr_in6 *)info->ai_addr; inet_ntop(AF_INET6, &(sa->sin6_addr), ipbuf, ipbuf_len); } freeaddrinfo(info); return ANET_OK; }
5 socket 创建socket套接字实例
domain 指定通信的地址族 无非就是网络通信或者本地通信
type 指定套接字类型 对应的是运输层的协议类型
SOCK_STREAM表示面向连接的流套接字
SOCK_DGRAM表示无连接的数据报套接字
protocol 使用的协议 通常使用默认的协议 即0
c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 static int anetCreateSocket (char *err, int domain) { int s; if ((s = socket(domain, SOCK_STREAM, 0 )) == -1 ) { anetSetError(err, "creating socket: %s" , strerror(errno)); return ANET_ERR; } if (anetSetReuseAddr(err,s) == ANET_ERR) { close(s); return ANET_ERR; } return s; }
6 connect 连接到服务器的套接字
c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 if (connect(s,(struct sockaddr*)&sa,sizeof (sa)) == -1 ) { if (errno == EINPROGRESS && flags & ANET_CONNECT_NONBLOCK) return s; anetSetError(err, "connect: %s" , strerror(errno)); close(s); return ANET_ERR; }
7 bind 将套接字绑定到一个特定的地址和端口上,这一步对服务端程序尤为重要
服务器需要在一个固定的地址和端口上监听客户端的连接请求
c 1 2 3 4 5 if (bind(s,sa,len) == -1 ) { anetSetError(err, "bind: %s" , strerror(errno)); close(s); return ANET_ERR; }
8 listen 将套接字设置为被动模式,以便接收来自客户端的请求
c 1 2 3 4 5 if (listen(s, backlog) == -1 ) { anetSetError(err, "listen: %s" , strerror(errno)); close(s); return ANET_ERR; }
9 accept 接受连接请求,并在网络服务器编程中扮演关键角色 它从监听套接字队列中获取一个待处理的连接,并返回一个新的套接字文件描述符,用于与客户端进行通信