基础API socket地址API 字节序 CPU的累加器一次至少装在4字节,字节在内存排序即字节序问题
大多PC采用小端:高高低低;大端相反
1 2 3 4 5 6 7 8 9 10 11 12 #include <stdio.h> void byteorder () { union { short value; char union_bytes[sizeof (short )]; }test; test.value = 0x0102 ; if ((test.union_bytes[0 ] == 1 ) && (test.union_bytes[2 ]) == 2 ) printf ("big endian\n" ); else if ((test.union_bytes[0 ] == 2 ) && (test.union_bytes[2 ]) == 1 ) printf ("little endian\n" ); else printf ("unknown\n" ); }
网络字节序即大端字节序,主机于网络字节序转换api
1 2 3 4 5 #include <netinet/in.h> unsigned long int htonl (unsigned long int hostlong) ;unsigned short int htons (unsigned short int hostshort) ;unsigned long int ntohl (unsigned long int netlong) ;unsgined short int ntohs (unsigned short int netshort) ;
socket地址 通用地址sockaddr 1 2 3 4 5 6 7 8 9 10 11 12 13 #include <bits/socket.h> struct sockaddr { sa_family_t sa_family; char sa_data[14 ]; }; struct sockaddr__storage { sa_family_t sa_family; unsigned long int __ss_align; char __ss_padding[128 -sizeof (__s_align)]; };
协议族
内容
PF_UNIX
文件名路径,长度可到108btye
PF_INET
16bit和32bit,6byte
PF_INET6
16bit,32bit流标识,128bitIPv6,32bit范围ID,26byte
AF* PF*值相同,一般混用
专用socket地址 1 2 3 4 5 #include <sys/un.h> struct sockaddr_un {sa_family_t sin_family;char sun_paath[108 ];};
1 2 3 4 5 6 struct sockaddr_in {sa_family_t sin_family;u_int16_t sin_port;struct in_addr sin_addr;}; struct in_addr { u_int32_t s_addr; };
1 2 3 4 5 6 7 8 struct sockaddr_in6 {sa_family_t sin6_family;u_int16_t sin6_port;u_int32_t sin6_flowinfo; struct in6_addr sin6_addr;u_int32_t sin6_scope_id;}; struct in6_addr { unsigned char sa_addr[16 ]; };
字符串ip地址转换 1 2 3 4 #include <arpa/inet.h> in_addr_t inet_addr (const char * strptr) ; int inet_aton (const char * cp, struct in_addr* inp) ; char * inet_ntoa (struct in_addr in) ;
1 2 3 4 5 6 7 int inet_pton (int ad, const char *src, void *dst) ;const char * inet_ntop (int af, const void * src, char * dst, socklen_t cnt) ;#include <netinet/in.h> #define INET_ADDRSTRLEN 16 #define INET6_ADDRSTRLEN 46
socket使用 默认成功返回0,失败返回-1并设置errno
创建 1 2 3 #include <sys/types.h> #include <sys/socket.h> int socket (int dmain, int type, int protocol) ;
type
protocal
SOCK_STREAM, SOCK_UGRAM
SOCK_NONBLOCK, SOCK_CLOEXEC
命名 1 2 3 #include <sys/types.h> #include <sys/socket.h> int bind (int sockfd, const struct sockaddr* my_addr, socklen_t addrlen) ;
errno
含义
EACCES
知名服务端口(0~1023)
EADDRINUSE
正在使用,比如TIME_WAIT
监听并test backlog参数 1 2 #include <sys/socket.h> int listen (int sockfd, int backlog) ;
backlog提示内核监听队列最大长度,在内核版本2.2后,表示全连接状态 的socket上限
半连接状态 :/proc/sys/net/ipv4/tcp_max_syn_backlog内核参数定义,一般为1024
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 #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <signal.h> #include <unistd.h> #include <stdlib.h> #include <assert.h> #include <stdio.h> #include <string.h> #include <stdbool.h> static bool stop = false ;static void handle_term (int sig) { stop = true ; }int main (int argc, char *argv[]) { signal (SIGTERM, handle_term); if (argc < 3 ) { printf ("useage:%s ip_address port_number backlog\n" , argv[0 ]); return 1 ; } const char *ip = argv[1 ]; int port = atoi (argv[2 ]); int backlog = atoi (argv[3 ]); int sock = socket (PF_INET, SOCK_STREAM, 0 ); assert (sock >= 0 ); struct sockaddr_in address; bzero (&address, sizeof (address)); address.sin_family = AF_INET; address.sin_port = htons (port); inet_pton (AF_INET, ip, &address.sin_addr); int ret = bind (sock, (struct sockaddr *)&address, sizeof (address)); assert (ret != -1 ); ret = listen (sock, backlog); assert (ret != -1 ); while (!stop) sleep (1 ); close (sock); return 0 ; }
1 2 3 4 5 ./test_backlog localhost 12345 0 tcp 0 0 127.0.0.1:35604 127.0.0.1:12345 ESTABLISHED tcp 0 0 127.0.0.1:12345 127.0.0.1:35604 ESTABLISHED tcp 0 1 127.0.0.1:35606 127.0.0.1:12345 SYN_SENT // 最多backlog+1个连接
接受连接 1 2 3 #include <sys/types.h> #include <sys/socket.h> int accept (int sockfd, struct sockaddr*addr, socklen_t *addrlen) ;
测试accept如何取出监听队列的连接,
三次握手进入全连接队列,这时候客户端掉线或网络断开,服务端依旧认为是ESTABLISHED
从结果发现accept只负责取出连接,不关心网络状态变化
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 #include <sys.socket.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <assert.h> #include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <errno.h> #include <string.h> int main (int argc, char *argv[]) { if (argc <= 2 ) { printf ("useage:%s ip_address port_number\n" , argv[0 ]); } const char *ip = argv[1 ]; int port = atoi (argv[2 ]); struct sockaddr_in address; bzero (&address, sizeof (address)); address.sin_family = AF_INET; inet_pton (AF_INET, ip, &address.sin_addr); address.sin_port = htons (port); int sock = socket (PF_INET, SOCK_STREAM, 0 ); assert (sock >= 0 ); int ret = bind (sock, (struct sockaddr *)&address, sizeof (address)); assert (ret != -1 ); ret = listen (sock, 5 ); assert (ret != -1 ); sleep (20 ); struct sockaddr_in client; socklen_t client_addrlen = sizeof (client); int connfd = connect (sock, (struct sockaddr *)&client, client_addrlen); if (connfd < 0 ) printf ("errno is:%d\n" , errno); else { char remote[INET_ADDRSTRLEN]; printf ("connected with ip:%s and port:%d\n" , inet_ntop (AF_INET, &client.sin_addr, remote, INET_ADDRSTRLEN), ntohs (client.sin_port)); close (connfd); } close (sock); return 0 ; }
发起连接 1 2 3 #inlcude<sys/types.h> #include <sys/socket.h> int connet (int sockfd, const struct sockaddr* serv_addr, socklen_t addrlen) ;
errno
含义
ECONNREFUSED
目标端口不存在,连接被拒绝
ETIMEDOUT
连接超时
关闭连接 1 2 3 4 #include <unistd.h> int close (int fd) ; #include <sys/socket.h> int shutdown (int sockfd, int howto) ;
howto
含义
SHUT_RD
关闭读通道,丢弃接受缓存区
SHUT_WR
关闭写通道,发送发送缓存区
SHUT_RDWR
同时关闭读写
数据读写 1 2 3 4 #include <sys/types.h> #include <sys/socket.h> 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) ;
send成功返回写入长度,失败返回-1并设置errno(好像并不常见)MSG_MORE,MSG_OOB
带外数据发送和接收 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 #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <assert.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <libgen.h> int main (int argc, char *argv[]) { if (argc <= 2 ) { printf ("usage:%s ip_address port\n" , basename (argv[0 ])); return 1 ; } const char *ip = argv[1 ]; int port = atoi (argv[2 ]); struct sockaddr_in address; memset (&address, 0 , sizeof (address)); address.sin_family = AF_INET; address.sin_port = htons (port); inet_pton (AF_INET, ip, &address.sin_addr); int sockfd = socket (AF_INET, SOCK_STREAM, 0 ); assert (sockfd >= 0 ); if (connect (sockfd, (struct sockaddr *)&address, sizeof (address)) < 0 ) { printf ("connection failed\n" ); } else { const char *oob_data = "abc" ; const char *normal_data = "123" ; send (sockfd, normal_data, strlen (normal_data), 0 ); send (sockfd, oob_data, strlen (oob_data), MSG_OOB); send (sockfd, normal_data, strlen (normal_data), 0 ); } close (sockfd); return 0 ; }
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 52 #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <assert.h> #include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <errno.h> #include <libgen.h> #define BUF_SIZE 1024 int main (int argc, char *argv[]) { if (argc <= 2 ) { printf ("usage:%s ip_address port_number\n" , basename (argv[0 ])); return 1 ; } const char *ip = argv[1 ]; int port = atoi (argv[2 ]); struct sockaddr_in address; socklen_t addrlen = sizeof (address); memset (&address, 0 , addrlen); address.sin_family = AF_INET; address.sin_port = htons (port); inet_pton (AF_INET, ip, &address.sin_addr); int sockfd = socket (AF_INET, SOCK_STREAM, 0 ); assert (sockfd >= 0 ); int ret = bind (sockfd, (struct sockaddr *)&address, addrlen); assert (ret != -1 ); ret = listen (sockfd, 5 ); int connfd = accept (sockfd, (struct sockaddr *)&address, &addrlen); if (connfd < 0 ) printf ("errno is %d\n" , errno); else { char buff[BUF_SIZE]; memset (buff, '\0' , BUF_SIZE); ret = recv (connfd, buff, BUF_SIZE - 1 , 0 ); printf ("get%d bytes of normal data %s\n" , ret, buff); memset (buff, '\0' , BUF_SIZE); ret = recv (connfd, buff, BUF_SIZE - 1 , MSG_OOB); printf ("get%d bytes of oob data %s\n" , ret, buff); memset (buff, '\0' , BUF_SIZE); ret = recv (connfd, buff, BUF_SIZE - 1 , 0 ); printf ("get%d bytes of normal data %s\n" , ret, buff); close (sockfd); } close (sockfd); return 0 ; }
flags仅生效一次,带外数据没有通过MSG_OOB读取的话后面的数据将给截断,
1 2 3 4 5 6 7 get5 bytes of normal data 123ab get1 bytes of oob data c get3 bytes of normal data 123 tcpdump -i lo port 8080 ... 20:37:35.937946 IP localhost.44714 > localhost.webcache: Flags [P.U], seq 4:7, ack 1, win 342, urg 3, options [nop,nop,TS val 1603934628 ecr 1603934628], length 3 ...
UDP数据读写 1 2 3 4 #include <sys/types.h> #include <sys/socket.h> 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) ;
通用数据读写(分散读,集中写) 1 2 3 #include <sys/socket.h> ssize_t recvmsg (int sockfd, struct msghdr* msg, int flags) ;ssize_t sendmsg (int sockfd, struct msghdr* msg, int flags) ;
1 2 3 4 5 6 7 8 9 struct msghdr { void * msg_name; socklen_t msg_namelen; struct iovec * msg_iov; int msg_iovlen; void * msg_contrtol; socklen_t msg_controllen; int msg_flags; };
1 2 3 4 struct iovec { void * iov_base; size_t iov_len; };
带外标记 1 2 #include <sys.socket.h> int sockatmark (int sockfd) ;
获取地址信息 1 2 3 #include <sys/socket.h> int getsockname (int sockfd, struct sockaddr* address, socklen_t * address_len) ;int getpeername (int sockfd, struct sockaddr* address, socklen_t * address_len) ;
socket选项 1 2 3 4 #include <sys/socket.h> int getsockopt (int sockfd, int level, int option_name, void * option_value, socklen_t * __restrict__ option_len) ;int setsockopt (int sockfd, int level, int option_name, const void * option_value, socklen_t option_len) ;
对监听socket设置这些socket选项,那么accept返回的连 接socket将自动继承这些选项。这些socket选项包括:SO_DEBUG、 SO_DONTROUTE、SO_KEEPALIVE、SO_LINGER、 SO_OOBINLINE、SO_RCVBUF、SO_RCVLOWAT、SO_SNDBUF、 SO_SNDLOWAT、TCP_MAXSEG和TCP_NODELAY。而对客户端而 言,这些socket选项则应该在调用connect函数之前设置,因为connect调 用成功返回之后,TCP三次握手已完成。
SO_REUSEADDR 强制使用处于TIME_WAIT状态的sock连接
1 2 int sock = socket (PF_INET,SOCK_STREAM,0 );setsockopt (sock,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof (reuse));
修改内核 参数/proc/sys/net/ipv4/tcp_tw_recycle
SO_RCVBUF和SO_SNDBUF 系统会将设置的值double,并且不小于某个最小值
/proc/sys/net/ipv4/tcp_rmem和/proc/sys/net/ipv4/tcp_wmem
高级IO函数 fd创建 失败返回-1并设置errno
pipe:
1 2 #include <unistd.h> int pipe (int fd[2 ]) ;
双向管道:domain只能用本地协议族,可读可写
1 2 3 #include <sys/types.h> #include <sys/socket.h> int socketpair (int domain,int type,int protocol,int fd[2 ]) ;
dup和dup2 :不继承原来属性
1 2 3 #include <unistd.h> int dup (int file_descriptor) ;int dup2 (int oldfd,int newfd) ;
dup返回描述符最小可用值,dup2会直接关闭newfd
CGI服务器原理:关闭STDOUT_FILENO,dup(connfd)返回1fd,相当于重定位标准输出
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 #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <assert.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <libgen.h> int main (int argc, char *argv[]) { if (argc<=2 ) { printf ("usage:%s ip_address port\b" , basename (argv[0 ])); return 1 ; } const char *ip=argv[1 ]; int port=atoi (argv[2 ]); struct sockaddr_in address; bzero (&address,sizeof (address)); address.sin_family=AF_INET; inet_pton (AF_INET,ip,&address.sin_addr); address.sin_port=htons (port); int sock=socket (PF_INET,SOCK_STREAM,0 ); assert (sock>=0 ); int ret=bind (sock,(struct sockaddr*)&address, sizeof (address)); assert (ret!=-1 ); ret=listen (sock,5 ); assert (ret!=-1 ); struct sockaddr_in client; socklen_t client_addr_len = sizeof (client); int connfd=accept (sock,(struct sockaddr*)&client, &client_addr_len); if (connfd<0 ) printf ("errno: is%d\n" ,errno); else { close (STDOUT_FILENO); dup (connfd); printf ("abcd\n" ); close (connfd); } close (sock); return 0 ; }
数据读写 分散读和集中写 1 2 3 #include <sys/uio.h> ssize_t readv (int fd,const struct iovec*vector,int count) ;ssize_t writev (int fd,const struct iovec*vector,int count) ;
web服务器上集中写
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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <assert.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <libgen.h> #include <sys/stat.h> #include <fcntl.h> #include <stdbool.h> #include <sys/uio.h> #define BUFFER_SIZE 1024 static const char *status_line[2 ] = {"200 OK" , "500 Internal server error" };int main (int argc, char *argv[]) { if (argc <= 3 ) { printf ("usage:%s ip_address port\b" , basename (argv[0 ])); return 1 ; } const char *ip = argv[1 ]; int port = atoi (argv[2 ]); const char *file_name = argv[3 ]; struct sockaddr_in address; bzero (&address, sizeof (address)); address.sin_family = AF_INET; inet_pton (AF_INET, ip, &address.sin_addr); address.sin_port = htons (port); int sock = socket (PF_INET, SOCK_STREAM, 0 ); assert (sock >= 0 ); int ret = bind (sock, (struct sockaddr *)&address, sizeof (address)); assert (ret != -1 ); ret = listen (sock, 5 ); assert (ret != -1 ); struct sockaddr_in client; socklen_t client_addr_len = sizeof (client); int connfd = accept (sock, (struct sockaddr *)&client, &client_addr_len); if (connfd < 0 ) printf ("errno: is%d\n" , errno); else { char header_buf[BUFFER_SIZE]; memset (header_buf, '\0' , BUFFER_SIZE); char *file_buf; struct stat file_stat; bool valid = true ; int len = 0 ; if (stat (file_name, &file_stat) < 0 ) valid = false ; else { if (S_ISDIR (file_stat.st_mode)) valid = false ; else if (file_stat.st_mode & S_IROTH) { int fd = open (file_name, 0 , O_RDONLY); file_buf = new char [file_stat.st_size + 1 ]; memset (file_buf, '\0' , file_stat.st_size + 1 ); if (read (fd, file_buf, file_stat.st_size) < 0 ) valid = false ; } else valid = false ; } if (valid) { ret = snprintf (header_buf, BUFFER_SIZE - 1 , "%s%s\r\n" , "HTTP/1.1" , status_line[0 ]); struct iovec iv[2 ]; iv[0 ].iov_base = header_buf; iv[0 ].iov_len = strlen (header_buf); iv[1 ].iov_base = file_buf; iv[1 ].iov_len = file_stat.st_size; ret = writev (connfd, iv, 2 ); } else { ret = snprintf (header_buf + len, BUFFER_SIZE - 1 , "%s" , "\r\n" ); send (connfd, header_buf, strlen (header_buf), 0 ); } close (connfd); delete [] file_buf; } close (sock); return 0 ; }
零拷贝 sendfile :网络传输文件专用:
in_fd不能是socket和管道,必须是真实文件;out_fd必须是socket
1 2 #include <sys/sendfile.h> ssize_t sendfile (int out_fd,int in_fd,off_t *offset,size_t count) ;
类似上面web程序,但直接从内核读取发送,避免拷贝到用户区
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 #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <assert.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <libgen.h> #include <sys/stat.h> #include <fcntl.h> #include <stdbool.h> #include <sys/sendfile.h> int main (int argc, char *argv[]) { if (argc <= 3 ) { printf ("usage:%s ip_address port\b" , basename (argv[0 ])); return 1 ; } const char *ip = argv[1 ]; int port = atoi (argv[2 ]); const char *file_name = argv[3 ]; int filefd = open (file_name, O_RDONLY); assert (filefd > 0 ); struct sockaddr_in address; bzero (&address, sizeof (address)); address.sin_family = AF_INET; inet_pton (AF_INET, ip, &address.sin_addr); address.sin_port = htons (port); int sock = socket (PF_INET, SOCK_STREAM, 0 ); assert (sock >= 0 ); int ret = bind (sock, (struct sockaddr *)&address, sizeof (address)); assert (ret != -1 ); ret = listen (sock, 5 ); assert (ret != -1 ); struct sockaddr_in client; socklen_t client_addr_len = sizeof (client); int connfd = accept (sock, (struct sockaddr *)&client, &client_addr_len); if (connfd < 0 ) printf ("errno: is%d\n" , errno); struct stat stat_buf; fstat (filefd, &stat_buf); sendfile (connfd, filefd, NULL , stat_buf.st_size); close (connfd); close (filefd); close (sock); return 0 ; }
splice: 必须包含管道文件描述符,且off为NULL
1 2 3 #include <fcntl.h> ssize_t splice (int fd_in,loff_t *off_in,int fd_out,loff_t *off_out,size_t len,unsigned int flags) ;
零拷贝回射服务器:未涉及recv/send操作
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 #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <assert.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <libgen.h> #include <sys/stat.h> #include <fcntl.h> int main (int argc, char *argv[]) { if (argc <= 2 ) { printf ("usage:%s ip_address port\b" , basename (argv[0 ])); return 1 ; } const char *ip = argv[1 ]; int port = atoi (argv[2 ]); struct sockaddr_in address; bzero (&address, sizeof (address)); address.sin_family = AF_INET; inet_pton (AF_INET, ip, &address.sin_addr); address.sin_port = htons (port); int sock = socket (PF_INET, SOCK_STREAM, 0 ); assert (sock >= 0 ); int ret = bind (sock, (struct sockaddr *)&address, sizeof (address)); assert (ret != -1 ); ret = listen (sock, 5 ); assert (ret != -1 ); struct sockaddr_in client; socklen_t client_addr_len = sizeof (client); int connfd = accept (sock, (struct sockaddr *)&client, &client_addr_len); if (connfd < 0 ) printf ("errno: is%d\n" , errno); int pipefd[2 ]; ret = pipe (pipefd); ret = splice (connfd, NULL , pipefd[1 ], NULL , 32768 , SPLICE_F_MORE | SPLICE_F_MOVE); assert (ret != -1 ); ret = splice (pipefd[0 ], NULL , connfd, NULL , 32768 , SPLICE_F_MORE | SPLICE_F_MOVE); assert (ret != -1 ); close (connfd); close (sock); return 0 ; }
共享内存
1 2 3 #include <sys/mman.h> void *mmap (void *start,size_t length,int prot,int flags,int fd,off_t offset) ;int munmap (void *start,size_t length) ;
start为0则地址随机
prot:PROT_READ, PROT_WRITE, PROT_EXEC, PROT_NONE不能访问
tee: 在两个管道间操作
1 2 #include <fcntl.h> ssize_t tee (int fd_in,int fd_out,size_t len,unsigned int flags) ;
IO行为和属性控制 fcntl
1 2 #include <fcntl.h> int fcntl (int fd,int cmd,…) ;
设置非阻塞
1 2 3 4 5 6 7 int sentnonblocking (int fd) { int old_option=fcntl (fd,F_GETFL); int new_option=old_option|O_NONBLOCK; fcntl (fd,F_SETFL,new_option); return old_option; }
Linux服务器程序规范 一般以守护进程运行
拥有日志系统,记录于/var/log
非root身份运行
拥有配置文件,存放于/etc下
启动时生存PID文件并存在/var/run,记录该后台进程PID
高性能服务器框架 服务器模型:
CS模型:通过中心点获取资源;实现简单适合资源集中场合;不适合高访问量
P2P模型:通常带有发现服务器,每台主机即使客户也是服务端
编程框架: 一般基于以下框架,实现不同逻辑功能
对服务器集群来说,IO处理单元是一个专门接入服务器,为实现负载均衡
请求队列通常被实现为池的部分,线程池连接池等
IO模型: 数据读写:根据应用程序和内核的交互方式
五种IO模型:
阻塞
非阻塞
IO复用
信号驱动
前四种内核数据拷贝到用户空间都是阻塞的,看出区别:
同步IO模型通知IO就绪事件,需要你自己执行IO操作
异步IO模型通知IO完成事件,已经写入指定地址
异步
事件处理模型 Reactor:同步 实现:Event事件,Reactor反应堆,Demultiplex事件分发器,EventHandler事件处理器
工作流程:
注册读就绪事件——>epoll_wait监听事件——>分发事件到队列——>工作线程读取事件并处理
Proactor:异步 工作流程:
主线程调用aio_read注册写完成事件,通知内核用户读写缓存区位置
内核发送一个信号,通知数据已可用
信号处理函数选择一个工作线程逻辑处理,完成后注册写完成事件
当数据写入socket后发送信号
信号处理函数选择一个工作线程善后处理
并发模式:
计算密集型和IO密集型
并发模式中,同步指代码顺序执行;异步指程序执行需要由系统事件来驱动,也就是事件驱动模型
半同步/半异步模式:
同步线程处理客户逻辑,异步线程处理IO事件
半同步/半反应堆模式:
主线程监听socket连接,然后往epoll内核事件表注册该socket读写事件;一旦该连接socket有读写事件发生,则将该连接放入工作队列,通知工作线程,通过竞争获得任务接管权
缺点:每次取出任务添加任务都要加锁;一个工作线程同一时间只能处理一个客户请求
提高:每个工作线程都维持自己的事件循环,可用独立监听不同事件
领导者/追随者模式:
每次由一个领导检测IO事件,然后推选新领导以便处理IO事件,包含以下组件
句柄集:
线程集
事件处理器:包含回调函数,绑定到句柄上,当句柄有事件发生,执行回调函数,具体的事件处理器是派生类,执行特定任务
IO复用 信号 定时器 Libevent