首页 > 其他 > 详细

select_socket

时间:2020-09-07 22:54:01      阅读:56      评论:0      收藏:0      [点我收藏+]
/* server.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "wrap.h"

#define MAXLINE 80
#define SERV_PORT 8000

int main(int argc, char **argv)
{
	int i, maxi, maxfd, listenfd, connfd, sockfd;	//文件描述符listenfd是监听socket的fd,connfd是连接socket的fd
	
	//操作系统通过宏FD_SETSIZE来声明在一个进程中select所能操作的文件描述符的最大数目。一般默认1024
	int nready, client[FD_SETSIZE]; 
	
	ssize_t n;		//Read返回数
	
	//select()机制中提供一种fd_set的数据结构,它实际上是long类型的数组
	//每一个数组元素都能与一打开的文件句柄(不管是socket句柄,还是其他文件或命名管道或设备句柄)建立联系,
	//当调用select()时,由内核根据IO状态修改fd_set的内容,由此来通知执行了select()的进程哪一socket或文件发生了可读或可写事件。
	fd_set rset, allset;//rset是用户态临时的,allset是要传给内核态的总的
	
	char buf[MAXLINE];
	
	//<netinet/in.h>中有如下两个宏定义:INET_ADDRSTRLEN是使用10进制+句点表示时,所占用的char * 数组的长度
	//#define INET_ADDRSTRLEN 16 /* for IPv4 dotted-decimal */
	char str[INET_ADDRSTRLEN];
	
	socklen_t cliaddr_len;					//cliaddr_len = sizeof(sockaddr_in),用于Accept
	struct sockaddr_in	cliaddr, servaddr;	//cliaddr用于Accept,servaddr用于Bind

	listenfd = Socket(AF_INET, SOCK_STREAM, 0);  //监听文件,ipv——tcp形式
	/********初始化servaddr**********/
	bzero(&servaddr, sizeof(servaddr));          //服务器地址置零
	servaddr.sin_family      = AF_INET;          //赋值ipv4
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//网络地址小转大,长整型
	servaddr.sin_port        = htons(SERV_PORT); //端口号小转大,短整型
	/********************************/
	Bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));//绑定监听地址于服务器地址

	Listen(listenfd, 20);//监听文件  运行最大20个客户端处于待连接状态
	
	maxfd = listenfd;		/* initialize 定义最大文件描述符,select的参数n用*/
	maxi = -1;				/* index into client[] array */
	
	/*初始化用户态socket列表*/
	for (i = 0; i < FD_SETSIZE; i++)
	{
		client[i] = -1;	/* -1 indicates available entry *///用户值全置零
	}
		
	FD_ZERO(&allset);//清除全部allset
	FD_SET(listenfd, &allset);//将listenfd置位为allset中的值

	for ( ; ; ) //开始循环
	{
		rset = allset;	/* structure assignment  资源分配 */
		
		//int select(int n, fd_set *readfds, fd_set * writefds, fd_set * exceptfds, struct timeval * timeout);
		//n代表最大的文件描述符加1,因为描述符是从0开始的,因此如果最大的描述符为n的话,那么其实是有n+1个描述符。
		nready = select(maxfd+1, &rset, NULL, NULL, NULL);//准备接收客户端请求;只读方式
		if (nready < 0)
		{
			perr_exit("select error");//打印错误			
		}
		
		/*	FD_ISSET(int fd,fd_set *set),一般与select()系统调用一起使用。判断描述符fd是否在给定的描述符集fdset中*/
		if (FD_ISSET(listenfd, &rset)) //如果FD值为真;即读取到文件描述符读提醒,可执行accpet
		{ 	
			/* new client connection 创建新链接 */
			cliaddr_len = sizeof(cliaddr);//用户地址长度赋值
			connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);//链接文件

			printf("received from %s at PORT %d\n",
			       inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
			       ntohs(cliaddr.sin_port));//打印收到的客户端的详细信息:地址,端口号
		/*********************************************************************************************/
		/******   接收到一个client连接之后,注册到自己的用户空间client[FD_SETSIZE]队列中  ************/
		/******                也注册到内核空间&allset用于select监听                     *************/
		/*********************************************************************************************/
			//遍历客户端,先入先写队列,保留自己的连接socketfd
			for (i = 0; i < FD_SETSIZE; i++)
			{
				if (client[i] < 0) 
				{
					client[i] = connfd; /* save descriptor  保存描述符并退出*/
					break;				//只要写成功一个就退出
				}
			}
			
			//如果i==客户端数目达到最大值,打印输出:太多客户端并退出
			if (i == FD_SETSIZE)		
			{
				fputs("too many clients\n", stderr);//
				exit(1);//退出
			}
			
			/*FD_SET(int fd,fd_set*set); 用来设置描述词组set中相关fd的位*/
			FD_SET(connfd, &allset);	/* 注册到内核空间&allset用于select监听  */
			
			if (connfd > maxfd)
			{
				maxfd = connfd; /* 更新最大最大描述符,并再下一轮select时赋值给select */				
			}

			if (i > maxi)
			{	/*标记用户态client[FD_SETSIZE]中有效的sonnfd最大角标*/
				maxi = i;	/* max index in 用户态client[FD_SETSIZE] array */							
			}

			/*nready是select返回的文件描述符状态已改变的个数*/
			/*如果--nready == 0则表示只有一个监听scoket,如果不为0,则表示还有其他已经连接的client,socket响应*/
			if (--nready == 0)		
			{
				continue;	/* no more readable descriptors */
			}
		}
		
		/*还有其他已经连接的client,socket响应继续处理*/
		for (i = 0; i <= maxi; i++) 	//只访问前面有效元素connfd
		{	/* check all clients for data */
			if ( (sockfd = client[i]) < 0)
			{
				continue;	//这个失败就下一个
			}
			if (FD_ISSET(sockfd, &rset)) //确认sockfd = client[i]在给定的描述符集rset中
			{
				if ( (n = Read(sockfd, buf, MAXLINE)) == 0)//if读取信息为空则清理socket
				{
					/* connection closed by client */
					Close(sockfd);//关闭
					FD_CLR(sockfd, &allset);//	FD_CLR(int fd,fd_set* set); 用来清除描述词组allset中相关sockfd的位
					client[i] = -1;//空出位置,置为-1
				} else
				{
					int j;
					for (j = 0; j < n; j++)
					{
						buf[j] = toupper(buf[j]);	//将buf中的字符小写转大写						
					}
					Write(sockfd, buf, n);//写读取到的信息
				}
				
				/*挨个执行,直到nready为0,也就从本次select回来的都处理完毕*/
				if (--nready == 0)
				{
					break;	/* no more readable descriptors */										
				}
			}
		}
	}
}


select_socket

原文:https://www.cnblogs.com/IvanLxy/p/13629397.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!