linux网络编程echo多进程服务器

时间:2022-06-06 16:34:43

echo_server 多进程版本

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <memory.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
int sockfd; void sign_handler(int signo)
{
pid_t pid;
int stat;
if(signo==SIGINT)
{
printf("echo server close\n");
close(sockfd);
exit();
}
if(signo==SIGCHLD){
while((pid=waitpid(-,&stat,WNOHANG))>){
printf("child %d closed\n",pid);
}
}
return ;
}
void out_fd(int fd)
{
struct sockaddr_in arr;
socklen_t len=sizeof(arr);
if(getpeername(fd,(struct sockaddr*)&arr,&len)<){
perror("getpeername fail\n");
exit();
}
char ip[];
memset(&ip,,sizeof(ip));
inet_ntop(AF_INET,&arr.sin_addr.s_addr,ip,sizeof(ip));
printf("%s connected\n",ip);
}
void server_do(int fd)
{
char buffer[BUFSIZ];
while(){
printf("ready to read\n");
memset(buffer,,sizeof(buffer));
ssize_t size;
if((size=read(fd,buffer,sizeof(buffer)))<){
perror("server child read fail\n");
break;
}else if(size==){
break;
}else{
printf("number of received bytes=%ld\n",size);
buffer[size-]='\0';
printf("%s\n",buffer);
if(write(fd,buffer,size)<){
if(errno==EPIPE){
break;
}
perror("server child write fail\n");
}
}
}
}
int main(int argc,char *argv[])
{
int fd;
pid_t pid;
if(argc<)
{
printf("usage:%s <port>",argv[]);
exit();
}
//注册信号
if(signal(SIGINT,sign_handler)==SIG_ERR){
perror("signal sigint error\n");
exit();
}
if(signal(SIGCHLD,sign_handler)==SIG_ERR){
perror("signal sigint error\n");
exit();
}
/*create socket*/
sockfd=socket(AF_INET,SOCK_STREAM,);
if(sockfd<){
perror("socket create fail\n");
exit();
}
/*bind socket*/
struct sockaddr_in serveraddr;
serveraddr.sin_family=AF_INET;
serveraddr.sin_port=htons(atoi(argv[]));
serveraddr.sin_addr.s_addr=INADDR_ANY;
if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<){
perror("socket bind fail\n");
exit();
}
if(listen(sockfd,)<){
perror("socket listen fail\n");
exit();
}
/*accept*/
while(){
if((fd=accept(sockfd,NULL,NULL))<){
if(errno==EINTR){
continue;
}
perror("socket accept fail\n");
}
pid_t pid=fork();
if((pid=fork())<){
perror("fork fail\n");
continue;
}else if(pid==){
close(sockfd);
out_fd(fd);
server_do(fd);
exit();
}
close(fd);
}
return ;
}

echo_client

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <memory.h>
#include <signal.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int sockfd;
void cli_do(int sockfd)
{ /*双向通信*/ char buffer[BUFSIZ];
    char *promat=">";
size_t size;
while(1){
memset(buffer,0,sizeof(buffer));
write(STDOUT_FILENO,promat,1);
if((size=read(STDIN_FILENO,buffer,sizeof(buffer))>0){
if(size<0) continue;
buffer[size-1]='\0';
}else if(size==0){
       continue;
     }
if(write(sockfd,buffer,size)>0){
if(read(sockfd,buffer,sizeof(buffer))>0){
printf("%s\n",buffer);
}
}
}
}
int main(int argc,char *argv[])
{
if(argc<){
printf("usage:%s <ip><port>",argv[]);
exit();
}
/*create socket*/
sockfd=socket(AF_INET,SOCK_STREAM,);
if(sockfd<){
perror("socket create fail\n");
}
struct sockaddr_in serveraddr;
serveraddr.sin_family=AF_INET;
serveraddr.sin_port=htons(atoi(argv[]));
inet_pton(AF_INET,argv[],&serveraddr.sin_addr.s_addr);
if(connect(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<){
perror("server connect fail\n");
exit();
}
cli_do(sockfd);
close(sockfd);
return ;
}

 echo客户机改进:

客户机read阻塞与stdin时,如果此时服务器断开连接,服务器给客户机发送一个FIN,但是客户机此时阻塞与标准输入,它将看不到这个EOF

所以进程需要一个预先告知内核能力,使得内核一旦发现进程指定一个或者多个I/O条件就绪 ,它就通知进程,这个能力称为I/O多路复用

所以客户端cli_do改用select方式

cli_do修订

void cli_do(int sockfd)
{
char buffer[BUFSIZ];
char *promat=">";
size_t size;
fd_set rset;
FD_ZERO(&rset);
while(){
//select系统调用设置
FD_SET(sockfd,&rset);
FD_SET(STDIN_FILENO,&rset);
//设置select就绪事件
if(select(sockfd+,&rset,NULL,NULL,NULL)<){
perror("select close\n");
break;
}
if(FD_ISSET(STDIN_FILENO,&rset)){/*input is readable*/
if((size=read(STDIN_FILENO,buffer,sizeof(buffer))>0{
buffer[size-]='\0';
       }
write(sockfd,buffer,size)
}
if(FD_ISSET(sockfd,&rset)){/*socket is readable*/
if((size=read(sockfd,buffer,sizeof(buffer))==0){//服务端发送FIN过来
perror("server connect close\n");
break;
}else{
printf("%s\n",buffer);
}
}
}
}

echo客户端改进:

这种客户端--服务端   应-答  模式全双工管道数据并没有写满,存在空间的极大浪费

我们改用 批量输入-批量应答  模式

linux网络编程echo多进程服务器

使用select 和shutdown 函数,前者能接收到服务器关闭时的通知,后者运行我们批量输入

linux网络编程echo多进程服务器

cli_do修订

void cli_do(int sockfd)
{
int stdineof;
char buffer[BUFSIZ];
char *promat=">";
size_t size;
fd_set rset; FD_ZERO(&rset);
stdineof=;
while(){
write(STDOUT_FILENO,promat,);
//select系统调用设置
FD_SET(sockfd,&rset);
if(stdineof==) FD_SET(STDIN_FILENO,&rset);
//设置select就绪事件
if(select(sockfd+,&rset,NULL,NULL,NULL)<){
perror("select close\n");
break;
}
if(FD_ISSET(STDIN_FILENO,&rset)){/*input is readable*/
if((size=read(STDIN_FILENO,buffer,sizeof(buffer)))==){
stdineof=;
shutdown(sockfd,SHUT_WR);/*send FIN*/
FD_CLR(STDIN_FILENO,&rset);
continue;
}
buffer[size-]='\0';
write(sockfd,buffer,size);
}
if(FD_ISSET(sockfd,&rset)){/*socket is readable*/
if((size=read(sockfd,buffer,sizeof(buffer)))==){
if(stdineof==){
return;
}else{
perror("server terminated prematurely\n");
            break;
}
}
printf("%s\n",buffer);
}
}
}

 使用I/O复用模型  单进程 select 就绪事件监听描述符集 修改echo_server

echo_server 修订版

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <memory.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/select.h>
int sockfd; void sign_handler(int signo)
{
pid_t pid;
int stat;
if(signo==SIGINT)
{
printf("echo server close\n");
close(sockfd);
exit();
}
if(signo==SIGCHLD){
printf("client close\n");
wait();
}
return ;
}
void out_fd(int fd)
{
struct sockaddr_in arr;
socklen_t len=sizeof(arr);
if(getpeername(fd,(struct sockaddr*)&arr,&len)<){
perror("getpeername fail\n");
exit();
}
char ip[];
memset(&ip,,sizeof(ip));
inet_ntop(AF_INET,&arr.sin_addr.s_addr,ip,sizeof(ip));
printf("%s connected\n",ip);
} int main(int argc,char *argv[])
{
int fd;
pid_t pid;
if(argc<)
{
printf("usage:%s <port>",argv[]);
exit();
}
//注册信号
if(signal(SIGINT,sign_handler)==SIG_ERR){
perror("signal sigint error\n");
exit();
}
if(signal(SIGCHLD,sign_handler)==SIG_ERR){
perror("signal sigint error\n");
exit();
}
/*create socket*/
sockfd=socket(AF_INET,SOCK_STREAM,);
if(sockfd<){
perror("socket create fail\n");
exit();
}
/*bind socket*/
struct sockaddr_in serveraddr,cliaddr;
serveraddr.sin_family=AF_INET;
serveraddr.sin_port=htons(atoi(argv[]));
serveraddr.sin_addr.s_addr=INADDR_ANY;
if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<){
perror("socket bind fail\n");
exit();
}
if(listen(sockfd,)<){
perror("socket listen fail\n");
exit();
}
/*accept*/
int i,maxfd,maxi,connfd,clientfd;
int nready,client[FD_SETSIZE];
ssize_t size;
fd_set rset,allset;
char buffer[BUFSIZ];
socklen_t clien; maxfd=sockfd;
maxi=-;/*index into client[] array*/
for(i=;i<FD_SETSIZE;i++){
client[i]=-;
}
FD_ZERO(&allset);
FD_SET(sockfd,&allset);
for(;;){
rset=allset;
nready=select(maxfd+,&rset,NULL,NULL,NULL); if(FD_ISSET(sockfd,&rset)){/*new client connection*/
clien=sizeof(cliaddr);
if((connfd=accept(sockfd,(struct sockaddr*)&cliaddr,&clien))>){
out_fd(connfd);
}
for(i=;i<FD_SETSIZE;i++){
if(client[i]<){
client[i]=connfd;
break;
}
}
if(i==FD_SETSIZE){
perror("too many clients\n");
}
FD_SET(connfd,&allset);//添加新的监听就绪事件
if(connfd>maxfd) maxfd=connfd;
if(i>maxi) maxi=i;
if(--nready<=) continue;
}
for(i=;i<=maxi;i++){/*check all clients for data*/
if((clientfd=client[i])<) continue;
if(FD_ISSET(clientfd,&rset)){
printf("ready to read\n");
if((size=read(clientfd,buffer,sizeof(buffer)))==){//接受到EOF,client已经关闭
close(clientfd);
FD_CLR(clientfd,&allset);
client[i]=-;
}else{
printf("%s\n",buffer);
write(clientfd,buffer,size);
if(--nready<=) break;
}
} }
}
}

poll系统调用

void cli_do(int sockfd)
{
int stdineof;
char buffer[BUFSIZ];
char *promat=">";
size_t size;
fd_set rset; FD_ZERO(&rset);
stdineof=;
while(){
write(STDOUT_FILENO,promat,);
//select系统调用设置
FD_SET(sockfd,&rset);
if(stdineof==) FD_SET(STDIN_FILENO,&rset);
//设置select就绪事件
if(select(sockfd+,&rset,NULL,NULL,NULL)<){
perror("select close\n");
break;
}
if(FD_ISSET(STDIN_FILENO,&rset)){/*input is readable*/
if((size=read(STDIN_FILENO,buffer,sizeof(buffer)))==){
stdineof=;
shutdown(sockfd,SHUT_WR);/*send FIN*/
FD_CLR(STDIN_FILENO,&rset);
continue;
}
buffer[size-]='\0';
write(sockfd,buffer,size);
}
if(FD_ISSET(sockfd,&rset)){/*socket is readable*/
if((size=read(sockfd,buffer,sizeof(buffer)))==){
if(stdineof==){
return;
}else{
perror("server terminated prematurely\n");
break;
}
}
printf("%s\n",buffer);
}
}
}

echo_server

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <memory.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <sys/select.h>
#include <poll.h> int sockfd; void sign_handler(int signo)
{
pid_t pid;
int stat;
if(signo==SIGINT)
{
printf("echo server close\n");
close(sockfd);
exit();
}
if(signo==SIGCHLD){
printf("client close\n");
wait();
}
return;
}
void out_fd(int fd)
{
struct sockaddr_in arr;
socklen_t len=sizeof(arr);
if(getpeername(fd,(struct sockaddr*)&arr,&len)<){
perror("getpeername fail\n");
exit();
}
char ip[]; memset(&ip,,sizeof(ip));
inet_ntop(AF_INET,&arr.sin_addr.s_addr,ip,sizeof(ip));
printf("%s connected\n",ip);
} int main(int argc,char *argv[])
{
printf("tsest"); if(argc<)
{
printf("usage:%s <port>",argv[]);
exit();
}
//注册信号
if(signal(SIGINT,sign_handler)==SIG_ERR){
perror("signal sigint error\n");
exit();
}
if(signal(SIGCHLD,sign_handler)==SIG_ERR){
perror("signal sigint error\n");
exit();
}
/*create socket*/
sockfd=socket(AF_INET,SOCK_STREAM,);
if(sockfd<){
perror("socket create fail\n");
exit();
}
/*bind socket*/
struct sockaddr_in serveraddr,cliaddr;
serveraddr.sin_family=AF_INET;
serveraddr.sin_port=htons(atoi(argv[]));
serveraddr.sin_addr.s_addr=INADDR_ANY;
if(bind(sockfd,(struct sockaddr*)&serveraddr,sizeof(serveraddr))<){
perror("socket bind fail\n");
exit();
}
if(listen(sockfd,)<){
perror("socket listen fail\n");
exit();
}
/*poll系统调用*/
int i,maxfd,maxi,connfd,clientfd,nready,open_max;
ssize_t size;
char buffer[BUFSIZ];
socklen_t clien;
open_max=sysconf(_SC_OPEN_MAX); struct pollfd client[open_max];
client[].fd=sockfd;
client[].events=POLLRDNORM;
for(i=;i<open_max;i++) client[i].fd=-;
maxi=; for(;;){
nready=poll(client,maxi+,); if(client[].revents & POLLRDNORM){/*new client connection*/
printf("werwerew");
break;
clien=sizeof(cliaddr);
if((connfd=accept(sockfd,(struct sockaddr*)&cliaddr,&clien))>) out_fd(connfd); for(i=;i<open_max;i++){
if(client[i].fd<){
client[i].fd=connfd;
break;
}
}
if(i==open_max) perror("too many clients\n");
client[i].events=POLLRDNORM;//添加新的监听就绪事件
if(i>maxi) maxi=i;
if(--nready<=) continue;
}
for(i=;i<=maxi;i++){/*check all clients for data*/
if((clientfd=client[i].fd)<) continue;
if(client[i].revents&(POLLRDNORM|POLLERR)){
printf("ready to read\n");
break;
if((size=read(clientfd,buffer,sizeof(buffer)))==){//接受到EOF,client已经关闭
close(clientfd);
client[i].fd=-;
}else{
printf("%s\n",buffer);
write(clientfd,buffer,size);
if(--nready<=) break;
}
} }
}
return ;
}