原理:
利用父子进程。父进程接收客户端的连接请求,子进程处理客户端的数据处理操作,两者各施其职。最终实现能够多个客户端连接一个服务端的操作。
代码:
服务端代码:
#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 128
void 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 128
void 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;
}