TCP/IP,多进程服务器,可多连接,通过fork创建子进程并由子进程维护与客户端的连接,通过信号量回收子进程
一、思路 1、服务端创建并维护用于监听端口的套接字 2、当监听套接字accept到客户端的连接后,使用fork创建子进程 3、通过判断fork的返回值分辨父进程和子进程, 4、父进程关闭与客户端通信的套接字,子进程关闭监听的套接字,原因见: Linux下fork复制出来的进程数据中文件描述符的共享状态,.data数据拷贝的设定,无用文件描述符需要关闭,仅保留当前进程需要使用的文件描述符 5、客户端关闭后,与之相连的子进程退出变成僵尸进程,父进程通过信号量回收子进程
二、服务端连接思路 1、父进程创建子进程并进行判断
Listen(lfd,128);
while(1)
{
cfd = Accept(lfd,(struct sockaddr*)&clt_addr,&clt_addr_len);
pid = fork();
if(pid < 0)
{
sys_err("fork error");
}
else if(pid == 0)
{
close(lfd);
break;
}
else
{
close(cfd);
}
}
if(pid == 0)
{
while(1)
{
ret = read(cfd,buf,sizeof(buf));
if(ret == 0)
{
printf("client is been closed,close\n");
close(cfd);
exit(1);
}
else if(ret == -1)
{
perror("read error");
}
else
{
for(int i = 0;i<ret;i++)
{
buf[i] = toupper(buf[i]);
}
}
write(cfd, buf ,ret);
write(STDOUT_FILENO,buf,ret);
}
}
2、父进程结合信号量回收子进程
void catch_child(int signum)
{
if(signum == SIGCHLD)
{
int status;
pid_t id = waitpid(-1,status,WNOHANG);
if(WIFEXITED(status))
{
printf("Process ended, id = %d",id);
printf("child send num %d", WEXITSTATUS(status));
}
}
}
int main(int argc,char *argv[])
{
int lfd,cfd;
pid_t pid;
int ret;
struct sockaddr_in srv_addr,clt_addr;
char buf[BUFSIZ];
signal(SIGCHLD,catch_child);
socklen_t clt_addr_len;
三、服务端整体代码
#include <string.h>
#include <sys/wait.h>
#include "tcp_socket.h"
#define SRV_PORT 9527
void catch_child(int signum)
{
if(signum == SIGCHLD)
{
int status;
pid_t id = waitpid(-1,status,WNOHANG);
if(WIFEXITED(status))
{
printf("Process ended, id = %d",id);
printf("child send num %d", WEXITSTATUS(status));
}
}
}
int main(int argc,char *argv[])
{
int lfd,cfd;
pid_t pid;
int ret;
struct sockaddr_in srv_addr,clt_addr;
char buf[BUFSIZ];
signal(SIGCHLD,catch_child);
socklen_t clt_addr_len;
bzero(&srv_addr,sizeof(srv_addr));
srv_addr.sin_family = AF_INET;
srv_addr.sin_port = htons(SRV_PORT);
srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
lfd = Socket(AF_INET,SOCK_STREAM,0);
Bind(lfd,(struct sockaddr *)&srv_addr, sizeof(srv_addr));
clt_addr_len = sizeof(clt_addr);
Listen(lfd,128);
while(1)
{
cfd = Accept(lfd,(struct sockaddr*)&clt_addr,&clt_addr_len);
pid = fork();
if(pid < 0)
{
sys_err("fork error");
}
else if(pid == 0)
{
close(lfd);
break;
}
else
{
close(cfd);
}
}
if(pid == 0)
{
while(1)
{
ret = read(cfd,buf,sizeof(buf));
if(ret == 0)
{
printf("client is been closed,close\n");
close(cfd);
exit(1);
}
else if(ret == -1)
{
perror("read error");
}
else
{
for(int i = 0;i<ret;i++)
{
buf[i] = toupper(buf[i]);
}
}
write(cfd, buf ,ret);
write(STDOUT_FILENO,buf,ret);
}
}
return 0;
}
四、与客户端连接结果,客户端代码见 客户端简单实例 1、多个客户端均成功与服务器通信 2、子进程均被回收,未产生僵尸进程
|