原理:
利用父子进程。父进程接收客户端的连接请求,子进程处理客户端的数据处理操作,两者各施其职。最终实现能够多个客户端连接一个服务端的操作。
代码:
服务端代码:
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>#define N 128void handler(int sig){wait(NULL); // 不关心子进程状态,等待子进程终止,回收其资源
}int main(int argc, const char *argv[]){int sockfd, acceptfd;struct sockaddr_in severaddr, clientaddr; // 存储客户端和服务器的地址信息 socklen_t addrlen = sizeof(severaddr);char buf[N] = "";pid_t pid;bzero(&severaddr, addrlen); bzero(&clientaddr, addrlen); // 清除特定长度的区域 if(argc < 3){printf("argc number error\n");return -1;}if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ // IPv4协议、流式套接字 (创建套接字) printf("socket create error\n");return -1;}/* 填充信息 */severaddr.sin_family = AF_INET;severaddr.sin_addr.s_addr = inet_addr(argv[1]);severaddr.sin_port = htons(atoi(argv[2]));/* 将套接字与服务器网络信息结构体绑定 */if(bind(sockfd, (struct sockaddr *) &severaddr, addrlen) < 0){printf("bind error\n");return -1;}if(listen(sockfd, 5) < 0){ // 设置被动监听模式 printf("listen error\n");return -1; }signal(SIGUSR1, handler); // 注册一个信号 while(1){ // 等待客户端连接请求 if((acceptfd = accept(sockfd, (struct sockaddr *) &clientaddr, &addrlen)) < 0){ // 阻塞等待 printf("accept error\n");}printf("ip : %s, port : %d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));pid = fork(); // 创建子进程 if(pid < 0){printf("fork error\n");return -1;}else if(pid == 0){ // 子进程 close(sockfd); // 只需要处理信息,不负责客户端连接while(1){ssize_t bytes;if((bytes = recv(acceptfd, buf, N, 0)) < 0){printf("recv error\n");return -1;}else if(bytes == 0){printf("no data\n");}else{if(strncmp(buf, "quit", 4) == 0){printf("client quit\n");sleep(1);break;}else{printf("client: %s\n", buf);strcat(buf, "-server");if(send(acceptfd, buf, N, 0) < 0){printf("send error\n");return -1;}}}}kill(getppid(), SIGUSR1);exit(0); }else{close(acceptfd); // 释放资源,关闭与客户端连接 } }return 0;
}
客户端代码:
#include <stdio.h>
#include <sys/types.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>
#include <signal.h>#define N 128void handler(int sig){wait(NULL); // 不关心子进程状态,等待子进程终止,回收其资源
}int main(int argc, const char *argv[]){int sockfd, acceptfd;struct sockaddr_in serveraddr, clientaddr; // 存储客户端和服务器的地址信息 socklen_t addrlen = sizeof(serveraddr);char buf[N] = "";pid_t pid;bzero(&serveraddr, addrlen); bzero(&clientaddr, addrlen); // 清除特定长度的区域 if(argc < 3){printf("argc number error\n");return -1;}if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){ // IPv4协议、流式套接字 (创建套接字) printf("socket create error\n");return -1;}/* 填充信息 */serveraddr.sin_family = AF_INET;serveraddr.sin_addr.s_addr = inet_addr(argv[1]);serveraddr.sin_port = htons(atoi(argv[2]));/* 将套接字与服务器网络信息结构体绑定 *//* if(bind(sockfd, (struct sockaddr *) &severaddr, addrlen) < 0){printf("bind error\n");return -1;}if(listen(sockfd, 5) < 0){ // 设置被动监听模式 printf("listen error\n");return -1; }signal(SIGUSR1, handler); // 注册一个信号
*/if(connect(sockfd, (struct sockaddr *) &serveraddr, addrlen) < 0){ // 发送客户端连接请求 printf("connect error\n");return -1;}while(1){ // 等待客户端连接请求 fgets(buf, N, stdin);buf[strlen(buf) - 1] = '\0';if(send(sockfd, buf, N, 0) < 0){printf("send error\n");return -1;}else{if(strncmp(buf, "quit", 4) == 0){break;}}if(recv(sockfd, buf, N, 0) < 0){printf("recv error\n");return -1;}printf("server: %s\n", buf); }return 0;
}