一、多进程服务器代码
#include<stdio.h> #include<unistd.h> #include<stdlib.h> #include<string.h> #include <sys/types.h> /* See NOTES */ #include <sys/socket.h> #include <arpa/inet.h> #include <sys/wait.h> #include<signal.h> #include<errno.h> void reclye()//进程回收函数 { pid_t pid; while((pid=waitpid(-1,NULL,WNOHANG))>0)//-1表示回收任意子进程;NULL表示不关心子进程的状态;WNOHANG表示如果当前没有子进程退出会立即返回,如果有子进程退出则返回退出的id, { printf("child died,pid=%d\n",pid); } } int main(int argc,const char* argv[]) { if(argc<2) { printf("eg:./a.out port\n"); return -1; } int port=atoi(argv[1]);//argv[1],从命令行输入端口号 //1、创建socket套接字,bind绑定,listen监听 int lfd=socket(AF_INET,SOCK_STREAM,0); if(lfd==-1)//socket创建套接字失败返回-1,并且设置errno,出现错误的时候系统会自动检测错误类型并将错误信息打印到命令行,下述代码同理 { printf("socket err"); return -1; } //bind struct sockaddr_in ser_addr;//创建结构体存储服务器ip地址、端口号 ser_addr.sin_family=AF_INET;//ipv4协议 ser_addr.sin_addr.s_addr=htonl(INADDR_ANY);//htonl函数将ip地址从本机字节顺序转换到网络字节顺序(l表示无符号长整型),INADDR_ANY表示本机任意可用IP ser_addr.sin_port=htons(port);//htons函数将端口号从本机字节顺序转换到网络字节顺序(s表示无符号短整型) int ret=bind(lfd,(struct sockaddr*)&ser_addr,sizeof(ser_addr)); if(ret==-1) { printf("bind error"); return -1; } //listen listen(lfd,128); //使用信号回收子进程 struct sigaction act;//创建捕捉信号的结构体 act.sa_flags=0;//一般赋值为0 act.sa_handler=reclye;//函数指针指向被调用函数,执行捕捉函数的时候调用该函数 sigemptyset(&act.sa_mask);//清空信号集 sigaction(SIGCHLD,&act,NULL);//子进程在暂停或退出的时候会发送SIGCHLD信号 //2、循环等待接收客户端连接--accept,如果有连接创建子进程 struct sockaddr_in cli_addr; socklen_t cli_addr_len=sizeof(cli_addr); while(1) { //父进程接收连接请求 //accept阻塞的时候被信号中断,处理信号对应的操作之后 //回来之后不阻塞了,直接返回-1,这个时候errno被设置为EINTR int cfd=accept(lfd,(struct sockaddr*)&cli_addr,&cli_addr_len); while(cfd==-1&&errno==EINTR) { cfd=accept(lfd,(struct sockaddr*)&cli_addr,&cli_addr_len); } // char ip[16]; memset(ip,0,sizeof(ip)); const char* addr_dst=inet_ntop(AF_INET,&cli_addr.sin_addr.s_addr,ip,sizeof(ip));//将客户端IP转换为字符串形式存储到ip中 printf("client IP:%s,port:%d,connect sucessful\n",addr_dst,ntohs(cli_addr.sin_port));//将端口号从网络字节顺序转换到本机字节顺序(无符号短整型) pid_t pid=fork(); if(pid==0)//子进程 { close(lfd); char buf[1024]; while(1)//使用cfd文件描述符通信 { //接收数据 memset(buf,0,sizeof(buf)); int len=read(cfd,buf,sizeof(buf)); if(len<0) { printf("read error"); exit(1); }else if(len==0) { printf("IP:%s,客户端断开了连接\n",addr_dst); close(cfd); break; } printf("IP:%s,recv buf:%s\n",addr_dst,buf); //发送数据,将接收到的数据发送给客户端 write(cfd,buf,sizeof(buf)); } exit(1);//子进程退出 } else if(pid>0) { //wait();不能等待回收,因为wait/waitpid都会阻塞,所以应该用信号回收子进程 close(cfd); } } return 0; }
注:注释较为详细,解题思路以后再补上(见谅)
二、运行截图
1、服务器端运行截图
2、客户端A、B运行截图
注:nc命令可用模拟客户端测试,nc+ip+端口号
为了测试方便,仅模拟了两个客户端同时连接服务器,也可用同时多个客户端连接服务器
一入编程深似海,多学多查多动手