在学习Linux高并发网络编程开发总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
10-Linux系统编程-第11天(tcp三次握手-并发)
目录:
一、学习目标
二、复习
三、TCP三次握手-并发
1、TCP服务器端和客户端代码实现
2、socket 函数封装
3、TCP 3次握手
4、TCP 数据传输过程
5、TCP 四次挥手
6、滑动窗口
7、多进程并发服务器分析
8、多进程并发服务器伪代码
9、多进程并发服务器代码实现
10、多线程并发服务器实现思路
11、多线程版服务器端代码实现
一、学习目标
1、熟练掌握三次握手建立连接过程
2、熟练掌握四次挥手断开连接过程
3、掌握滑动窗口概念
4、掌握错误处理函数封装
5、实现多进程并发服务器
6、实现多线程并发服务
二、复习
三、TCP三次握手-并发
》server模板.c
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <sys/types.h> 5 #include <string.h> 6 #include <sys/socket.h> 7 #include <arpa/inet.h> 8 #include <ctype.h> 9 10 //进程回调函数 11 12 //主函数 13 int main(int argc, const char *argv[]) 14 { 15 if(argc < 2) 16 { 17 printf("eg: ./a.out port "); 18 exit(1); 19 } 20 struct sockaddr_in serv_addr; 21 socklen_t serv_len = sizeof(serv_addr); 22 int port = atoi(argv[1]); 23 24 //创建套接字 25 int lfd = socket(AF_INET, SOCK_STREAM, 0); 26 //初始化服务器 sockaddr_in 27 memset(&serv_addr, 0, serv_len); 28 serv_addr.sin_family = AF_INET;//地址族 29 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//监听本机所有IP 30 serv_addr.sin_port = htons(port);//设置端口 31 //绑定IP 和端口 32 bind(lfd, (struct sockaddr*)&serv_addr, serv_len); 33 34 //设置同时监听的最大个数 35 listen(lfd, 36); 36 printf("Start accept ...... "); 37 38 struct sockaddr_in client_addr; 39 socklen_t cli_len = sizeof(client_addr); 40 while(1) 41 { 42 43 } 44 45 close(lfd); 46 return 0; 47 }
1、TCP服务器端和客户端代码实现
》TCP服务器端
>touch tcp_server.c
>vi tcp_server.c
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <sys/types.h> 5 #include <sys/stat.h> 6 #include <string.h> 7 #include <sys/socket.h> 8 #include <arpa/inet.h> 9 #include <ctype.h> 10 11 12 int main(int argc, const char* argv[]) 13 { 14 // 创建用于监听的套节字 15 int lfd = socket(AF_INET, SOCK_STREAM, 0); 16 if(lfd == -1) 17 { 18 perror("socket error"); 19 exit(1); 20 } 21 22 // 绑定 23 struct sockaddr_in serv_addr; 24 // init 25 memset(&serv_addr, 0, sizeof(serv_addr)); 26 // bzero(&serv_addr, sizeof(serv_addr)); 27 serv_addr.sin_family = AF_INET; // 地址族协议 ipv4 28 serv_addr.sin_port = htons(9999); // 本地端口, 需要转换为大端 29 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 0 是用本机的任意IP 30 31 int ret = bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); 32 if(ret == -1) 33 { 34 perror("bind error"); 35 exit(1); 36 } 37 38 // 设置监听 39 ret = listen(lfd, 64); 40 if(ret == -1) 41 { 42 perror("listen error"); 43 exit(1); 44 } 45 46 // 等待并接受连接请求 47 struct sockaddr_in cline_addr; 48 socklen_t clien_len = sizeof(cline_addr); 49 int cfd = accept(lfd, (struct sockaddr*)&cline_addr, &clien_len); 50 if(cfd == -1) 51 { 52 perror("accept error"); 53 exit(1); 54 } 55 56 char ipbuf[64]; 57 // int -> char* 58 printf("cliient ip: %s, port: %d ", 59 inet_ntop(AF_INET, &cline_addr.sin_addr.s_addr, ipbuf, sizeof(ipbuf)), 60 ntohs(cline_addr.sin_port)); 61 62 // 通信 63 while(1) 64 { 65 // 先接收数据 66 char buf[1024] = {0}; 67 int len = read(cfd, buf, sizeof(buf)); 68 if(len == -1) 69 { 70 perror("read error"); 71 break; 72 } 73 else if(len > 0) 74 { 75 // 顺利读出了数据 76 printf("read buf = %s ", buf); 77 // 小写 -》 大写 78 for(int i=0; i<len; ++i) 79 { 80 buf[i] = toupper(buf[i]); 81 } 82 printf(" -- toupper: %s ", buf); 83 84 // 数据发送给客户端 85 write(cfd, buf, strlen(buf)+1); 86 } 87 else if( len == 0 ) 88 { 89 printf("client disconnect ... "); 90 break; 91 } 92 } 93 94 close(lfd); 95 close(cfd); 96 97 return 0; 98 }
>gcc tcp_server.c -o server
》TCP客户端
>touch tcp_client.c
>vi tcp_client.c
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <sys/types.h> 5 #include <sys/stat.h> 6 #include <string.h> 7 #include <arpa/inet.h> 8 #include <fcntl.h> 9 10 // tcp client 11 int main(int argc, const char* argv[]) 12 { 13 if(argc < 2) 14 { 15 printf("eg: ./a.out port "); 16 exit(1); 17 } 18 19 int port = atoi(argv[1]); 20 // 创建套接字 ( AF_INET为IPv4) 21 int fd = socket(AF_INET, SOCK_STREAM, 0);//查文档 :! man 'socket' 22 if(fd == -1) 23 { 24 perror("socket error"); 25 exit(1); 26 } 27 28 29 // 连接服务器 30 struct sockaddr_in serv_addr; 31 memset(&serv_addr, 0, sizeof(serv_addr)); 32 serv_addr.sin_family = AF_INET; 33 serv_addr.sin_port = htons(port); 34 //serv_addr.sin_addr.s_addr = htonl();//htonl括号中只能放整型,所以换用inet_pton 35 inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr.s_addr); 36 int ret = connect(fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); 37 if(ret == -1) 38 { 39 perror("connect error"); 40 exit(1); 41 } 42 43 // 通信 44 while(1) 45 { 46 // 发送数据 47 // 接收键盘输入 48 char buf[1024]; 49 printf("请输入要发送的字符串: "); 50 fgets(buf, sizeof(buf), stdin); 51 // 发送给服务器 52 write(fd, buf, strlen(buf)+1); 53 54 // 等待接收服务器端的数据 55 int len = read(fd, buf, sizeof(buf)); 56 if(len == -1) 57 { 58 perror("read error"); 59 exit(1); 60 } 61 else if(len == 0) 62 { 63 printf("服务器端关闭了连接 "); 64 break; 65 } 66 else 67 { 68 printf("read buf = %s, len = %d ", buf, len); 69 } 70 } 71 close(fd); 72 73 return 0; 74 }
>gcc tcp_client.c -o client
>./server
(打开另一个终端,切换到目录下,运行./client 9999,然后输入要发送的字符串:hello,会收到服务器转换为大写的HELLO;查看原终端server,可以看到client IP:127.0.0.1, port: 34844, 收到字符串:hello,发送HELLO)
2、socket 函数封装
函数的封装在wrap.c和wrap.h,函数的调用在client.c和server.c中
理解
wrap.h
1 #ifndef __WRAP_H_ 2 #define __WRAP_H_ 3 4 void perr_exit(const char *s); 5 int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr); 6 int Bind(int fd, const struct sockaddr *sa, socklen_t salen); 7 int Connect(int fd, const struct sockaddr *sa, socklen_t salen); 8 int Listen(int fd, int backlog); 9 int Socket(int family, int type, int protocol); 10 ssize_t Read(int fd, void *ptr, size_t nbytes); 11 ssize_t Write(int fd, const void *ptr, size_t nbytes); 12 int Close(int fd); 13 ssize_t Readn(int fd, void *vptr, size_t n); 14 ssize_t Writen(int fd, const void *vptr, size_t n); 15 ssize_t my_read(int fd, char *ptr); 16 ssize_t Readline(int fd, void *vptr, size_t maxlen); 17 18 #endif
wrap.c
1 #include <stdlib.h> 2 #include <stdio.h> 3 #include <unistd.h> 4 #include <errno.h> 5 #include <sys/socket.h> 6 //错误输出 7 void perr_exit(const char *s) 8 { 9 perror(s); 10 exit(-1); 11 } 12 //接受 13 int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr) 14 { 15 int n; 16 17 again: 18 if ((n = accept(fd, sa, salenptr)) < 0) 19 { 20 //ECONNABORTED 发生在重传(一定次数)失败后,强制关闭套接字 21 //EINTR 进程被信号中断 22 if ((errno == ECONNABORTED) || (errno == EINTR)) 23 { 24 goto again; 25 } 26 else 27 { 28 perr_exit("accept error"); 29 } 30 } 31 return n; 32 } 33 //绑定 34 int Bind(int fd, const struct sockaddr *sa, socklen_t salen) 35 { 36 int n; 37 38 if ((n = bind(fd, sa, salen)) < 0) 39 { 40 perr_exit("bind error"); 41 } 42 43 return n; 44 } 45 //连接 46 int Connect(int fd, const struct sockaddr *sa, socklen_t salen) 47 { 48 int n; 49 n = connect(fd, sa, salen); 50 if (n < 0) 51 { 52 perr_exit("connect error"); 53 } 54 55 return n; 56 } 57 58 int Listen(int fd, int backlog) 59 { 60 int n; 61 62 if ((n = listen(fd, backlog)) < 0) 63 { 64 perr_exit("listen error"); 65 } 66 67 return n; 68 } 69 70 int Socket(int family, int type, int protocol) 71 { 72 int n; 73 74 if ((n = socket(family, type, protocol)) < 0) 75 { 76 perr_exit("socket error"); 77 } 78 79 return n; 80 } 81 82 ssize_t Read(int fd, void *ptr, size_t nbytes) 83 { 84 ssize_t n; 85 86 again: 87 if ( (n = read(fd, ptr, nbytes)) == -1) //判断是否阻塞 88 { 89 if (errno == EINTR)//判断是否被信号中断 90 goto again; 91 else 92 return -1; 93 } 94 95 return n; 96 } 97 98 ssize_t Write(int fd, const void *ptr, size_t nbytes) 99 { 100 ssize_t n; 101 102 again: 103 if ((n = write(fd, ptr, nbytes)) == -1) //有可能写缓冲区满了,阻塞,等待 104 { 105 if (errno == EINTR)//判断是否被信号中断 106 goto again; 107 else 108 return -1; 109 } 110 return n; 111 } 112 113 int Close(int fd) 114 { 115 int n; 116 if ((n = close(fd)) == -1) 117 perr_exit("close error"); 118 119 return n; 120 } 121 122 /*参三: 应该读取的字节数*/ 123 //socket 4096 readn(cfd, buf, 4096) nleft = 4096-1500 124 ssize_t Readn(int fd, void *vptr, size_t n) 125 { 126 size_t nleft; //usigned int 剩余未读取的字节数 127 ssize_t nread; //int 实际读到的字节数 128 char *ptr; 129 130 ptr = vptr; 131 nleft = n; //n 未读取字节数 132 133 while (nleft > 0) 134 { 135 if ((nread = read(fd, ptr, nleft)) < 0) 136 { 137 if (errno == EINTR) 138 { 139 nread = 0; 140 } 141 else 142 { 143 return -1; 144 } 145 } 146 else if (nread == 0) 147 { 148 break; 149 } 150 151 nleft -= nread; //nleft = nleft - nread 152 ptr += nread; 153 } 154 return n - nleft; 155 } 156 157 ssize_t Writen(int fd, const void *vptr, size_t n) 158 { 159 size_t nleft; 160 ssize_t nwritten; 161 const char *ptr; 162 163 ptr = vptr; 164 nleft = n; 165 while (nleft > 0) 166 { 167 if ( (nwritten = write(fd, ptr, nleft)) <= 0) 168 { 169 if (nwritten < 0 && errno == EINTR) 170 nwritten = 0; 171 else 172 return -1; 173 } 174 nleft -= nwritten; 175 ptr += nwritten; 176 } 177 return n; 178 } 179 180 static ssize_t my_read(int fd, char *ptr)//静态函数 181 { 182 static int read_cnt;//静态变量 183 static char *read_ptr; 184 static char read_buf[100]; 185 186 if (read_cnt <= 0) { 187 again: 188 if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) //"hello " 189 { 190 if (errno == EINTR) 191 goto again; 192 return -1; 193 } 194 else if (read_cnt == 0) 195 return 0; 196 197 read_ptr = read_buf; 198 } 199 read_cnt--; 200 *ptr = *read_ptr++; 201 202 return 1; 203 } 204 205 /*readline --- fgets*/ 206 //传出参数 vptr 207 ssize_t Readline(int fd, void *vptr, size_t maxlen) 208 { 209 ssize_t n, rc; 210 char c, *ptr; 211 ptr = vptr; 212 213 for (n = 1; n < maxlen; n++) 214 { 215 if ((rc = my_read(fd, &c)) == 1) //ptr[] = hello 216 { 217 *ptr++ = c; 218 if (c == ' ') 219 break; 220 } 221 else if (rc == 0) 222 { 223 *ptr = 0; 224 return n-1; 225 } 226 else 227 return -1; 228 } 229 *ptr = 0; 230 231 return n; 232 }
server.c
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/types.h> 4 #include <sys/socket.h> 5 #include <strings.h> 6 #include <string.h> 7 #include <ctype.h> 8 #include <arpa/inet.h> 9 10 #include "wrap.h" 11 12 #define SERV_PORT 6666 13 14 int main(void) 15 { 16 int sfd, cfd; 17 int len, i; 18 char buf[BUFSIZ], clie_IP[128]; 19 20 struct sockaddr_in serv_addr, clie_addr; 21 socklen_t clie_addr_len; 22 23 sfd = Socket(AF_INET, SOCK_STREAM, 0); 24 25 bzero(&serv_addr, sizeof(serv_addr)); 26 serv_addr.sin_family = AF_INET; 27 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); 28 serv_addr.sin_port = htons(SERV_PORT); 29 30 Bind(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); 31 32 Listen(sfd, 2); 33 34 printf("wait for client connect ... "); 35 36 clie_addr_len = sizeof(clie_addr_len); 37 cfd = Accept(sfd, (struct sockaddr *)&clie_addr, &clie_addr_len); 38 printf("cfd = ----%d ", cfd); 39 40 printf("client IP: %s port:%d ", 41 inet_ntop(AF_INET, &clie_addr.sin_addr.s_addr, clie_IP, sizeof(clie_IP)), 42 ntohs(clie_addr.sin_port)); 43 44 while (1) 45 { 46 len = Read(cfd, buf, sizeof(buf)); 47 Write(STDOUT_FILENO, buf, len); 48 49 for (i = 0; i < len; i++) 50 buf[i] = toupper(buf[i]); 51 Write(cfd, buf, len); 52 } 53 54 Close(sfd); 55 Close(cfd); 56 57 return 0; 58 }
client.c
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <string.h> 4 #include <sys/socket.h> 5 #include <arpa/inet.h> 6 7 #include "wrap.h" 8 9 #define SERV_IP "127.0.0.1" 10 #define SERV_PORT 6666 11 12 int main(void) 13 { 14 int sfd, len; 15 struct sockaddr_in serv_addr; 16 char buf[BUFSIZ]; 17 18 sfd = Socket(AF_INET, SOCK_STREAM, 0); 19 20 bzero(&serv_addr, sizeof(serv_addr)); 21 serv_addr.sin_family = AF_INET; 22 inet_pton(AF_INET, SERV_IP, &serv_addr.sin_addr.s_addr); 23 serv_addr.sin_port = htons(SERV_PORT); 24 25 Connect(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)); 26 27 while (1) { 28 fgets(buf, sizeof(buf), stdin); 29 int r = Write(sfd, buf, strlen(buf)); 30 printf("Write r ======== %d ", r); 31 len = Read(sfd, buf, sizeof(buf)); 32 printf("Read len ========= %d ", len); 33 Write(STDOUT_FILENO, buf, len); 34 } 35 36 Close(sfd); 37 38 return 0; 39 }
makefile
1 src = $(wildcard *.c) 2 obj = $(patsubst %.c, %.o, $(src)) 3 4 all: server client 5 6 server: server.o wrap.o 7 gcc server.o wrap.o -o server -Wall 8 client: client.o wrap.o 9 gcc client.o wrap.o -o client -Wall 10 11 %.o:%.c 12 gcc -c $< -Wall 13 14 .PHONY: clean all 15 clean: 16 -rm -rf server client $(obj)
3、TCP 3次握手
4、TCP 数据传输过程
5、TCP 四次挥手
6、滑动窗口
7、多进程并发服务器分析
>之前讲的客户端-服务器的缺点分析?
》读时共享,写时复制,如何理解?
8、多进程并发服务器伪代码
》伪代码:
1 void recyle(int num) 2 { 3 while(waitpid(-1, NULL, wnohang) > 0); 4 } 5 6 int main() 7 { 8 //监听 9 int lfd = sock(); 10 //绑定 11 bind(); 12 //设置监听 13 listen(); 14 15 //信号回收子进程 16 struct sigaction act; 17 act.sa_handler = recyle; 18 act.sa_flages = 0; 19 sigemptyset(&act.sa_mask); 20 sigaction(SIGCHLD, &act, NULL); 21 22 //父进程 23 while(1) 24 { 25 int cfd = accept(lfd, &client); 26 //创建子进程 27 pid_t pid = fork(); 28 //子进程 29 if(pid == 0) 30 { 31 close(lfd); 32 //通信 33 while(1) 34 { 35 int len = read(); 36 if(len == -1) 37 { 38 exit(1); 39 } 40 else if(len == 0) 41 { 42 close(cfd); 43 break; 44 } 45 else 46 { 47 write(); 48 } 49 } 50 //退出子进程 51 return 0;//exit(1); 52 } 53 else 54 { 55 //父进程 56 close(cfd);//如果在此处while回收,就会阻塞在这,所以用信号检测 57 } 58 } 59 }
9、多进程并发服务器代码实现
>touch process_server.c
>vi process_server.c
1 #include <stdio.h> 2 3 #include <arpa/inet.h> 4 #include <ctype.h> 5 #include <unistd.h> 6 #include <stdlib.h> 7 #include <sys/types.h> 8 #include <sys/socket.h> 9 #include <signal.h> 10 #include <sys/wait.h> 11 #include <string.h> 12 #include <errno.h> 13 14 15 //进程回调函数 16 void recyle(int num) 17 { 18 pid_t pid; 19 while((pid = waitpid(-1, NULL, WNOHANG)) > 0) 20 { 21 printf("child died, pid=%d ", pid); 22 } 23 } 24 25 int main(int argc, const char *argv[]) 26 { 27 if(argc < 2) 28 { 29 printf("eg: ./a.out port "); 30 exit(1); 31 } 32 struct sockaddr_in serv_addr; 33 socklen_t serv_len = sizeof(serv_addr); 34 int port = atoi(argv[1]); 35 36 //创建套接字 37 int lfd = socket(AF_INET, SOCK_STREAM, 0); 38 //初始化服务器 sockaddr_in 39 memset(&serv_addr, 0, serv_len); 40 serv_addr.sin_family = AF_INET;//地址族 41 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//监听本机所有IP 42 serv_addr.sin_port = htons(port);//设置端口 43 //绑定IP 和端口 44 bind(lfd, (struct sockaddr*)&serv_addr, serv_len); 45 46 //设置同时监听的最大个数 47 listen(lfd, 36); 48 printf("Start accept ...... "); 49 50 //使用信号回收子进程pcb 51 struct sigaction act; 52 act.sa_handler = recyle; 53 act.sa_flags = 0; 54 sigemptyset(&act.sa_mask); 55 sigaction(SIGCHLD, &act, NULL); 56 57 struct sockaddr_in client_addr; 58 socklen_t cli_len = sizeof(client_addr); 59 while(1) 60 { 61 //父进程接收连接请求 62 //accept阻塞的时候被信号中断,处理信号对应的操作之后 63 //回来之后不阻塞了,直接返回-1,这时候errno = EINTR 64 int cfd = accept(lfd, (struct sockaddr*)&client_addr, &cli_len); 65 while((cfd == -1) && (errno == EINTR)) 66 { 67 cfd = accept(lfd, (struct sockaddr*)&client_addr, &cli_len); 68 } 69 /*if(cfd == -1) 70 { 71 perror("accept error"); 72 exit(1); 73 } 74 */ 75 printf("connect successful "); 76 //创建子进程 77 pid_t pid = fork(); 78 if(pid == 0) 79 { 80 close(lfd); 81 //child process 82 //通信 83 char ip[64]; 84 while(1) 85 { 86 //client ip port 87 printf("client IP: %s, port: %d ", inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip, sizeof(ip)), ntohs(client_addr.sin_port)); 88 89 char buf[1024]; 90 int len = read(cfd, buf, sizeof(buf)); 91 if(len == -1) 92 { 93 perror("read error"); 94 exit(1); 95 } 96 else if(len == 0) 97 { 98 printf("客户端断开了连接 "); 99 close(cfd); 100 break; 101 } 102 else 103 { 104 printf("recv buf: %s ", buf); 105 write(cfd, buf, len); 106 } 107 } 108 //干掉子进程 109 return 0; 110 } 111 else if(pid > 0) 112 { 113 //parent process 114 close(cfd); 115 116 } 117 118 119 120 } 121 122 close(lfd); 123 return 0; 124 }
>gcc process_server.c -o server
>./server 9876
客户端仍用之前的tcp_client.c,gcc tcp_client.c -o client 编译后为client
(打开另外两个终端,执行./client 9876,然后分别输入数据,看原server终端的接收情况)
10、多线程并发服务器实现思路
》伪代码:
1 typedef struct sockInfo 2 { 3 pthread_t id; 4 int fd; 5 struct sockaddr_in addr; 6 }SockInfo; 7 //回调函数 8 void *worker(void *arg) 9 { 10 while(1) 11 { 12 //打印客户端ip和port 13 read(); 14 write(); 15 } 16 } 17 18 int main() 19 { 20 //监听 21 int lfd = sock(); 22 //绑定 23 bind(); 24 //设置监听 25 listen(); 26 27 SockInfo sock[256]; 28 //父线程 29 while(1) 30 { 31 int sock[i].fd = accept(lfd, &sock[i].addr, &len); 32 //创建子线程 33 pthread_create(&sock[i].id, NULL, worker, &sock[i]); 34 pthread_deatch(sock[i].id); 35 36 } 37 }
11、多线程版服务器端代码实现
>touch pthread_server.c
>vi pthread_server.c
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <stdlib.h> 4 #include <sys/types.h> 5 #include <string.h> 6 #include <sys/socket.h> 7 #include <arpa/inet.h> 8 #include <ctype.h> 9 #include <pthread.h> 10 11 //自定义数据结构 12 typedef struct SockInfo 13 { 14 int fd;//文件描述符 15 struct sockaddr_in addr;//存放ip地址的结构体 16 pthread_t id;//线程id 17 }SockInfo; 18 19 //子线程处理函数 20 void *worker(void *arg) 21 { 22 char ip[64]; 23 char buf[1024]; 24 SockInfo *info = (SockInfo *)arg; 25 //通信 26 while(1) 27 { 28 printf("Client IP: %s, port: %d ", inet_ntop(AF_INET, &info->addr.sin_addr.s_addr, ip, sizeof(ip)),ntohs(info->addr.sin_port)); 29 int len = read(info->fd, buf, sizeof(buf)); 30 if(len == -1) 31 { 32 perror("read error"); 33 pthread_exit(NULL); 34 } 35 else if(len == 0) 36 { 37 printf("客户端已经断开了连接 "); 38 close(info->fd); 39 break; 40 } 41 else 42 { 43 printf("recv buf: %s ", buf); 44 write(info->fd, buf, len); 45 } 46 } 47 return NULL; 48 } 49 50 //主函数 51 int main(int argc, const char *argv[]) 52 { 53 if(argc < 2) 54 { 55 printf("eg: ./a.out port "); 56 exit(1); 57 } 58 struct sockaddr_in serv_addr; 59 socklen_t serv_len = sizeof(serv_addr); 60 int port = atoi(argv[1]); 61 62 //创建套接字 63 int lfd = socket(AF_INET, SOCK_STREAM, 0); 64 //初始化服务器 sockaddr_in 65 memset(&serv_addr, 0, serv_len); 66 serv_addr.sin_family = AF_INET;//地址族 67 serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);//监听本机所有IP 68 serv_addr.sin_port = htons(port);//设置端口 69 //绑定IP 和端口 70 bind(lfd, (struct sockaddr*)&serv_addr, serv_len); 71 72 //设置同时监听的最大个数 73 listen(lfd, 36); 74 printf("Start accept ...... "); 75 76 int i = 0; 77 SockInfo info[256]; 78 //规定 fd == -1 79 for(i = 0; i < sizeof(info) / sizeof(info[0]); ++i) 80 { 81 info[i].fd = -1; 82 } 83 84 socklen_t cli_len = sizeof(struct sockaddr_in); 85 while(1) 86 { 87 //选一个没有被使用的,最小的数组元素 88 for(i = 0; i < 256; ++i) 89 { 90 if(info[i].fd == -1) 91 { 92 break; 93 } 94 } 95 if(i == 256) 96 { 97 break; 98 } 99 //主线程 - 等待接收连接请求 100 info[i].fd = accept(lfd, (struct sockaddr*)&info[i].addr, &cli_len); 101 102 //创建子线程 - 通信 103 pthrad_create(&info[i].i, NULL, worker, &info[i]); 104 //设置线程分离 105 pthread_detach(info[i].id); 106 107 } 108 109 close(lfd); 110 111 //只退出主线程 112 pthread_exit(NULL); 113 return 0; 114 }
>gcc pthread_server.c -lpthread -o server
>./server 9876
(打开另外两个终端,执行./client 9876,然后分别输入数据,看原server终端的接收情况)
在学习Linux高并发网络编程开发总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。