要实现TCP通信服务器处理并发的任务,使用多线程或者多进程来解决。
思路:
1. 一个父进程,多个子进程
2.父进程负责等待并接受客户端的连接
3.子进程:完成通信,接受一个客户端连接,就创建一个子进程用于通信。
客户端
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- int main() {
- //创建套接字
- int fd = socket(AF_INET, SOCK_STREAM, 0);
- if(fd == -1) {
- perror("socket");
- exit(-1);
- }
- //连接服务器
- struct sockaddr_in serverAddr;
- inet_pton(AF_INET, "192.168.79.130", &serverAddr.sin_addr.s_addr);
- serverAddr.sin_family = AF_INET;
- serverAddr.sin_port = htons(9999);
- int ret = connect(fd,(struct sockaddr*) &serverAddr, sizeof(serverAddr));
- if(ret == -1) {
- perror("connect");
- exit(-1);
- }
- //通信
- char recvBuf[1024];
- int i = 0;
- while(1) {
- sprintf(recvBuf, "%d\n", i++);
- //给服务端发送数据
- write(fd, recvBuf, strlen(recvBuf) + 1);
- sleep(1);
- //接收数据
- int len = read(fd, recvBuf, sizeof(recvBuf) + 1);
- if(len == -1) {
- perror("read");
- exit(-1);
- } else if(len > 0) {
- printf("recv server data : %s\n", recvBuf);
- } else if(len == 0) {
- //表示服务端断开连接
- printf("server closed...\n");
- break;
- }
- }
- close(fd);
- return 0;
- }
服务端
1.创建子进程
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- void recycleChild(int arg) {
- while(1) {
- int ret = waitpid(-1, NULL, WNOHANG);
- if(ret == -1) {
- // 所有的子进程都回收了
- break;
- }else if(ret == 0) {
- // 还有子进程活着
- break;
- } else if(ret > 0){
- // 被回收了
- printf("子进程 %d 被回收了\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(PF_INET, SOCK_STREAM, 0);
- if(lfd == -1) {
- perror("socket");
- exit(-1);
- }
- struct sockaddr_in saddr;
- saddr.sin_addr.s_addr = INADDR_ANY;
- saddr.sin_family = AF_INET;
- saddr.sin_port = htons(9999);
- //绑定
- int ret = bind(lfd, (struct sockaddr*)&saddr, sizeof(saddr));
- if(ret == -1) {
- perror("bind");
- exit(-1);
- }
- //监听
- ret = listen(lfd, 8);
- if(ret == -1) {
- perror("listen");
- exit(-1);
- }
- //不断循环等待客户连接
- while(1) {
- struct sockaddr_in cliaddr;
- int len = sizeof(cliaddr);
- int cfd = accept(lfd, (struct sockaddr*) &cliaddr, &len);
- if(cfd == -1) {
- if(errno == EINTR) {
- continue;
- }
- perror("accept");
- exit(-1);
- }
- //每一个连接进来,创建一个子进程跟客户端通信
- pid_t pid = fork();
- if(pid == 0) {
- //子进程
- //获取客户端的信息
- char clientIP[16];
- inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, clientIP, sizeof(clientIP));
- unsigned short cliPort = ntohs(cliaddr.sin_port);
- printf("client ip is : %s, port is %d\n", clientIP, cliPort);
- //接收客户端发来的信息
- char recvBuf[1024] = {0};
- while(1) {
- int len = read(cfd, &recvBuf, sizeof(recvBuf) + 1);
- if(len == -1) {
- perror("read");
- exit(-1);
- } else if(len > 0) {
- printf("recv client data is : %s\n", recvBuf);
- } else {
- printf("client closed...\n");
- break;
- }
- write(cfd, recvBuf, sizeof(recvBuf) + 1);
- }
- close(cfd);
- exit(0);
- }
- }
- close(lfd);
- return 0;
- }
2.创建子线程
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- struct sockInfo{
- int fd;
- pthread_t tid;
- struct sockaddr_in addr;
- };
- struct sockInfo sockInfos[128];
- void * working(void * arg) {
- //子线程和客户端通信 cfd 客户端信息 线程号(封装成一个结构体传入)
- //获取客户端信息
- struct sockInfo * pinfo = (struct sockInfo *) arg;
-
- char clientIP[16];
- inet_ntop(AF_INET, &pinfo->addr.sin_addr.s_addr, clientIP, sizeof(clientIP));
- unsigned short cliPort = ntohs(pinfo->addr.sin_port);
- printf("client ip is : %s, port is %d\n", clientIP, cliPort);
- //接收客户端发来的信息
- char recvBuf[1024] = {0};
- while(1) {
- int len = read(pinfo->fd, &recvBuf, sizeof(recvBuf) + 1);
- if(len == -1) {
- perror("read");
- exit(-1);
- } else if(len > 0) {
- printf("recv client data is : %s\n", recvBuf);
- } else {
- printf("client closed...\n");
- break;
- }
- write(pinfo->fd, recvBuf, sizeof(recvBuf) + 1);
- }
- close(pinfo->fd);
- exit(0);
- }
- int main() {
- //创建套接字
- int lfd = socket(PF_INET, SOCK_STREAM, 0);
- if(lfd == -1) {
- perror("socket");
- exit(-1);
- }
- struct sockaddr_in saddr;
- saddr.sin_addr.s_addr = INADDR_ANY;
- saddr.sin_family = AF_INET;
- saddr.sin_port = htons(9999);
- //绑定
- int ret = bind(lfd, (struct sockaddr*)&saddr, sizeof(saddr));
- if(ret == -1) {
- perror("bind");
- exit(-1);
- }
- //监听
- ret = listen(lfd, 8);
- if(ret == -1) {
- perror("listen");
- exit(-1);
- }
- //初始化数据
- 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 cliaddr;
- int len = sizeof(cliaddr);
- //接收连接
- int cfd = accept(lfd, (struct sockaddr*) &cliaddr, &len);
- struct sockInfo *pinfo;
- for(int i = 0; i < max; i++) {
- //从这个数组中找到一个可以用的sockInfo元素
- if(sockInfos[i].fd == -1) {
- pinfo = &sockInfos[i];
- break;
- }
- if(i == max - 1) {
- sleep(1);
- i--;
- }
- }
- pinfo->fd = cfd;
- memcpy(&pinfo->addr, &cliaddr, len);
-
- //创建子线程
- pthread_t tid;
- pthread_create(&pinfo->tid, NULL, working, pinfo);
-
- //回收子线程
- pthread_detach(pinfo->tid);
- }
- close(lfd);
- return 0;
- }