SIGCHLD 只要子进程状态发生变化就会产生,不仅仅是子进程死亡,还有子进程暂停和继续
SIGCHLD 默认处理动作是忽略
用信号回收子进程时 要使用while循环回收多次 。原因是信号不支持排队,多个子进程结束只有一个信号。
程序流程
Socket()创建套接字
Bind()绑定IP和端口号
Listen()设置最大监听数
while(1){
Accept()阻塞等待客户端接入
创建子线程,给子线程传入 包含cfd和clt_addr的结构体
子线程分离
子线程:
}
Close(lfd);
在主进程中定义一个结构体数组,每创建一个子线程存放一个结构体到数组中
struct s_info{
struct sockaddr_in clt_addr;
int cfd;
};
因为以上2MSL的存在,如果服务器处于TIME_WAIT状态,要等待2MSL时间才能重启。
不等待2MSL可以使用端口复用。
端口复用在客户端和服务器端都可以使用
int opt = 1; //设置端口可以复用
setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,(void*)&opt,sizeof(opt));
使用select帮助监听文件描述符,不需要再创建进程或线程来完成操作。
#include
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
nfds: 监听的所有的文件描述符中,最大的文件描述+1
readfds: 读 文件描述符监听集合 传入、传出参数
writefds 写 文件描述符监听集合 传入、传出参数 NULL
exceptfds 异常 文件描述符监听集合 传入、传出参数 NULL
timeout: 设置超时时长。
> 0: 设置监听时长
NULL:阻塞监听
0: 非阻塞监听, 需要轮询。
返回值:
> 0: 所有监听集合(3个)中, 满足对应事件的文件描述符的【总】个数。
0 : 没有满足监听条件的文件描述符。
-1: errno
// 清空一个文件描述符集合
void FD_ZERO(fd_set *set);
// 例:
fd_set read_set;
FD_ZERO(&read_set);
// 将待监听的文件描述符, 添加到监听集合中。
void FD_SET(int fd, fd_set *set);
// 例:
FD_SET(3, &read_set);
FD_SET(7, &read_set);
FD_SET(5, &read_set);
// 将一个文件描述符从监听集合中,移除
void FD_CLR(int fd, fd_set *set);
// 例:
FD_CLR(7, &read_set);
// 判断一个文件描述符,是否在 监听集合中
int FD_ISSET(int fd, fd_set *set);
返回值:在:1; 不在:0
// 例:
FD_ISSET(4, &read_set); ----> 0
代码实现:
#include
#include
#include
#include
#include
#include
#include "wrap.h"
#define SERV_PORT 6666
int main(int argc, char *argv[])
{
int i, j, n, nready;
int maxfd = 0;
int listenfd, connfd;
char buf[BUFSIZ]; /* #define INET_ADDRSTRLEN 16 */
struct sockaddr_in clie_addr, serv_addr;
socklen_t clie_addr_len;
fd_set rset, allset; /* rset 读事件文件描述符集合 allset用来暂存 */
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bzero(&serv_addr, sizeof(serv_addr));
serv_addr.sin_family= AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port= htons(SERV_PORT);
Bind(listenfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
Listen(listenfd, 128);
maxfd = listenfd;
FD_ZERO(&allset);
FD_SET(listenfd, &allset); /* 构造select监控文件描述符集 */
while (1) {
rset = allset; /* 每次循环时都从新设置select监控信号集 */
nready = select(maxfd+1, &rset, NULL, NULL, NULL);
if (nready < 0)
perr_exit("select error");
if (FD_ISSET(listenfd, &rset)) { /* 说明有新的客户端链接请求 */
clie_addr_len = sizeof(clie_addr);
connfd = Accept(listenfd, (struct sockaddr *)&clie_addr, &clie_addr_len); /* Accept 不会阻塞 */
FD_SET(connfd, &allset); /* 向监控文件描述符集合allset添加新的文件描述符connfd */
if (maxfd < connfd)
maxfd = connfd;
if (0 == --nready)
continue;
}
for (i = listenfd+1; i <= maxfd; i++) { /* 检测哪个clients 有数据就绪 */
if (FD_ISSET(i, &rset)) {
if ((n = Read(i, buf, sizeof(buf))) == 0) { /* 当client关闭链接时,服务器端也关闭对应链接 */
Close(i);
FD_CLR(i, &allset); /* 解除select对此文件描述符的监控 */
} else if (n > 0) {
for (j = 0; j < n; j++)
buf[j] = toupper(buf[j]);
Write(i, buf, n);
}
}
}
}
Close(listenfd);
return 0;
}