网络编程 day 5

1、根据select TCP服务器流程图编写服务器

#include 

#define ERR_MSG(msg)  do{\
    fprintf(stderr, "__%d__:", __LINE__); \
    perror(msg);\
}while(0)

#define PORT 8888               //端口号,范围1024~49151
#define IP  "192.168.114.72"   //本机IP,ifconfig


int main(int argc, const char *argv[])
{
    //创建流式套接字 socket
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sfd < 0)
    {
        ERR_MSG("socket");
        return -1;
    }
    printf("socket create success sfd=%d\n", sfd);

    //允许端口快速的被复用
    int reuse = 1;
    if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
    {
        ERR_MSG("setsockopt");
        return -1;
    }
    printf("允许端口快速的被复用成功\n");


    //填充地址信息结构体给bind函数绑定,
    //真实的地址信息结构体根据地址族指定 AF_INET:man 7 ip
    struct sockaddr_in sin;
    sin.sin_family      = AF_INET;      //必须填AF_INET;
    sin.sin_port        = htons(PORT);  //端口号的网络字节序
    sin.sin_addr.s_addr = inet_addr(IP);//本机IP


    //绑定服务器的地址信息---> 必须绑定 bind
    if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
    {
        ERR_MSG("bind");
        return -1;
    }
    printf("bind success\n");


    //将套接字设置为被动监听状态 listen
    if(listen(sfd, 128) < 0)
    {
        ERR_MSG("listen");
        return -1;
    }
    printf("listen success\n");

	//创建一个读集合
	fd_set readfds,tempfds;

	//清空集合
	FD_ZERO(&readfds);

	//将需要监测的文件描述符添加到集合中
	FD_SET(0,&readfds);
	FD_SET(sfd,&readfds);

	int maxfd = sfd;    //存储最大的文件描述符

	int s_res = -1;
	ssize_t res = -1;
    char buf[128] = "";
	int newfd = -1;
	struct sockaddr_in cin;           //存储客户端的地址信息
	socklen_t addrlen = sizeof(cin);  //真实的地址信息结构体的大小

	while(1)
    {
       tempfds = readfds;
	   //执行IO多路复用函数
	   s_res = select(maxfd+1,&tempfds,NULL,NULL,NULL);

		//进行判断
		if(s_res < 0)
        {
            ERR_MSG("select");
            return -1;
        }
        else if(0 == s_res)
        {
            printf("time out\n");
			break;
		}

		printf("_%d_\n",__LINE__);



		//能运行到当前位置,则代表select函数解除阻塞
		//此时集合中会只剩下产生事件的文件描述符
		//0号准备就绪,则集中会只剩下0号
		//sfd准备就绪,则集合中会只剩下sfd
		//0和sfd均准备就绪,则集中会剩下0和sfd;
		//只需要判断集合中剩下哪个文件描述符,走对应处理函数即可
		

		if(FD_ISSET(0,&tempfds))
		{
			printf("触发键盘输入事件\n");
			bzero(buf,sizeof(buf));
			fgets(buf,sizeof(buf),stdin);
			buf[strlen(buf)-1] = 0;

			printf(":%s\n",buf);

		}
			//判断sfd是否在文件集合中
		if(FD_ISSET(sfd,&tempfds))
		{
			printf("触发客户端连接事件\n");
			newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen);
			if(newfd < 0)
			{
				ERR_MSG("newfd");
				return -1;
			}
			printf("[%s:%d]客户端连接成功 newfd=%d\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);
			
			FD_SET(newfd,&readfds);    //将newfd添加到集合中

			maxfd = maxfd>newfd ? maxfd:newfd; //更新maxfd
		}

	//除了0和sfd事件外,其余均为newfd对应的客户端交互事件
	//通过循环i,遍历所有newfd是否在tempfds中。
	

	for(int i=4; i<=maxfd; i++)
	{
		if(FD_ISSET(i,&tempfds))
		{
			printf("触发客户端交互事件\n");
			bzero(buf,sizeof(buf));
			//接收
			res = recv(i,buf,sizeof(buf),0);
			if(res < 0)
			{
				ERR_MSG("recv");
				return -1;

			}
			else if(0 == res)
			{
				printf("[%s;%d]客户端下线 newfd=%d\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),i);
				//关闭文件描述符
				close(i);
				//将文件描述符从集合中剔除
				FD_CLR(i,&readfds);
				continue;
			}

			printf("[%s:%d]newfd=%d ; %s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),i,buf);

			//发送
			strcat(buf,"*_*");
			if(send(i,buf,sizeof(buf),0) <0 )
			{
				ERR_MSG("send");
				return -1;
			}
			printf("send success\n");
			}
		}
	}
	//关闭文件名描述符
	if(close(sfd) < 0)
	{
		ERR_MSG("close");
		return -1;
	}

	return 0;
}


2、思维导图

网络编程 day 5_第1张图片

你可能感兴趣的:(网络,php,开发语言,数据挖掘)