首页 > 技术文章 > 设计并实现并发型 Echo 服务器 (Linux网络编程实践)

greenpepper 2020-12-18 09:30 原文

Echo服务器

设计并实现并发型 Echo 服务器 按照 RFC 862 协议规定,实现并发型 Echo 服务器应用。 要求给出服务端及客户端应用程序。采用守护进程实现。

思路

​ 简单的TCP协议,没啥好讲的,实现就行。对于每一个链接,fork出一个子进程持续处理。注意通过 \(recv\) 的返回值判断断开连接。守护进程把原来的main函数封装进daemon就行了。

​ 守护进程不会在前台,想关掉请 \(top\) 一下,找到进程然后 $ kill -9 id $ 就行。如果发现端口占用绑定失败,请看看是否有挂在后台的进程占用了端口。

代码部分

$ server.c $

#include<stdio.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>
void Error(char str[]){
	printf("%s error!\n",str);
	exit(-1);
}
int main(int argc,char* argv[]){
	int s_fd,c_fd,ret;
	struct	sockaddr_in s_addr,c_addr;
    socklen_t c_len=sizeof(c_addr);
	char buf[256];
	char ip[20]={0};
	s_fd=socket(AF_INET,SOCK_STREAM,0);
	if(s_fd<0)Error("socket");
	memset(&s_addr,0,sizeof(s_addr));
	s_addr.sin_family=AF_INET;
	s_addr.sin_port=htons(8000);
	s_addr.sin_addr.s_addr=htonl(INADDR_ANY);
	ret=bind(s_fd,(struct sockaddr*)&s_addr,sizeof(s_addr));
	if(ret==-1)Error("bind");
	ret=listen(s_fd,7);
	if(ret)Error("listen");
	int pid=0;
	while(1){
		c_fd=accept(s_fd,(struct sockaddr*)&c_addr ,&c_len );
		if(c_fd<0)Error("accept!");
		pid=fork();
		if(pid==0)break;
	}
	printf("a client has Connected!\n");
	fflush(stdout);
	int tot=0;

	while(1){
		ret=recv(c_fd,buf,(size_t)255,0);
		buf[ret]=0;
		printf("from:%s:%d,tot:%d recv:%s\n",inet_ntoa(c_addr.sin_addr),(int)c_addr.sin_port,++tot,buf);
		send(c_fd,buf,ret,0);
		if(ret==0)break;
		sleep(1);
	}
	printf("from:%s:%d client closed\n",inet_ntoa(c_addr.sin_addr),(int)c_addr.sin_port);
	return 0;
}

守护进程版本

#include<stdio.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<netinet/in.h>
#include<stdlib.h>
#include<string.h>
#include<arpa/inet.h>
void Error(char str[]){
	printf("%s error!\n",str);
	exit(-1);
}
void work();
int daemon_init(void){
	pid_t pid;
	if((pid=fork())<0)return -1;
	else if(pid!=0){
		exit(0);
	}
	setsid();
	chdir("/");
	umask(0);
	work();
	return 0;
}
void work(){
	int s_fd,c_fd,ret;
	struct	sockaddr_in s_addr,c_addr;
    socklen_t c_len=sizeof(c_addr);
	char buf[256];
	char ip[20]={0};
	s_fd=socket(AF_INET,SOCK_STREAM,0);
	if(s_fd<0)Error("socket");
	memset(&s_addr,0,sizeof(s_addr));
	s_addr.sin_family=AF_INET;
	s_addr.sin_port=htons(8000);
	s_addr.sin_addr.s_addr=htonl(INADDR_ANY);
	ret=bind(s_fd,(struct sockaddr*)&s_addr,sizeof(s_addr));
	if(ret==-1)Error("bind");
	ret=listen(s_fd,7);
	if(ret)Error("listen");
	int pid=0;
	while(1){
		c_fd=accept(s_fd,(struct sockaddr*)&c_addr ,&c_len );
		if(c_fd<0)Error("accept!");
		pid=fork();
		if(pid==0)break;
	}
	int tot=0;

	while(1){
		ret=recv(c_fd,buf,(size_t)255,0);
		buf[ret]=0;
		send(c_fd,buf,ret,0);
		if(ret==0)break;
		sleep(1);
	}
}

int main(int argc,char* argv[]){
	return daemon_init();
}

客户端两个方式没啥区别。

#include<stdio.h>
#include<unistd.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<stdlib.h>
#include<string.h>
void Error(char *str){
	printf("%s error!\n",str);
	exit(-1);
}	
int main(int argc,char* argv[]){
	int s_fd,ret;
	char r_buf[256], s_buf[256],ip[40];
	struct sockaddr_in s_addr;
	printf("please input server ip\n");
	scanf("%s",ip);
	ret=s_fd=socket(AF_INET,SOCK_STREAM,0);
	if(ret<0)Error("socket");
	memset(&s_addr,0,sizeof(s_addr));
	s_addr.sin_family=AF_INET;
	s_addr.sin_port=htons(8000);
	s_addr.sin_addr.s_addr=inet_addr(ip);
	ret=connect(s_fd,(struct sockaddr*)&s_addr,sizeof(s_addr));
	if(ret<0)Error("connect");
	printf("Connect!");
	while(1){
		scanf("%s",s_buf);
		send(s_fd,s_buf,strlen(s_buf),0);
		ret=recv(s_fd,r_buf,255,0);
		r_buf[ret]=0;
		printf("recv:%s\n",r_buf);
	}
	return 0;
}

推荐阅读