前言
select 函数是控制 文件描述符集合的位图
正文
fd_set 文件描述符集合 位图
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
nfds 代表三个集合中最大的文件描述符+1
readfds 读的集合 这三个集合都是传入传出
writefds 写的集合
exceptfds 异常的集合
timeout 超时时间
NULL 代表永久阻塞 -- 直到监控的事件发生
全填0 立即返回,不管是否有监控的事件产生
填具体的值,限时等待 -- 直到监控的事件发生或者时间到了返回
返回值
成功
返回 三个文件描述符中就绪事件的个数
失败 返回 -1,设置errno
文件描述符集合操作函数
将fd从set中清除--置0
void FD_CLR(int fd, fd_set *set);
判断fd是否在集合中
int FD_ISSET(int fd, fd_set *set);
§ 返回值
□ 成功 返回 1
□ 失败 返回 0
将fd添加到集合中 -- 置1
void FD_SET(int fd, fd_set *set);
将set集合清空
void FD_ZERO(fd_set *set);
一,TCP的三次回首
关闭状态 FIN_WAIT_1
二,端口复用技术
int getsockopt(int sockfd, int level, int optname,
void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,
const void *optval, socklen_t optlen);
三,IO复用的高并发服务器
/*************************************************************************
> File Name: server_select_sum.c
> Author: songli
> QQ: 2734030745
> Mail: 15850774503@163.com
> CSDN: http://my.csdn.net/Poisx
> github: https://github.com/chensongpoixs
> Created Time: Tue 31 Oct 2017 10:10:16 PM CST
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "wrap.h"
#include <sys/select.h>
#include <ctype.h>
#define SERV_PORT 8888
int main(int argc, char *argv[])
{
int listenfd, maxfd, sockfd;
struct sockaddr_in servaddr, cliaddr;
socklen_t len = sizeof(cliaddr);
char buf[256], strip[16];
int i, optval = 1;
int client[FD_SETSIZE];
fd_set rset, allset;
int nready = 0; // select
int maxindex = -1;
//1,创建socket
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
//2,绑定ip和port
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
//设置端口复用
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));
Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
//3,监听Listen
Listen(listenfd, 128);
//4,设置描述符集合
FD_ZERO(&rset);
FD_SET(listenfd, &rset);
//副本
maxfd = listenfd;
allset = rset;
for (i = 0; i < FD_SETSIZE; i++)
client[i] = -1;
while (1)
{
rset = allset;
//5,阻塞select事件
nready = select(maxfd + 1, &rset, NULL, NULL, NULL);
if (nready < 0)
perror("select error");
//5.1 判断是否client链接
if (FD_ISSET(listenfd, &rset))
{
int connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &len);
printf("client ip:%s, port:%d\n",
inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, strip, sizeof(strip)),
ntohs(cliaddr.sin_port));
for (i = 0; i < FD_SETSIZE; i++)
{
if (client[i] < 0)
{
client[i] = connfd; //记录client描述符
break;
}
}
if (i == FD_SETSIZE)
{
fputs("too manys clients\n", stderr);
exit(1);
}
printf("i:%d\n", i);
FD_SET(connfd, &allset); //client的描述符添加到集合中
if (i > maxindex)
maxindex = i;
if (maxfd < connfd )
maxfd = connfd; //最大描述符数量
printf("maxindex:%d\n", maxindex);
printf("maxfd:%d\n", maxfd);
printf("nready:%d\n", nready);
if (--nready == 0)
continue;
}
//5.2 read信息的发送
for (i = 0; i <= maxindex; i++)
{
printf("client[%d] = %d\n", i, client[i]);
if ((sockfd = client[i]) < 0)
continue;
if (FD_ISSET(sockfd, &rset)) //有client发送信息
{
int ret = Read(sockfd, buf, sizeof(buf));
if (ret < 0)
{
perror("Read error");
client[i] = -1;
Close(sockfd);
FD_CLR(sockfd, &allset); ///去除描述符
}
else if (ret == 0) //client 退出
{
client[i] = -1;
Close(sockfd);
FD_CLR(sockfd, &allset);
}
else
{
int j;
for (j = 0; j < ret; j++)
{
buf[j] = toupper(buf[j]);
}
Write(sockfd, buf, ret);
}
if (--nready == 0) //处理完成返回
break;
}
}
}
Close(listenfd);
return 0;
}