文章目录
思路
每当一个客户端连接服务器后,创建一个子进程负责与该客户端通信,客户端断开连接之后,服务器回收子进程资源。
问题
信号的注册,以及回调函数的编写:
//子进程回收回调函数
void recvChild(int arg)
{
while(1)
{
int ret = waitpid(-1, NULL, WNOHANG);
if(ret > 0)
{
printf("recv child, the num is:%d\n", ret);
}
else if(ret == 0)
{
//还有子进程
}
else if(ret == -1)
{
//没有子进程了
break;
}
}
}
//注册信号,解决子进程的回收问题
struct sigaction act;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
act.sa_handler = recvChild;
sigaction(SIGCHLD, &act, NULL);
********//
//接收数据没有字符结束符,无法判断数据结束,
//使用printf%s出现问题:可以将末尾增加字符结束符/0,也可以使用数据初始化(浪费资源)
//memset(recv, 0, 1024);
int len = read(cfd, recv, 1024);
recv[len] = 0;
多进程并发回环服务器代码
#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <wait.h>
#include <errno.h>
void recvChild(int arg)
{
while(1)
{
int ret = waitpid(-1, NULL, WNOHANG);
if(ret > 0)
{
printf("recv child, the num is:%d\n", ret);
}
else if(ret == 0)
{
//还有子进程
}
else if(ret == -1)
{
//没有子进程了
break;
}
}
}
int main()
{
//注册信号,解决子进程的回收问题
struct sigaction act;
act.sa_flags = 0;
sigemptyset(&act.sa_mask);
act.sa_handler = recvChild;
sigaction(SIGCHLD, &act, NULL);
//socket
int lfd = socket(PF_INET, SOCK_STREAM, 0);
if(lfd == -1)
{
perror("socket");
exit(-1);
}
//bind
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
inet_pton(AF_INET, "192.168.1.108", &saddr.sin_addr.s_addr);
//saddr.sin_addr.s_addr = INADDR_ANY;
saddr.sin_port = htons(9999);
int ret = bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));
if(ret == -1)
{
perror("bind");
exit(-1);
}
//listen
ret = listen(lfd, 2);
if(ret == -1)
{
perror("listen");
exit(-1);
}
int prosess_num = 0;
while(1)
{
//accept
struct sockaddr_in caddr;
socklen_t len = sizeof(caddr);
int cfd = accept(lfd, (struct sockaddr *)&caddr, &len);
if(cfd == -1)
{
if(errno == EINTR) continue;
perror("accept");
exit(-1);
}
//child
prosess_num++;
pid_t fd = fork();
if(fd == -1)
{
perror("fork");
exit(-1);
}
if(fd == 0)
{
char cip[16];
printf("the process %d link success!\n", prosess_num);
inet_ntop(AF_INET, &caddr.sin_addr, cip, sizeof(cip));
printf("client IP:%s, Port:%d\n\n", cip, ntohs(caddr.sin_port));
char recv[1025];
while(1)
{
********//
//接收数据没有字符结束符,无法判断数据结束,
//使用printf%s出现问题:可以将末尾增加字符结束符/0,也可以使用数据初始化(浪费资源)
//memset(recv, 0, 1024);
int len = read(cfd, recv, 1024);
recv[len] = 0;
if(len == -1)
{
perror("read");
exit(-1);
}
else if(len > 0)
{
if(strcmp(recv, "break\r\n") == 0) break;
write(cfd, recv, len+1);
printf("IP:%s Port:%d: %s", cip, ntohs(caddr.sin_port), recv);
}
else
{
printf("client is closed...\n");
break;
}
}
printf("the process %d, IP:%s, port:%d, will close!\n", prosess_num, cip, ntohs(caddr.sin_port));
close(cfd);
exit(0);
}
}
close(lfd);
return 0;
}
客户端代码
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>
#include <fcntl.h>
int main()
{
int lfd = socket(PF_INET, SOCK_STREAM, 0);
if(lfd == -1)
{
perror("socket");
exit(-1);
}
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
int len = sizeof(saddr);
inet_pton(AF_INET, "192.168.1.108", &saddr.sin_addr.s_addr);
saddr.sin_port = htons(9999);
int ret = connect(lfd, (struct sockaddr *)&saddr, sizeof(saddr));
if(ret == -1)
{
perror("connect");
exit(-1);
}
printf("client link success!\n");
//由于读操作会阻塞,客户端需要先发送数据,若先读取数据,一个进程就会阻塞住,一个进程就要先发数据,如果一次数据没有接收到,则会阻塞住。
//可以采用两个进程,发、收互不影响
pid_t pid = fork();
if(pid==0)
{
char rbuf[1024];
while(1)
{
//memset(rbuf, 0, 1024);
int lent = read(lfd, rbuf, 1024);
if(lent > 0)
{
printf("send: %s", rbuf);
}
else if(lent == -1) perror("read");
}
}
else if(pid > 0)
{
int i = 0;
char sbuf[1024];
while(1)
{
i++;
if(i > 255) i = 0;
sprintf(sbuf, "the num is %d\n", i);
write(lfd, sbuf,strlen(sbuf));
sleep(1);
}
}
close(lfd);
return 0;
}