#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <errno.h>
using namespace std;
void recycle(int arg)
{
pid_t pid;
while ((pid=waitpid(-1, NULL, WNOHANG))<=0) {}
cout<<"one child is dying, pid is"<<pid<<endl;
}//接受到SIGCHLD信号时的回收函数
/*并发服务器中,父进程负责接收客户端,子进程负责与客户端通信,每接收到一个客户端,就开一个子进程*/
int main(int argc, const char *argv[])
{
if (argc<2)
{
cout<<"input :./a.out port";
exit(1);
}
int sfd, cfd;
int port=atoi(argv[1]);
char buf[256];
int n;
/*定义结构体sockaddr,存储服务端的ip和端口*/
struct sockaddr_in addr_server;
addr_server.sin_port=htons(port);
addr_server.sin_addr.s_addr=htonl(INADDR_ANY);
addr_server.sin_family=AF_INET;
/*定义结构体sigaction,用来捕捉SIGCHLD信号*/
struct sigaction act;
act.sa_handler=recycle;
act.sa_flags=0;
sigemptyset(&act.sa_mask);
sigaction(SIGCHLD, &act, NULL);
struct sockaddr_in addr_client;
socklen_t addrlen=sizeof(addr_client);
sfd=socket(AF_INET, SOCK_STREAM, 0);//建立套接字
bind(sfd, (struct sockaddr*) &addr_server, sizeof(addr_server));//绑定ip和端口
listen(sfd, 20);
//父进程接收连接请求
// accept阻塞的时候被信号中断, 处理信号对应的操作之后
// 回来之后不阻塞了, 直接返回-1, 这时候 errno==EINTR
while (1)
{
cfd=accept(sfd, (struct sockaddr*) &addr_client, &addrlen);
while (cfd==-1&&errno==EINTR)//当
cfd=accept(sfd, (struct sockaddr*) &addr_client, &addrlen);
if (cfd==-1&&errno!=EINTR)
{
cout<<"accept is error!";
exit(1);
}
pid_t pid=fork();//当接受到一个客户端以后,打开一个子进程和客户端通信
if (pid<0)
{
cout<<"fork is error!";
exit(1);
}
else if (pid==0)//子进程
{
close(sfd);子进程中不再需要接受客户端,所以关闭接受客户端的描述符
char ip[64];
while (1)
{
cout<<"the client's ip is"<<inet_ntop(AF_INET, &addr_client.sin_addr.s_addr, ip, sizeof(ip))<<"\n";
cout<<"the client's port is"<<ntohs(addr_client.sin_port)<<"\n";
int n=read(cfd, buf, sizeof(buf));
if (n==-1)
{
cout<<"read is error!";
exit(1);
}else if (n==0)//read是阻塞的,如果返回结果是0, 说明是客户端发送了文件结束符,表明要结束了,不是客户端不发消息就是0
{
cout<<"client is over!";
break;
}else
{
for (int i=0; i<n; i++)
buf[i]=toupper(buf[i]);
write(cfd, buf, n);
}
}
return 0;
}
else//父进程
{
close(cfd);父进程不需要用通信的文件描述符,所以关闭
}
}
close(sfd);//能执行到这一步就只有父进程了,父进程的通信描述符已经关闭了,所以只需要关闭接受描述符就行了。
return 0;
}
现象是首先执行./a.out文件
可以打开多个终端
服务器终端上结果,每次连入一个客户端或者在客户端做一次输入,都会显示一个通信的ip和端口号。