Linux 基本语句_15_Tcp并发服务器

原理:

利用父子进程。父进程接收客户端的连接请求,子进程处理客户端的数据处理操作,两者各施其职。最终实现能够多个客户端连接一个服务端的操作。

代码:

服务端代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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; 
}

效果:

Linux 基本语句_15_Tcp并发服务器_第1张图片

你可能感兴趣的:(linux,服务器,tcp/ip)