1、正常的TCP下的C/S模型
2、父进程先注册信号捕捉函数(避免循环注册), 借助SIGCHLD信号回收子进程
3、accept()成功调用并返回才能fork()创建子进程
4、父进程关闭sfd(通信socket), 子进程关闭lfd(监听socket)
5、nc 127.0.0.1 9527多开客户端测试
#include "wrap.h"
#include <arpa/inet.h>
#define PORT 9527
#define MAXNUM 128
#include <ctype.h>
#include <signal.h>
#include <sys/wait.h>
// 信号捕捉函数
void func(int signo)
{
pid_t wpid;
printf("catch you SIGCHLD\n");
// 6、捕捉到信号--循环回收子进程(非阻塞)
while ((wpid = waitpid(-1, NULL, WNOHANG)) >= 0)
{
if (wpid > 0)
printf("waitpid %d\n", wpid);
}
return;
}
int main(int argc, char *argv[])
{
int lfd, sfd; // 2个套接字(监听 + 通信)
int n; // 读的字节数
int ret;
pid_t pid;
// 1、Socket()监听专用套接字
lfd = Socket(AF_INET, SOCK_STREAM, 0); // 采用ipv4 流式协议的典型协议TCP
// 2、Bind()绑定服务器自己的sockaddr地址结构(ip + 端口号)
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(PORT);
saddr.sin_addr.s_addr = htonl(INADDR_ANY); // INADDR_ANY获取本机可用的ip地址(本地字节序)
Bind(lfd, (struct sockaddr *)&saddr, sizeof(saddr));
// 3、设置监听上限
Listen(lfd, MAXNUM);
// 4、阻塞等待客户端建立连接--建立连接并新建1个通信专用socket
struct sockaddr_in caddr; // 用来接收客户端的地址结构
socklen_t caddrlen = sizeof(caddr);
// 6、父进程得回收子进程(wait和waitpid不能用, 借助信号捕捉)
sigset_t myset; // 1、定义自己的信号集
sigemptyset(&myset); // 2、清空信号集(不屏蔽任何信号)
sigprocmask(SIG_BLOCK, &myset, NULL); // 3、自定义的信号集短暂代替阻塞集(回调函数执行时)
struct sigaction myact;
myact.sa_handler = func;
myact.sa_flags = 0;
myact.sa_mask = myset;
ret = sigaction(SIGCHLD, &myact, NULL); // 5、注册信号捕捉函数---一旦收到信号, 内核会回调捕捉函数(自定义行为)
if (ret == -1)
sys_error("sigaction error");
while (1)
{
sfd = Accept(lfd, (struct sockaddr *)&caddr, &caddrlen); // 一直等待客户端连接
pid = fork(); // 创建子进程
if (pid == -1)
sys_error("fork error");
else if (pid > 0)
{
// 5、父进程并不需要通信
Close(sfd); // 关闭accept新产生的socket
// 注册信号捕捉1遍就够了, 不能放在循环里
continue;
}
else
{
// 5、子进程并不需要监听
Close(lfd); // 关闭监听专用的socket
break;
}
}
// 在外面写具体子进程xxx 逻辑更清楚
if (pid == 0)
{
// 打印与服务器建立连接的客户端的地址结构(网络字节序下的(二进制的整数))
char cip[50];
inet_ntop(AF_INET, (void *)&caddr.sin_addr.s_addr, cip, sizeof(cip));
printf("客户端ip地址: %s\n", cip);
printf("客户端端口号: %u\n", ntohs(caddr.sin_port));
// 5、读客户端发的数据
char buf[BUFSIZ]; // 接收读到的数据
while ((n = read(sfd, buf, BUFSIZ)) != 0)
{
// 打印到屏幕上
printf("客户端发来的数据: %s", buf);
// 转大写
for (int i = 0; i < n; ++i)
buf[i] = toupper(buf[i]);
// 6、写给客户端
ret = write(sfd, buf, n);
if (ret == -1)
sys_error("server write error");
}
// 7、读客户端的数据完毕--断开连接
Close(sfd);
}
return 0;
}