设计并实现并发型 Echo 服务器 (Linux网络编程实践)
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;
}