Linux C TCP Server [單進程(單線程)/多進程/多線程] 範例原始碼
Linux C TCP Server [單進程(單線程)/多進程/多線程] 範例原始碼
資料來源: https://mp.weixin.qq.com/s/dgGBZqtwM0D-Q4wEjC-lpw
單進程(單線程)
// server.c #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <arpa/inet.h> int main() { // 1. 创建监听的套接字 int lfd = socket(AF_INET, SOCK_STREAM, 0); // 2. 将socket()返回值和本地的IP端口绑定到一起 struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(10000); // 大端端口 // INADDR_ANY代表本机的所有IP, 假设有三个网卡就有三个IP地址 // 这个宏可以代表任意一个IP地址 addr.sin_addr.s_addr = INADDR_ANY; // 这个宏的值为0 == 0.0.0.0 int ret = bind(lfd, (struct sockaddr*)&addr, sizeof(addr)); // 3. 设置监听 ret = listen(lfd, 128); // 4. 阻塞等待并接受客户端连接 struct sockaddr_in cliaddr; int clilen = sizeof(cliaddr); int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &clilen); // 5. 和客户端通信 while(1) { // 接收数据 char buf[1024]; memset(buf, 0, sizeof(buf)); int len = read(cfd, buf, sizeof(buf)); if(len > 0) { printf("客户端say: %s\n", buf); write(cfd, buf, len); } else if(len == 0) { printf("客户端断开了连接...\n"); break; } else { perror("read"); break; } } close(cfd); close(lfd); return 0; }
多進程
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <arpa/inet.h> #include <signal.h> #include <sys/wait.h> #include <errno.h> // 信号处理函数 void callback(int num) { while(1) { pid_t pid = waitpid(-1, NULL, WNOHANG); if(pid <= 0) { printf("子进程正在运行, 或者子进程被回收完毕了\n"); break; } printf("child die, pid = %d\n", pid); } } int childWork(int cfd); int main() { // 1. 创建监听的套接字 int lfd = socket(AF_INET, SOCK_STREAM, 0); if(lfd == -1) { perror("socket"); exit(0); } // 2. 将socket()返回值和本地的IP端口绑定到一起 struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(10000); // 大端端口 // INADDR_ANY代表本机的所有IP, 假设有三个网卡就有三个IP地址 // 这个宏可以代表任意一个IP地址 // 这个宏一般用于本地的绑定操作 addr.sin_addr.s_addr = INADDR_ANY; // 这个宏的值为0 == 0.0.0.0 // inet_pton(AF_INET, "192.168.237.131", &addr.sin_addr.s_addr); int ret = bind(lfd, (struct sockaddr*)&addr, sizeof(addr)); if(ret == -1) { perror("bind"); exit(0); } // 3. 设置监听 ret = listen(lfd, 128); if(ret == -1) { perror("listen"); exit(0); } // 注册信号的捕捉 struct sigaction act; act.sa_flags = 0; act.sa_handler = callback; sigemptyset(&act.sa_mask); sigaction(SIGCHLD, &act, NULL); // 接受多个客户端连接, 对需要循环调用 accept while(1) { // 4. 阻塞等待并接受客户端连接 struct sockaddr_in cliaddr; int clilen = sizeof(cliaddr); int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &clilen); if(cfd == -1) { if(errno == EINTR) { // accept调用被信号中断了, 解除阻塞, 返回了-1 // 重新调用一次accept continue; } perror("accept"); exit(0); } // 打印客户端的地址信息 char ip[24] = {0}; printf("客户端的IP地址: %s, 端口: %d\n", inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ip, sizeof(ip)), ntohs(cliaddr.sin_port)); // 新的连接已经建立了, 创建子进程, 让子进程和这个客户端通信 pid_t pid = fork(); if(pid == 0) { // 子进程 -> 和客户端通信 // 通信的文件描述符cfd被拷贝到子进程中 // 子进程不负责监听 close(lfd); while(1) { int ret = childWork(cfd); if(ret <=0) { break; } } // 退出子进程 close(cfd); exit(0); } else if(pid > 0) { // 父进程不和客户端通信 close(cfd); } } return 0; } // 5. 和客户端通信 int childWork(int cfd) { // 接收数据 char buf[1024]; memset(buf, 0, sizeof(buf)); int len = read(cfd, buf, sizeof(buf)); if(len > 0) { printf("客户端say: %s\n", buf); write(cfd, buf, len); } else if(len == 0) { printf("客户端断开了连接...\n"); } else { perror("read"); } return len; }
多線程
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> struct SockInfo { int fd; // 通信 pthread_t tid; // 线程ID struct sockaddr_in addr; // 地址信息 }; struct SockInfo infos[128]; void* working(void* arg) { while(1) { struct SockInfo* info = (struct SockInfo*)arg; // 接收数据 char buf[1024]; int ret = read(info->fd, buf, sizeof(buf)); if(ret == 0) { printf("客户端已经关闭连接...\n"); info->fd = -1; break; } else if(ret == -1) { printf("接收数据失败...\n"); info->fd = -1; break; } else { write(info->fd, buf, strlen(buf)+1); } } return NULL; } int main() { // 1. 创建用于监听的套接字 int fd = socket(AF_INET, SOCK_STREAM, 0); if(fd == -1) { perror("socket"); exit(0); } // 2. 绑定 struct sockaddr_in addr; addr.sin_family = AF_INET; // ipv4 addr.sin_port = htons(8989); // 字节序应该是网络字节序 addr.sin_addr.s_addr = INADDR_ANY; // == 0, 获取IP的操作交给了内核 int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr)); if(ret == -1) { perror("bind"); exit(0); } // 3.设置监听 ret = listen(fd, 100); if(ret == -1) { perror("listen"); exit(0); } // 4. 等待, 接受连接请求 int len = sizeof(struct sockaddr); // 数据初始化 int max = sizeof(infos) / sizeof(infos[0]); for(int i=0; i<max; ++i) { bzero(&infos[i], sizeof(infos[i])); infos[i].fd = -1; infos[i].tid = -1; } // 父进程监听, 子进程通信 while(1) { // 创建子线程 struct SockInfo* pinfo; for(int i=0; i<max; ++i) { if(infos[i].fd == -1) { pinfo = &infos[i]; break; } if(i == max-1) { sleep(1); i--; } } int connfd = accept(fd, (struct sockaddr*)&pinfo->addr, &len); printf("parent thread, connfd: %d\n", connfd); if(connfd == -1) { perror("accept"); exit(0); } pinfo->fd = connfd; pthread_create(&pinfo->tid, NULL, working, pinfo); pthread_detach(pinfo->tid); } // 释放资源 close(fd); // 监听 return 0; }