https://www.cnblogs.com/lan0725/p/11634267.html 只是简单的处理,服务器返回客户端一个时间,然后关闭了socket。
如果要进行双向通信,服务器势必要调用read函数,而read默认阻塞,那么如果客户端不向服务器发送数据,则主线程一直阻塞,其它客户端无法连接成功。这就需要处理高并发问题。
服务器高并发处理的三种方式
- 多进程 https://www.cnblogs.com/lan0725/p/11634709.html
- 多线程 https://www.cnblogs.com/lan0725/p/11639142.html
- I/O多路复用
目录 makefile的设置都在多进程里面有介绍 https://www.cnblogs.com/lan0725/p/11634709.html
多线程的效率明显高于多进程,而且因为线程共享大部分进程空间,而多进程复制空间,从而降低了系统开销。
- 主线程只是负责accepte,然后创建带分离属性的子线程。
- 每个客户端连接维护一个带分离属性的线程,在此线程里做双向通信。即有多少客户端连接,就会开辟多少子线程。
客户端和多进程的用同一个。
实际上只在main函数accept的时候有区别,创建了分离线程,免去了主线程join等待回收线程。具体的do_something没有区别。
多线程服务端 echo_tcp_server_thread.c :
1 #include <sys/socket.h> 2 #include <sys/types.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <unistd.h> 6 #include <signal.h> 7 #include <time.h> 8 #include <string.h> 9 #include <netdb.h> 10 #include <arpa/inet.h> 11 #include "msg.h" 12 #include <errno.h> 13 #include <pthread.h> 14 15 #define SERVER_PORT 8888 16 #define LISTEN_QUEUE_SISE 10 17 18 int socketfd; 19 20 void signal_handler(int signo) 21 { 22 if (signo == SIGINT) 23 { 24 printf("this serveice close "); 25 close(socketfd); 26 exit(1); 27 } 28 } 29 30 void print_clientinfo(int fd, const char* type) 31 { 32 struct sockaddr_in addr; 33 memset(&addr, 0, sizeof(addr)); 34 socklen_t len = sizeof(addr); 35 36 if(getpeername(fd, (struct sockaddr*)&addr, &len) < 0) 37 { 38 perror("getpeername error"); 39 return; 40 } 41 42 char ipstr[16]; 43 memset(ipstr, 0, sizeof(ipstr)); 44 // 将地址从网络字节序转换为点分十进制 45 inet_ntop(AF_INET, &addr.sin_addr.s_addr, ipstr, sizeof(ipstr)); 46 47 printf("%s(%d) %s ", ipstr, ntohs(addr.sin_port), type); 48 } 49 50 void do_something(int fd) 51 { 52 char buff[1024]; 53 while(1) 54 { 55 //读取客户端发送过来的内容 56 memset(buff, 0, sizeof(buff)); 57 size_t size = read_msg(fd, buff, sizeof(buff)); 58 if (size < 0) 59 { 60 // 协议出错 跳出while 回到main 结束子进程 61 perror("read_msg error"); 62 break; 63 } 64 else if (size == 0) 65 { 66 /* 客户端断开了连接 挂掉了 跳出while 会结束子线程 67 **/ 68 break; 69 } 70 71 printf("recev from client:%s ",buff); 72 73 //将数据写回 74 if (write_msg(fd, buff, sizeof(buff)) < 0) 75 { 76 if(errno == EPIPE) 77 { 78 /* 客户端断开了连接 79 * 产生SIGPIPE信号 80 * 将error设置为EPIPE 81 * 所以可在此判断errno 也可捕捉SIGPIPE信号 82 * 俗称管道爆裂 83 * 跳出while 会结束子线程 84 **/ 85 break; 86 } 87 88 89 perror("write_msg error"); 90 } 91 } 92 } 93 94 void* threadWork(void* arg) 95 { 96 int fd = (int)arg; 97 print_clientinfo(fd, "connected"); 98 do_something(fd); 99 print_clientinfo(fd, "closed"); 100 close(fd); 101 return NULL; 102 } 103 104 105 int main(int argc, char const *argv[]) 106 { 107 if (signal(SIGINT, signal_handler) == SIG_ERR) 108 { 109 perror("signal error"); 110 exit(1); 111 } 112 113 // 1 sokect 114 // AF_INET ipv4 115 // SOCK_STREAM tcp 116 if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 117 { 118 perror("socket error"); 119 exit(1); 120 } 121 122 // 2 bind 绑定本地地址和端口 123 struct sockaddr_in serveraddr; 124 memset(&serveraddr, 0, sizeof(serveraddr)); 125 serveraddr.sin_family = AF_INET;//ipv4 126 serveraddr.sin_port = htons(SERVER_PORT); //端口 127 serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);//响应任意网卡的请求 128 if(bind(socketfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) 129 { 130 perror("bind error"); 131 exit(1); 132 } 133 134 // 3 listen 启动监听 通知系统接受来自客户端的连接 准备好连接队列 135 if(listen(socketfd, LISTEN_QUEUE_SISE) < 0) 136 { 137 perror("listen error"); 138 exit(1); 139 } 140 141 pthread_t thread; 142 //设置分离属性相关 不需要主线程调用pthread_join回收子线程 143 pthread_attr_t attr; 144 pthread_attr_init(&attr); 145 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 146 int retno; 147 while(1) 148 { 149 // 4 accept 从队列拿出第一个 150 int clientfd = accept(socketfd, NULL, NULL); 151 if (clientfd < 0) 152 { 153 perror("accept error"); 154 continue; 155 } 156 // 5 创建分离属性的子线程 在子线程中做read/write 157 if( (retno = pthread_create(&thread, &attr, 158 threadWork, (void*)clientfd)) != 0 ) 159 { 160 printf("error nuber:%d, %s ", retno, strerror(retno)); 161 close(clientfd); 162 continue; 163 } 164 165 } 166 167 // 销毁线程属性 168 pthread_attr_destroy(&attr); 169 // 6 close 170 close(socketfd); 171 return 0; 172 }