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没有区别。
多线程服务端 2_echo_multhread_tcp_server.c :
#include <sys/socket.h> #include <sys/types.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <time.h> #include <string.h> #include <netdb.h> #include <arpa/inet.h> #include "msg.h" #include <errno.h> #include <pthread.h> #define SERVER_PORT 8888 #define LISTEN_QUEUE_SISE 10 int socketfd; void signal_handler(int signo) { if (signo == SIGINT) { printf("this serveice close\n"); close(socketfd); exit(1); } } void print_clientinfo(int fd, const char* type) { struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); socklen_t len = sizeof(addr); if(getpeername(fd, (struct sockaddr*)&addr, &len) < 0) { perror("getpeername error"); return; } char ipstr[16]; memset(ipstr, 0, sizeof(ipstr)); // 将地址从网络字节序转换为点分十进制 inet_ntop(AF_INET, &addr.sin_addr.s_addr, ipstr, sizeof(ipstr)); printf("%s(%d) %s \n", ipstr, ntohs(addr.sin_port), type); } void do_something(int fd) { char buff[1024]; while(1) { //读取客户端发送过来的内容 memset(buff, 0, sizeof(buff)); size_t size = read_msg(fd, buff, sizeof(buff)); if (size < 0) { // 协议出错 跳出while 回到main 结束子进程 perror("read_msg error"); break; } else if (size == 0) { /* 客户端断开了连接 挂掉了 跳出while 会结束子线程 **/ break; } printf("recev from client:%s\n",buff); //将数据写回 if (write_msg(fd, buff, sizeof(buff)) < 0) { if(errno == EPIPE) { /* 客户端断开了连接 * 产生SIGPIPE信号 * 将error设置为EPIPE * 所以可在此判断errno 也可捕捉SIGPIPE信号 * 俗称管道爆裂 * 跳出while 会结束子线程 **/ break; } perror("write_msg error"); } } } void* threadWork(void* arg) { int fd = (int)arg; print_clientinfo(fd, "connected"); do_something(fd); print_clientinfo(fd, "closed"); close(fd); return NULL; } int main(int argc, char const *argv[]) { if (signal(SIGINT, signal_handler) == SIG_ERR) { perror("signal error"); exit(1); } // 1 sokect // AF_INET ipv4 // SOCK_STREAM tcp if((socketfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { perror("socket error"); exit(1); } // 2 bind 绑定本地地址和端口 struct sockaddr_in serveraddr; memset(&serveraddr, 0, sizeof(serveraddr)); serveraddr.sin_family = AF_INET;//ipv4 serveraddr.sin_port = htons(SERVER_PORT); //端口 serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);//响应任意网卡的请求 if(bind(socketfd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) < 0) { perror("bind error"); exit(1); } // 3 listen 启动监听 通知系统接受来自客户端的连接 准备好连接队列 if(listen(socketfd, LISTEN_QUEUE_SISE) < 0) { perror("listen error"); exit(1); } pthread_t thread; //设置分离属性相关 不需要主线程调用pthread_join回收子线程 pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); int retno; while(1) { // 4 accept 从队列拿出第一个 int clientfd = accept(socketfd, NULL, NULL); if (clientfd < 0) { perror("accept error"); continue; } // 5 创建分离属性的子线程 在子线程中做read/write if( (retno = pthread_create(&thread, &attr, threadWork, (void*)clientfd)) != 0 ) { printf("error nuber:%d, %s\n", retno, strerror(retno)); close(clientfd); continue; } } // 销毁线程属性 pthread_attr_destroy(&attr); // 6 close close(socketfd); return 0; }