网络结构模式 C/S 结构 MAC地址、IP地址、端口 MAC地址48位,OSI第二层(数据链路层) 网卡(以太网、无线网)功能:
数据的封装与解封装
链路管理
数据编码与译码
MAC: 前三个字节:制造商 后三个字节:制造商的某个产品 ifconfig
IP A 1.0.0.1 126.255.255.254 10.(私有地址) B 128.0.0.1 191.255.255.254 172.16~172.31 C 192.0.0.1 233.255.255.254 192.168.
网络IP + 主机IP 网络地址,广播地址。 A类126个 B类16384个网络 127.* 测试回路 子网掩码
Port 标识进程,一个应用程序可有多个进程 虚拟端口 物理端口(接口) 65535个 0-1023 周知端口 1024-49151:注册端口 私有/动态端口 4.4 网络模型 OSI7层网络模型
应用层 应用程序
表示层 数据的表示、安全、压缩
会话层 建立、管理和连接会话
传输层 建立、管理和维护端到端的链接,传输数据的协议、端口号,TCP/UDP 段
网络层 IP选址和路由选择
数据链路层 建立逻辑连接,介质访问和链路管理LLC+MAC,差错检验,帧
物理层 比特 TCP/IP协议族 应用层: 传输层:TCP、UDP 网络层:IP,ICMP,IGMP 网络接口层:ARP,RARP 上层协议使用下层协议的服务
协议 协议,网络协议的简称,网络协议是通信计算机双方必须共同遵从的一组約定。如怎么样建立连接、怎么样互相识别等。只有遵守这个约定,计算机之间才能相互通信交流。它的三要素是:语法、语义、时序。为了使数据在网络上从源到达目的,网络通信的参与方必须遵循相同的规则,这套规则称为协议。
1 2 netstat ps aux | grep ssh
UDP:
16位端口号,16位目的端口号 16位UDP长度,16位校验和
TCP:
16位元端口,16位目的端口 32位序号 32位确认号 URG,ACK,PSH,RST,SYN,FIN;16位窗口大小 16位校验和,16位紧急指针
IP:
16位总字节数 16位标识,3位标识,13位偏移 8位TTL,8位协议(上层的协议) 网络通信的过程 封装,分用(解析下层协议中的头来传送给对应的上层协议)
socket 介绍 文件描述符,读写缓冲区 服务器端 客户端
字节序 Big-Endian: 最高位在低地址处 Little-Endian:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <stdio.h> int main () { union { short value; char bytes[sizeof (short )]; }test; test.value = 0x0102 ; if ( (test.bytes[0 ]==1 ) && (test.bytes[1 ]==2 ) ) { printf ("Big-Endian" ); }else printf ("Little-Endian" ); return 0 ; }
字节序转换函数 网络字节序:Big-Endian
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 #include <arpa/inet.h> uint16_t htons (uint16_t hostshort) ;uint16_t ntohs (uint16_t netshort) ;uint32_t htonl (uint32_t hostlong) ;uint32_t ntohl (uint32_t netlong) ;#include <stdio.h> #include <arpa/inet.h> int main () { unsigned short a = 0x0102 ; printf (": %x\n" , a); unsigned short b = htons(a); printf ("b : %x\n" , b); return 0 ; char buf [4 ] = {192 , 168 , 1 , 100 ); int num = *(int *)buf; int sum = hton1 (num); unsigned char *p = (char *)∑ 34 35 printf ("%d %d %d %d\n" , *p, * (p+1 ), * (p+2 ), *(p+3 )); }
socket 地址 IP 地址转换函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <arpa/inet.h> int inet_pton (int af, const char *src, void *dst) ; const char *inet_ntop (int af, const void *src, char *dst, socklen_t size) ;#include <stdio.h> #include <arpa/inet.h> int main () { char buf[] = "192.168.10.14" ; unsigned int num = 0 ; inet_pton(AF_INET, buf, &num); unsigned char *p = (unsigned char *)# printf ("%d.%d.%d.%d\n" , p[0 ], p[1 ], p[2 ], p[3 ]); char ip[16 ]; const char * str = inet_ntop(AF_INET, &num, ip, sizeof (ip)); printf ("%s\n" , str); return 0 ; }
TCP 通信流程 UDP: 无连接,单播、多播、广播,不可靠 TCP:面向连接的,可靠,面向字节流,仅支持单播传输 服务器端
建立用于链接的监听套接字(文件描述符)
将监听的文件描述符和本地的IP和端口绑定
设置监听
阻塞等待
通信
通信结束,断开连接 客户端:
创建用于通信的套接字
连接服务器,指定IP和port
通信
通信结束,断开连接
socket 函数 1 2 3 4 5 6 7 8 9 10 11 #include <sys/types.h #include <sys/socket.h> #include <arpa/inet.h> int socket (int domain, int type, int protocol) ; int bind (int sockfd, const struct sockaddr addr, socklen_t addrlen) ;int listen (int sockfd, int backlog) ; int accept (int sockfd, struct sockaddr *addr, socklen_t *addrlen) ;int connect (int sockfd, const struct sockaddr *addr, socklen_t addrlen) ;ssize_t write (int fd, const void *buf, size_t count) ; ssize_t read (int fd, void *buf, size_t count) ;
TCP通信实现(服务器端) 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 #include <stdio.h> #include <arpa/inet.h> #include <stdlib.h> #include <string.h> #include <unistd.h> int main () { int lfd = socket(AF_INET, SOCK_STREAM, 0 ); struct sockaddr_in saddr ; saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = INADDR_ANY; saddr.sin_port = htons(9999 ); int ret = bind(lfd, (struct sockaddr*)&saddr, sizeof (saddr)); if (ret==-1 ) { perror("bind" ); exit (0 ); } ret = listen(lfd, 8 ); if (ret==-1 ) { perror("listen" ); exit (0 ); } struct sockaddr_in clientaddr ; socklen_t len = sizeof (clientaddr); int cfd = accept(lfd, (struct sockaddr*)&clientaddr, &len); if (cfd==-1 ) { perror("accept" ); exit (0 ); } char clientIP[16 ]; inet_ntop(AF_INET, &clientaddr.sin_addr.s_addr, clientIP, sizeof (clientIP)); unsigned short clientPort = ntohs(clientaddr.sin_port); printf ("client ip is %s, port:%d\n" , clientIP, clientPort); while (1 ) { char recvBuf[1024 ] = {0 }; int recvlen = read(cfd, recvBuf, sizeof (recvBuf)); if (recvlen==-1 ) { perror("read" ); exit (-1 ); }else if (recvlen>0 ) { printf ("received: %s\n" , recvBuf); }else { printf ("connection closed\n" ); break ; } char *sstr = "Hello, I'm server." ; write(cfd, sstr, strlen (sstr)); } close(cfd); close(lfd); return 0 ; }
TCP通信实现(客户端) 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 #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <arpa/inet.h> int main () { int fd = socket(AF_INET, SOCK_STREAM, 0 ); struct sockaddr_in clientSockerAddr ; inet_pton(AF_INET, "127.0.0.1" , &clientSockerAddr.sin_addr.s_addr); clientSockerAddr.sin_port = htons(9999 ); clientSockerAddr.sin_family = AF_INET; int ret = connect(fd, (struct sockaddr*)&clientSockerAddr, sizeof (clientSockerAddr)); if (ret == -1 ) { perror("connect" ); exit (0 ); } char *data = "Hello, I'm cllient." ; write(fd, data, strlen (data)); char recvBuf[1024 ] = {0 }; int recvlen = read(fd, recvBuf, sizeof (recvBuf)); if (recvlen==-1 ) { perror("read" ); exit (-1 ); }else if (recvlen>0 ) { printf ("received message from server: %s\n" , recvBuf); }else { printf ("server closed\n" ); } close(fd); return 0 ; }
TCP三次握手 滑动窗口 TCP四次挥手 多进程实现并发服务器(1) 一个父进程,多个子进程 父进程:等待连接 子进程:完成通信
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 #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/stat.h> int main () { int lfd = socket(AF_INET, SOCK_STREAM, 0 ); if (lfd==-1 ) { perror("socket" ); exit (0 ); } struct sockaddr_in serverAddr ; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(9999 ); serverAddr.sin_addr.s_addr = INADDR_ANY; int ret = bind(lfd, (struct sockaddr*)&serverAddr, sizeof (serverAddr)); if (ret==-1 ) { perror("bind" ); exit (-1 ); } ret = listen(lfd, 8 ); if (ret==-1 ) { perror("listen" ); exit (0 ); } while (1 ) { struct sockaddr_in clientAddr ; int len = sizeof (clientAddr); int cfd = accept(lfd, (struct sockaddr*)&clientAddr, &len); if (cfd==-1 ) { perror("accept" ); exit (0 ); } pid_t pid = fork(); if (pid==0 ) { char ip[16 ] = {0 }; inet_ntop(AF_INET, &clientAddr.sin_addr.s_addr, ip, sizeof (ip)); unsigned short port = ntohs(clientAddr.sin_port); printf ("client ip:%s, port:%d\n" , ip, port); char recvBuf[1024 ]; while (1 ) { int len = read(cfd, recvBuf, sizeof (recvBuf)); if (len==-1 ) { perror("read" ); exit (0 ); }else if (len==0 ) { printf ("client closed\n" ); }else { printf ("server received message:%s\n" , recvBuf); } write(cfd, recvBuf, strlen (recvBuf)); } close(cfd); exit (0 ); } } close(lfd); return 0 ; }
多进程实现并发服务器(2) 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 89 90 91 92 93 94 95 96 #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/stat.h> #include <signal.h> #include <wait.h> #include <errno.h> void recycleChild (int arg) { while (1 ) { int ret = waitpid(-1 , NULL , WNOHANG); if (ret==-1 ) { break ; }else if (ret==0 ) { break ; }else { printf ("child %d was recyled\n" , ret); } } } int main () { struct sigaction act ; act.sa_flags = 0 ; sigemptyset(&act.sa_mask); act.sa_handler = recycleChild; sigaction(SIGCHLD, &act, NULL ); int lfd = socket(AF_INET, SOCK_STREAM, 0 ); if (lfd==-1 ) { perror("socket" ); exit (0 ); } struct sockaddr_in serverAddr ; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(9999 ); serverAddr.sin_addr.s_addr = INADDR_ANY; int ret = bind(lfd, (struct sockaddr*)&serverAddr, sizeof (serverAddr)); if (ret==-1 ) { perror("bind" ); exit (-1 ); } ret = listen(lfd, 8 ); if (ret==-1 ) { perror("listen" ); exit (0 ); } while (1 ) { struct sockaddr_in clientAddr ; int len = sizeof (clientAddr); int cfd = accept(lfd, (struct sockaddr*)&clientAddr, &len); if (cfd==-1 ) { if (errno == EINTR) continue ; else { perror("accept" ); exit (0 ); } } pid_t pid = fork(); if (pid==0 ) { char ip[16 ] = {0 }; inet_ntop(AF_INET, &clientAddr.sin_addr.s_addr, ip, sizeof (ip)); unsigned short port = ntohs(clientAddr.sin_port); printf ("client ip:%s, port:%d\n" , ip, port); char recvBuf[1024 ]; while (1 ) { int len = read(cfd, recvBuf, sizeof (recvBuf)); if (len==-1 ) { perror("read" ); exit (0 ); }else if (len==0 ) { printf ("client closed\n" ); }else { printf ("server received message:%s\n" , recvBuf); } write(cfd, recvBuf, strlen (recvBuf)); } close(cfd); exit (0 ); } } close(lfd); 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 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 89 90 91 92 93 94 95 96 97 98 #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> #include <arpa/inet.h> #include <string.h> struct sockInfo { int fd; pthread_t tid; struct sockaddr_in addr ; }; struct sockInfo sockinfos [128];void *working (void * arg) { struct sockInfo *pinfo = (struct sockInfo*)arg; char ip[16 ] = {0 }; inet_ntop(AF_INET, &pinfo->addr.sin_addr.s_addr, ip, sizeof (ip)); unsigned short port = ntohs(pinfo->addr.sin_port); printf ("client ip:%s, port:%d\n" , ip, port); char recvBuf[1024 ]; while (1 ) { int len = read(pinfo->fd, recvBuf, sizeof (recvBuf)); if (len==-1 ) { perror("read" ); exit (0 ); }else if (len==0 ) { printf ("client closed\n" ); break ; }else { printf ("server received message:%s\n" , recvBuf); } write(pinfo->fd, recvBuf, strlen (recvBuf)); } close(pinfo->fd); return NULL ; } int main () { int lfd = socket(AF_INET, SOCK_STREAM, 0 ); if (lfd==-1 ) { perror("socket" ); exit (0 ); } struct sockaddr_in serverAddr ; serverAddr.sin_family = AF_INET; serverAddr.sin_port = htons(9999 ); serverAddr.sin_addr.s_addr = INADDR_ANY; int ret = bind(lfd, (struct sockaddr*)&serverAddr, sizeof (serverAddr)); if (ret==-1 ) { perror("bind" ); exit (-1 ); } ret = listen(lfd, 128 ); if (ret==-1 ) { perror("listen" ); exit (0 ); } int max = sizeof (sockinfos)/sizeof (sockinfos[0 ]); for (int i=0 ; i<max; i++) { bzero(&sockinfos[i], sizeof (sockinfos[i])); sockinfos[i].fd = -1 ; sockinfos[i].tid = -1 ; } while (1 ) { struct sockaddr_in clientAddr ; int len = sizeof (clientAddr); struct sockInfo *pinfo ; for (int i=0 ; i<max; i++) { if (sockinfos[i].fd == -1 ) { pinfo = &sockinfos[i]; break ; } if (i==max-1 ) { sleep(1 ); i--; } } pinfo->fd = accept(lfd, (struct sockaddr*)&clientAddr, &len); memcpy (&pinfo->addr, &clientAddr, sizeof (clientAddr)); pthread_create(&pinfo->tid, NULL , working, (void *)pinfo); pthread_detach(pinfo->tid); } close(lfd); return 0 ; }
TCP状态转换 半关闭、端口复用 1 2 3 4 netstat -a all -p 正在使用socket的程序的名称 -n 直接显示ipaddress
端口复用最常用的用途是:
防止服务器重启时之前绑定的端口还未释放(TIME_WAIT)
程序突然退出而系统没有释放端口1 2 3 4 5 #include <sys/socket.h> int setsockopt (int socket, int level, int option_name, const void *option_value, socklen_t option_len) ;
IO多路复用简介 IO多路复用/IO多路转接 IO多路复用使得程序能同时监控多个文件描述符,能够提高程序的性能,Linux下实现 I/0 多路复用的系统调用主要有 select、poll 和epoll. 阻塞等待,BIO模型 好处:不占CPU时间片 缺点:同一时刻只能处理一个操作 多线程或多进程 缺点:线程/进程调度消耗CPU资源 每个线程/进程一个Client 非阻塞,忙轮询,NIO模型 优点:提高了程序的执行效率 缺点:需要占用更多的CPU和系统资源 IO多路转接技术
select API介绍 主旨思想
首先要构造一个关于文件描述符的列表,将要监听的文件描述符添加到该列表中。
调用一个系统逐数,监听该列表中的文件玉达行,直到这些街达符中的一个或者多个进行1/0操作时,该函数才返回。
这个函数是阻塞
函数对文件描述符的检测的操作是由内核完成的
在返回时,它会告诉进程有多少(哪些) 描述符要进行I/0操作。
1 2 3 4 5 6 7 8 9 int select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) ;void FD_CLR (int fd, fd_set *set ) ;int FD_ISSET (int fd, fd_set *set ) ;void FD_SET (int fd, fd_set *set ) ;void FD_ZERO (fd_set *set ) ;
缺点:
每次调用select,都需要把fd集合从用广态拷贝到内核态,这个开销在fd很多时会很大
同时每次调用select都需妥在内核遍历传递进来的所有fd,这个开销在fa很多时也很大
sclect支持的文件描述符数量太小了,默认是1024
fdls集合不能重用,每次都需要重置
select代码编写 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 #include <stdio.h> #include <arpa/inet.h> #include <stdlib.h> #include <sys/select.h> #include <unistd.h> #include <string.h> int main () { int lfd = socket(AF_INET, SOCK_STREAM, 0 ); struct sockaddr_in saddr ; saddr.sin_port = htons(9999 ); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = INADDR_ANY; bind(lfd, (struct sockaddr*)&saddr, sizeof (saddr)); listen(lfd, 8 ); fd_set rdset, tmp; FD_ZERO(&rdset); FD_SET(lfd, &rdset); int maxfd = lfd; while (1 ) { tmp = rdset; int ret = select(maxfd+1 , &tmp, NULL , NULL , NULL ); if (ret==-1 ){ perror("select" ); exit (-1 ); }else if (ret==0 ) { continue ; }else { if (FD_ISSET(lfd, &tmp)) { struct sockaddr_in clientAddr; int len = sizeof (clientAddr); int cfd = accept(lfd, (struct sockaddr*)&clientAddr, &len); FD_SET(cfd, &rdset); maxfd = maxfd > cfd ? maxfd : cfd; } for (int i=lfd+1 ; i<=maxfd; i++) { if (FD_ISSET(i, &tmp)) { char buf[1024 ] = {0 }; int len = read(i, buf, sizeof (buf)); if (len==-1 ) { perror("read" ); exit (-1 ); }else if (len==0 ) { printf ("client closed.\n" ); close(i); FD_CLR(i, &rdset); }else { printf ("read: %s\n" , buf); write(i, buf, strlen (buf)+1 ); } } } } } close(lfd); return 0 ; }
poll API介绍及代码编写 1 2 3 4 5 6 struct pollfd { int fd; short events; short revents; }; int poll (struct pollfd *fds, nfds_t nfds, int timeout) ;
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 #include <stdio.h> #include <arpa/inet.h> #include <stdlib.h> #include <sys/poll.h> #include <unistd.h> #include <string.h> int main () { int lfd = socket(AF_INET, SOCK_STREAM, 0 ); struct sockaddr_in saddr ; saddr.sin_port = htons(9999 ); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = INADDR_ANY; bind(lfd, (struct sockaddr*)&saddr, sizeof (saddr)); listen(lfd, 8 ); struct pollfd fds [1024]; for (int i=0 ; i<1024 ; i++) { fds[i].fd = -1 ; fds[i].events = POLLIN; } fds[0 ].fd = lfd; int nfds = 0 ; while (1 ) { int ret = poll(fds, nfds+1 , -1 ); if (ret==-1 ){ perror("poll" ); exit (-1 ); }else if (ret==0 ) { continue ; }else { if (fds[0 ].revents & POLLIN ) { struct sockaddr_in clientAddr; int len = sizeof (clientAddr); int cfd = accept(lfd, (struct sockaddr*)&clientAddr, &len); for (int i=1 ; i<1024 ; i++) { if (fds[i].fd ==-1 ){ fds[i].fd = cfd; fds[i].events = POLLIN; nfds = nfds > cfd ? nfds: cfd; break ; } } } for (int i=1 ; i<=nfds; i++) { if (fds[i].revents & POLLIN == POLLIN) { char buf[1024 ] = {0 }; int len = read(fds[i].fd, buf, sizeof (buf)); if (len==-1 ) { perror("read" ); exit (-1 ); }else if (len==0 ) { printf ("client closed.\n" ); close(fds[i].fd); fds[i].fd = -1 ; }else { printf ("read: %s\n" , buf); write(fds[i].fd, buf, strlen (buf)+1 ); } } } } } close(lfd); return 0 ; }
epoll API介绍 Epoll
1 2 3 4 struct eventpoll { struct rb_root rbr ; struct list_head rdlist ; }
epoll 代码编写 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 #include <stdio.h> #include <sys/epoll.h> #include <arpa/inet.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main () { int lfd = socket(AF_INET, SOCK_STREAM, 0 ); struct sockaddr_in saddr ; saddr.sin_port = htons(9999 ); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = INADDR_ANY; bind(lfd, (struct sockaddr*)&saddr, sizeof (saddr)); listen(lfd, 8 ); int epfd = epoll_create(100 ); struct epoll_event epev ; epev.events = EPOLLIN; epev.data.fd = lfd; epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &epev); struct epoll_event epevs [1024]; while (1 ) { int ret = epoll_wait(epfd, epevs, 1024 , -1 ); if (ret==-1 ) { perror("epoll_wait" ); exit (-1 ); } printf ("ret = %d\n" , ret); for (int i=0 ; i<ret; i++) { if (epevs[i].data.fd == lfd) { struct sockaddr_in clientAddr ; int len = sizeof (clientAddr); int cfd = accept(lfd, (struct sockaddr*)&clientAddr, &len); epev.events = EPOLLIN; epev.data.fd = cfd; epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &epev); }else { int cfd = epevs[i].data.fd; char buf[1024 ] = {0 }; int len = read(cfd, buf, sizeof (buf)); if (len==-1 ) { perror("read" ); exit (-1 ); }else if (len==0 ) { printf ("client closed.\n" ); close(cfd); epoll_ctl(epfd, EPOLL_CTL_DEL, cfd, NULL ); }else { printf ("read: %s\n" , buf); write(cfd, buf, strlen (buf)+1 ); } } } } close(lfd); close(epfd); return 0 ; }
epoll的两种工作模式 LT level-triggered 水平触发(缺省) ET edge-triggered 边沿触发,或高速触发模式,只支持非阻塞
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 #include <stdio.h> #include <sys/epoll.h> #include <arpa/inet.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> int main () { int lfd = socket(AF_INET, SOCK_STREAM, 0 ); struct sockaddr_in saddr ; saddr.sin_port = htons(9999 ); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = INADDR_ANY; bind(lfd, (struct sockaddr*)&saddr, sizeof (saddr)); listen(lfd, 8 ); int epfd = epoll_create(100 ); struct epoll_event epev ; epev.events = EPOLLIN; epev.data.fd = lfd; epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &epev); struct epoll_event epevs [1024]; while (1 ) { int ret = epoll_wait(epfd, epevs, 1024 , -1 ); if (ret==-1 ) { perror("epoll_wait" ); exit (-1 ); } printf ("ret = %d\n" , ret); for (int i=0 ; i<ret; i++) { if (epevs[i].data.fd == lfd) { struct sockaddr_in clientAddr ; int len = sizeof (clientAddr); int cfd = accept(lfd, (struct sockaddr*)&clientAddr, &len); int flag = fcntl(cfd, F_GETFL); fcntl(cfd, F_SETFL, flag | O_NONBLOCK); epev.events = EPOLLIN | EPOLLET; epev.data.fd = cfd; epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &epev); }else { int cfd = epevs[i].data.fd; char buf[5 ] = {0 }; int len; while ((len = read(cfd, buf, sizeof (buf)))>0 ) { printf ("receive data: %s\n" , buf); write(cfd, buf, strlen (buf)); } if (len==0 ) { printf ("client closed.\n" ); }else if (len==-1 ) { perror("read" ); exit (-1 ); } } } } close(lfd); close(epfd); 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 #include <stdio.h> #include <arpa/inet.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main () { int fd = socket(PF_INET, SOCK_STREAM, 0 ); if (fd == -1 ) { perror("socket" ); return -1 ; } struct sockaddr_in seraddr ; inet_pton(AF_INET, "127.0.0.1" , &seraddr.sin_addr.s_addr); seraddr.sin_family = AF_INET; seraddr.sin_port = htons(9999 ); int ret = connect(fd, (struct sockaddr *)&seraddr, sizeof (seraddr)); if (ret == -1 ){ perror("connect" ); return -1 ; } while (1 ) { char sendBuf[1024 ] = {0 }; fgets(sendBuf, sizeof (sendBuf), stdin ); write(fd, sendBuf, strlen (sendBuf) + 1 ); int len = read(fd, sendBuf, sizeof (sendBuf)); if (len == -1 ) { perror("read" ); return -1 ; }else if (len > 0 ) { printf ("read buf = %s\n" , sendBuf); } else { printf ("服务器已经断开连接...\n" ); break ; } } close(fd); return 0 ; }
UDP通信实现 1 2 3 4 ssize_t sendto (int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) ;ssize_t recvfrom (int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) ;
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 #include <stdio.h> #include <arpa/inet.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main () { int fd = socket(AF_INET, SOCK_DGRAM, 0 ); if (fd==-1 ) { perror("socket" ); exit (-1 ); } struct sockaddr_in saddr ; saddr.sin_family = AF_INET; saddr.sin_port = htons(9999 ); inet_pton(AF_INET, "127.0.0.1" , &saddr.sin_addr.s_addr); int num = 0 ; while (1 ) { char sendBuf[1024 ] = {0 }; sprintf (sendBuf, "Hello I'm client, %d\n" , num++); sendto(fd, sendBuf, strlen (sendBuf), 0 , (struct sockaddr *)&saddr, sizeof (saddr)); sleep(1 ); } close(fd); 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 #include <stdio.h> #include <arpa/inet.h> #include <stdlib.h> #include <unistd.h> #include <string.h> int main () { int fd = socket(AF_INET, SOCK_DGRAM, 0 ); if (fd==-1 ) { perror("socket" ); exit (-1 ); } struct sockaddr_in addr ; addr.sin_family = AF_INET; addr.sin_port = htons(9999 ); addr.sin_addr.s_addr = INADDR_ANY; bind(fd, (struct sockaddr*)&addr, sizeof (addr)); while (1 ) { char buf[1024 ]; char ipbuf[16 ]; struct sockaddr_in clientAddr ; int len = sizeof (clientAddr); int num = recvfrom(fd, buf, sizeof (buf), 0 , (struct sockaddr *)&clientAddr, &len); if (num==-1 ) { perror("recvform" ); exit (-1 ); } inet_ntop(AF_INET, &clientAddr.sin_addr.s_addr, ipbuf, sizeof (ipbuf)); printf ("client IP:%s, port:%d\n" , ipbuf, ntohs(clientAddr.sin_port) ); printf ("client say: %s\n" , buf); sendto(fd, buf, strlen (buf), 0 , (struct sockaddr*)&clientAddr, sizeof (clientAddr)); } close(fd); return 0 ; }
广播 组播 多播通信必须依赖于IP多播地址,在IPV4 中它的范国从 224.0.0.0到 239.255.255.255,并被划分肩部链接多播地址、预留多播地址和管理杈限多播地址三类
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 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <arpa/inet.h> int main () { int fd = socket(PF_INET, SOCK_DGRAM, 0 ); if (fd == -1 ) { perror("socket" ); exit (-1 ); } struct in_addr in ; struct sockaddr_in addr ; addr.sin_family = AF_INET; addr.sin_port = htons(9999 ); addr.sin_addr.s_addr = INADDR_ANY; int ret = bind(fd, (struct sockaddr *)&addr, sizeof (addr)); if (ret == -1 ) { perror("bind" ); exit (-1 ); } struct ip_mreq op ; inet_pton(AF_INET, "239.0.0.10" , &op.imr_multiaddr.s_addr ); op.imr_interface.s_addr = INADDR_ANY; setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &op, sizeof (op)); while (1 ) { char buf[128 ]; int num = recvfrom(fd, buf, sizeof (buf), 0 , NULL , NULL ); printf ("server say : %s\n" , buf); } close(fd); 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 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <arpa/inet.h> int main () { int fd = socket(PF_INET, SOCK_DGRAM, 0 ); if (fd == -1 ) { perror("socket" ); exit (-1 ); } struct in_addr imr_multiaddr ; inet_pton(AF_INET, "239.0.0.10" , &imr_multiaddr.s_addr); setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, &imr_multiaddr, sizeof (imr_multiaddr)); struct sockaddr_in cliaddr ; cliaddr.sin_family = AF_INET; cliaddr.sin_port = htons(9999 ); inet_pton(AF_INET, "239.0.0.10" , &cliaddr.sin_addr.s_addr); int num = 0 ; while (1 ) { char sendBuf[128 ]; sprintf (sendBuf, "hello, client....%d\n" , num++); sendto(fd, sendBuf, strlen (sendBuf) + 1 , 0 , (struct sockaddr *)&cliaddr, sizeof (cliaddr)); printf ("Multicasting data: %s\n" , sendBuf); sleep(1 ); } close(fd); 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 53 54 55 56 57 58 59 #include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <arpa/inet.h> #include <sys/un.h> int main (int argc, const char * argv[]) { int lfd = socket(AF_LOCAL, SOCK_STREAM, 0 ); if (lfd == -1 ) { perror("socket" ); exit (-1 ); } struct sockaddr_un addr ; addr.sun_family = AF_LOCAL; unlink("server.sock" ); strcpy (addr.sun_path, "server.sock" ); int ret = bind(lfd, (struct sockaddr*)&addr, sizeof (addr)); if (ret==-1 ) { perror("bind" ); exit (-1 ); } ret = listen(lfd, 100 ); if (ret==-1 ) { perror("listen" ); exit (-1 ); } struct sockaddr_un clientAddr ; socklen_t len = sizeof (clientAddr); int cfd = accept(lfd, (struct sockaddr*)&clientAddr, &len); if (cfd==-1 ) { perror("accept" ); exit (-1 ); } printf ("client socket filename: %s\n" , clientAddr.sun_path ); while (1 ) { char buf[1024 ] = {0 }; int len = recv(cfd, buf, sizeof (buf), 0 ); if (len==-1 ) { perror("recv" ); exit (-1 ); }else if (len==0 ) { printf ("client closed..." ); break ; }else { printf ("cliend say: %s\n" , buf); send(cfd, buf, len, 0 ); } } close(cfd); close(lfd); 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 53 54 #include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <arpa/inet.h> #include <sys/un.h> int main (int argc, const char * argv[]) { int cfd = socket(AF_LOCAL, SOCK_STREAM, 0 ); if (cfd == -1 ) { perror("socket" ); exit (-1 ); } struct sockaddr_un clientAddr ; clientAddr.sun_family = AF_LOCAL; unlink("client.sock" ); strcpy (clientAddr.sun_path, "client.sock" ); int ret = bind(cfd, (struct sockaddr*)&clientAddr, sizeof (clientAddr)); if (ret==-1 ) { perror("bind" ); exit (-1 ); } struct sockaddr_un serverAddr ; strcpy (serverAddr.sun_path, "server.sock" ); ret = connect(cfd, (struct sockaddr*)&serverAddr, sizeof (serverAddr)); int num = 0 ; while (1 ) { char buf[1024 ] = {0 }; sprintf (buf, "hello, I'm client num:%d" , num++); send(cfd, buf, strlen (buf), 0 ); int len = recv(cfd, buf, sizeof (buf), 0 ); if (len==-1 ) { perror("recv" ); exit (-1 ); }else if (len==0 ) { printf ("connection closed..." ); break ; }else { printf ("server say: %s\n" , buf); } sleep(1 ); } close(cfd); return 0 ; }