多进程并发服务器

父进程负责与客户端建立链接,子进程负责通信

//TCPserve.c

#include <stdio.h>
#include <unistd.h>	//linux标准头文件,包含了各种linux系统服务函数原型和数据结构的定义

#include <sys/types.h>	//基本数据类型头文件,包含有基本系统数据类型定义
#include <sys/socket.h>	//提供套接字函数原型与数据结构的定义
#include <netinet/in.h>	//提供数据结构sockaddr_in的定义
#include <arpa/inet.h>	//提供ip地址转函数原型的定义
#include <signal.h>

#include <stdlib.h>
#include <string.h>

#define PORT 1234	//定义服务器端端口
#define QLEN 10	//定义允许排队的连接数
#define BUFSIZE 1024	//定义缓冲区大小为1024b
void sig_chld(int);	//声明sig_chld()函数
void process_cli(int connectfd,struct sockaddr_in client);	//声明用于处理与客户之间通信的子函数
int main()
{
	int listenfd,connectfd;	//定义主套接字和从套接字
	pid_t pid;	//定义进程标识变量
	struct sockaddr_in server;	//定义服务器端点地址结构体变量
	struct sockaddr_in client;	//定义客户端端点地址结构体变量
	socklen_t ssize;
	if((listenfd = socket(AF_INET,SOCK_STREAM,0)) == -1)	//创建主套接字
	{
		perror("Creating socket failed.");
		exit(1);
	}
	int opt = SO_REUSEADDR;	//设置与主套接字关联的选项,允许主套接字重用本地地址和端口
	setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
	bzero(&server,sizeof(server));	//清空服务器端点地址结构体变量

	//以下代码段用于对服务器端点地址结构体变量进行赋值
	server.sin_family = AF_INET;
	server.sin_port = htons(PORT);
	server.sin_addr.s_addr = htonl(INADDR_ANY);

	//调用bind()函数将主套接字绑定到服务器的端点地址
	if(bind(listenfd,(struct sockaddr*)&server,sizeof(struct sockaddr)) == -1)
	{
		perror("bind error\n");
		exit(1);	//调用bind()函数出错则退出系统
	}
	if(listen(listenfd,QLEN) == -1)	//调用listen()函数使主套接字处于被动监听模式,并为主套接字建立一个数据输入队列
	{
		perror("listen() error\n");
		exit(1);	//调用listen()出错则退出系统
	}
	ssize = sizeof(struct sockaddr_in);
	signal(SIGCHLD,sig_chld);	//调用signal()函数为SIGCHLD信号安装handler
	while(1)	//循环调用accpet()函数接受客户连接请求,建立连接,并创建新的从套接字connectfd用于处理该连接
	{
		if((connectfd = accept(listenfd,(struct sockaddr*)&client,&ssize)) == -1)
		{
			perror("accept()error\n");
			exit(1);
		}
		if((pid = fork()) > 0)	//调用fork()函数创建新的从进程
		{
			close(connectfd);	//在父进程中关闭从淘金诶子描述符
			continue;	//父进程返回while循环
		}
		else if(pid == 0)
		{
			close(listenfd);	//在子进程中关闭主套接字描述符
			process_cli(connectfd,client);	//在子进程中调用process_cli()函数基于新建的从套接字connectfd来处理与客户之间的通信
			exit(0);	//处理完毕与客户的通信之后退出子进程
		}
		else
		{
			//调用fork()创建从进程出错退出系统
			printf("fork error\n");
			exit(0);
		}
	}
	close(listenfd);	//主进程结束时关闭主套接字
	return 0;
}

void process_cli(int connectfd,struct sockaddr_in client)
{
	int num;
	char recvbuf[BUFSIZE],sendbuf[BUFSIZE],cli_name[BUFSIZE];	//定义相关缓存区
	int len;
	printf("You got a connection from %s.",inet_ntoa(client.sin_addr));	//打印输出客户的ip地址
	num = recv(connectfd,cli_name,BUFSIZE,0);	//从从套接字中读取客户发送过来的数据(即接收该客户的名字)
	if(num == 0)	//若从套接字中数据读取完毕则关闭该套接字并返回
	{
		close(connectfd);
		printf("Client disconnected.\n");
		return ;
	}
	cli_name[num-1] = '\0';	//在字符串末尾添加字符串结束符
	printf("Client's name is %s.\n",cli_name);		//打印输出客户的名字
	while(num = recv(connectfd,recvbuf,BUFSIZE,0))	//循环接收来自客户的其他信息
	{
		recvbuf[num] = '\0';	//在字符串末尾添加字符串结束符
		printf("Recived client(%s)message:%s",cli_name,recvbuf);
		for(int i = 0;i<num-1;i++)	//将接收到的客户消息进行反转
		{
			sendbuf[i] = recvbuf[num-i -2];
		}
		sendbuf[num-1] = '\0';	//在字符串末尾添加字符串结束符
		len = strlen(sendbuf);
		send(connectfd,sendbuf,len,0);	//将反转后的数据回送给客户端
	}
	close(connectfd);	//关闭从套接字
}

void sig_chld(int signo)
{
	pid_t pid;
	int stat;
	while((pid = waitpid(-1,&stat,WNOHANG))>0)	
	{
		/*
		 *
		 *调用waitpid()函数等待子进程结束,若
		  有子进程结束,则waitpid()函数将对其
		  回收,从而避免产生僵尸进程
		 * */
		printf("chile%d terminated\n",pid);
	}
	return;

}
//TCPclient.c

#include <stdio.h>
#include <unistd.h>	//LINUX标准头文件,包含了各种LINUX系统服务函数原型和数据结构的定义
#include <string.h>
#include <sys/types.h>	//基本数据类型头文件,含有基本数据类型的定义
#include <sys/socket.h>	//提供套接字函数原型的定义
#include <netinet/in.h>	//提供数据结构sockaddr_in的定义
#include <netdb.h>	//含有hostent结构与gethostbyname函数的定义
#include <stdlib.h>

#define PORT 1234	//定义端口号
#define BUFSIZE 100	//定义缓冲区大小
void process(FILE *fp,int sockfd);	//声明用于处理与服务器之间的通信子函数
char *getMessage(char *sendline,int line,FILE *fp);	//声明用于实现接受用户键盘输入数据的子函数
int main(int argc,char *argv[])
{
	int fd;	//定义文件描述符变量
	struct hostent *he;	//定义hostent结构变量
	struct sockaddr_in server;	
	if(argc!=2)	//若用户输入的命令行参数错误则提示用法并退出系统
	{
		printf("Usage:%s<IP Adress>\n",argv[0]);
		exit(1);
	}
	if((he=gethostbyname(argv[1]))==NULL)	//调用gethostbyname()由用户输入的远程服务器的十进制ip地址获得其二进制的ip地址
	{
		printf("gethostbyname() error\n");
		exit(1);	
	
	}
	if((fd = socket(AF_INET,SOCK_STREAM,0)) == -1)	//创建套接字
	{
		printf("socket() error\n");
		exit(1);
	}
	bzero(&server,sizeof(server));	//清空服务器端点地址结构体变量

	//以下代码段用于对服务器端点地址结构体变量进行赋值
	server.sin_family = AF_INET;
	server.sin_port=htons(PORT);
	server.sin_addr = *((struct in_addr *)he->h_addr);
	if(connect(fd,(struct sockaddr *)&server,sizeof(struct sockaddr)) == -1)	//调用connect()函数向远程服务器发出连接请求
	{
		printf("connect() error\n");
		exit(1);
	}
	process(stdin,fd);	//调用子process()函数基于新创建的套接字与服务器之间进行交互
	close(fd);	//交互完毕,关闭套接字
	return 0;
}

void process(FILE *fp,int sockfd)
{
	char sendline[BUFSIZE],recvline[BUFSIZE];
	int numbytes;
	printf("Connected to server.\n");
	printf("Input name:");
	if(fgets(sendline,BUFSIZE,fp) == NULL) //调用fgets()函数接受用户键盘输入的客户端名字并存入缓冲区sendline
	{
		printf("\nExit\n");
		return;
	
	}
	send(sockfd,sendline,strlen(sendline),0);	//将sendline中缓存的客户端名字发送给服务器

	while(getMessage(sendline,BUFSIZE,fp)!=NULL)	//循环调用getMessage()函数接受用户键盘输入的信息并存入缓冲区sendline
	{
		send(sockfd,sendline,strlen(sendline),0);	//将缓存在sendline中的信息发送给服务器
		if((numbytes = recv(sockfd,recvline,BUFSIZE,0)) == 0)	//调用recv()函数接收服务器回送的信息并存入缓冲区recvline
		{
			printf("Server terminated.\n");
			return;
		}
		recvline[numbytes] = '\0';	//在字符串末尾添加字符串结束符号
		printf("Server Message:%s\n",recvline);	//打印输出服务器的回送信息内容
	
	}
	 printf("\nExit. \n");

}

char *getMessage(char *sendline,int len,FILE* fp)
{
	printf("Input string to server:");
	return(fgets(sendline,BUFSIZE,fp));	//调用fgets()函数接收用户键盘输入的信息并存入缓冲区sendline

}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值