accept函数和read/write函数会导致服务器阻塞,只能实现1对1的服务,鉴于此,为了实现1对多的服务,采用了多线程和多进程的技术。
fd_client = accept(fd, (struct sockaddr*)&clientscokaddr, &clientaddrlen); //造成阻塞
#include <cstdio> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include <pthread.h> #include <errno.h> #define SERV_PORT 5050 #define SERV_IP_ADDR "192.168.0.158" void* func_recall(void* arg); char botton[64]; int main() { int fd = -1; int result = 0,i=0; struct sockaddr_in mysockaddr; struct sockaddr_in clientscokaddr; socklen_t clientaddrlen; static pthread_t nptd; /* 1. 创建socket fd */ fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); return -1; } /*优化4: 允许绑定地址快速重用 */ int b_reuse = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int)); /*2. 绑定 */ /*2.1 填充struct sockaddr_in结构体变量 */ bzero((void*)&mysockaddr, sizeof(mysockaddr)); mysockaddr.sin_family = AF_INET; mysockaddr.sin_port = htons(SERV_PORT);//网络字节序的端口号 #if 1 //mysockaddr.sin_addr.s_addr = inet_addr(SERV_IP_ADDR); mysockaddr.sin_addr.s_addr = htonl(INADDR_ANY); #else result = inet_pton(AF_INET, SERV_IP_ADDR, (void*)&mysockaddr.sin_addr); if (result != 1) { perror("inet_pton"); return -1; } #endif /*2.2 绑定 */ result = bind(fd, (struct sockaddr*)&mysockaddr, sizeof(mysockaddr)); if (result != 0) { perror("bind"); return -1; } /*3. 调用listen()把主动套接字变成被动套接字 */ result = listen(fd, 5); if (result != 0) { perror("listen"); return -1; } /*4. 阻塞等待客户端连接请求 */ /*优化2:通过程序获取刚建立连接的socket的客户端的IP地址和端口号*/ int fd_client; while (1) { bzero((void*)&clientscokaddr, sizeof(clientscokaddr)); fd_client = accept(fd, (struct sockaddr*)&clientscokaddr, &clientaddrlen); if (-1 == result) { perror("accpet"); return 0; } char ipv4_addr[16]; if (!inet_ntop(AF_INET, (void*)&clientscokaddr.sin_addr, ipv4_addr, clientaddrlen)) { perror("inet_ntop"); return -1; } printf("Clinet(%s:%d) is connected!\n", ipv4_addr, ntohs(clientscokaddr.sin_port)); result = pthread_create(&nptd, 0, func_recall, (void*)&fd_client); if (result != 0) { perror("pthread_create"); return -1; } i++; } close(fd); close(fd_client); return 0; } void* func_recall(void* arg) { int result = 0; int newfd = *(int*)arg; printf("handler thread: newfd =%d\n", newfd); //..和newfd进行数据读写 char buf[BUFSIZ]; while (1) { bzero(buf, BUFSIZ); do { result = read(newfd, buf, BUFSIZ - 1); } while (result < 0 && EINTR == errno); if (result < 0) { perror("read"); break; } if (!result) break; printf("message is:%s\n", buf); } close(newfd); return 0; }
--------------------------------------------分割线---------------------------------------------------------
#include <cstdio> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include <errno.h> #define SERV_PORT 5050 #define SERV_IP_ADDR "192.168.0.158" int main() { int fd = -1; int result = 0; struct sockaddr_in mysockaddr; char MESSAGE[] = "hELLo wOLd!!!\n"; /* 1. 创建socket fd */ fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); return -1; } /*2.连接服务器 */ /*2.1 填充struct sockaddr_in结构体变量 */ bzero((void*)&mysockaddr, sizeof(mysockaddr)); mysockaddr.sin_family = AF_INET; mysockaddr.sin_port = htons(SERV_PORT); //网络字节序的端口号 #if 0 mysockaddr.sin_addr.s_addr = inet_addr(SERV_IP_ADDR); #else result = inet_pton(AF_INET, SERV_IP_ADDR, (void*)&mysockaddr.sin_addr); if (result != 1) { perror("inet_pton"); return -1; } #endif /*2.2 connect 链接服务端*/ result = connect(fd, (struct sockaddr*)&mysockaddr, sizeof(mysockaddr)); if (result != 0) { perror("connect"); return -1; } printf("Client staring...OK!\n"); /*3. 读写数据 */ while (1) { do { result = write(fd, (void*)MESSAGE, sizeof(MESSAGE)); } while (result < 0 && EINTR == errno); //判断是否阻塞了 sleep(10); } close(fd); return 0; }
#include <cstdio> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include <errno.h> #include <signal.h> #include <sys/wait.h> #define SERV_PORT 5050 //端口号 根据需要可自行修改 #define SERV_IP_ADDR "192.168.0.158" //IP地址 根据需要可自行修改 void* func_recall(void* arg); void handler(int signo) { //自动回收子进程 防止僵尸进程 if (signo == SIGCHLD) { printf("Recall SIGINT\n"); waitpid(-1, NULL, WNOHANG); } } int main() { int fd = -1; int result = 0; struct sockaddr_in mysockaddr; struct sockaddr_in clientscokaddr; socklen_t clientaddrlen; static pthread_t nptd; /* 1. 创建socket fd */ fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); return -1; } /*优化4: 允许绑定地址快速重用 */ int b_reuse = 1; setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof(int)); /*2. 绑定 */ /*2.1 填充struct sockaddr_in结构体变量 */ bzero((void*)&mysockaddr, sizeof(mysockaddr)); mysockaddr.sin_family = AF_INET; mysockaddr.sin_port = htons(SERV_PORT);//网络字节序的端口号 mysockaddr.sin_addr.s_addr = htonl(INADDR_ANY); /*2.2 绑定 */ result = bind(fd, (struct sockaddr*)&mysockaddr, sizeof(mysockaddr)); if (result != 0) { perror("bind"); return -1; } /*3. 调用listen()把主动套接字变成被动套接字 */ result = listen(fd, 5); if (result != 0) { perror("listen"); return -1; } /*4. 阻塞等待客户端连接请求 */ /*优化2:通过程序获取刚建立连接的socket的客户端的IP地址和端口号*/ int fd_client; pid_t pid; signal(SIGCHLD, handler); while (1) { bzero((void*)&clientscokaddr, sizeof(clientscokaddr)); fd_client = accept(fd, (struct sockaddr*)&clientscokaddr, &clientaddrlen); if (-1 == result) { perror("accpet"); return 0; } char ipv4_addr[16]; if (!inet_ntop(AF_INET, (void*)&clientscokaddr.sin_addr, ipv4_addr, clientaddrlen)) { perror("inet_ntop"); return -1; } printf("Clinet(%s:%d) is connected!\n", ipv4_addr, ntohs(clientscokaddr.sin_port)); pid = fork(); if (pid < 0) { perror("fork"); return -1; } else if (pid > 0) { //父进程 close(fd_client); } else { //子进程 close(fd); func_recall((void*)&fd_client); //调用读写函数 return 0; } } close(fd); close(fd_client); return 0; } void* func_recall(void* arg) { int result = 0; int newfd = *(int*)arg; printf("handler thread: newfd =%d\n", newfd); //..和newfd进行数据读写 char buf[BUFSIZ]; while (1) { bzero(buf, BUFSIZ); do { result = read(newfd, buf, BUFSIZ - 1); } while (result < 0 && EINTR == errno); //如果遇到阻塞,则重新执行一次 if (result < 0) { perror("read"); break; } if (!result) break; printf("message is:%s\n", buf); } close(newfd); return 0; }
--------------------------------------------分割线---------------------------------------------------------
#include <cstdio> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include <errno.h> #define SERV_PORT 5050 #define SERV_IP_ADDR "192.168.0.158" int main() { int fd = -1; int result = 0; struct sockaddr_in mysockaddr; char MESSAGE[] = "hELLo wOLd!!!\n"; /* 1. 创建socket fd */ fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { perror("socket"); return -1; } /*2.连接服务器 */ /*2.1 填充struct sockaddr_in结构体变量 */ bzero((void*)&mysockaddr, sizeof(mysockaddr)); mysockaddr.sin_family = AF_INET; mysockaddr.sin_port = htons(SERV_PORT); //网络字节序的端口号 #if 0 mysockaddr.sin_addr.s_addr = inet_addr(SERV_IP_ADDR); #else result = inet_pton(AF_INET, SERV_IP_ADDR, (void*)&mysockaddr.sin_addr); if (result != 1) { perror("inet_pton"); return -1; } #endif /*2.2 connect 链接服务端*/ result = connect(fd, (struct sockaddr*)&mysockaddr, sizeof(mysockaddr)); if (result != 0) { perror("connect"); return -1; } printf("Client staring...OK!\n"); /*3. 读写数据 */ while (1) { do { result = write(fd, (void*)MESSAGE, sizeof(MESSAGE)); } while (result < 0 && EINTR == errno); //判断是否阻塞了 sleep(10); } close(fd); return 0; }