IO多路复用

基本思想

先构造一张有关描述符的表,然后调用一个函数。当这些文件描述符中的一个或多个已准备好进行I/O时函数才返回。

函数返回时告诉进程那个描述符已就绪,可以进行I/O操作。






 #include <sys/time.h>
 #include <sys/types.h>
 #include <unistd.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
功能:
       实现IO的多路复用
参数:
  nfds:关注的最大的文件描述符+1
  readfds:关注的读表
  writefds:关注的写表
  exceptfds:关注的异常表
 timeout:超时的设置
 NULL:一直阻塞,直到有文件描述符就绪或出错
时间值
 为0:仅仅检测文件描述符集的状态,然后立即返回
时间值
 不为0:在指定时间内,如果没有事件发生,则超时返回。
 
返回值:
  准备好的文件描述符的个数
  -1  失败:
  0:超时检测时间到
注意:
 select返回后  ,关注列表中只存在准备好的文件描述符


 void FD_CLR(int fd, fd_set *set);
功能:
  将fd从关注列表中清除
参数:
   fd:文件描述符
  set:关注列表的指针
返回值:无

 int  FD_ISSET(int fd, fd_set *set);//判断fd是否咋关注列表中  是--》1   不是---》0
void FD_SET(int fd, fd_set *set);//将fd放入关注列表中
void FD_ZERO(fd_set *set);//清空关注列表

net.h

#ifndef __NET_H__
#define __NET_H__

#include <stdio.h>
#include <arpa/inet.h> //inet_addr htons
#include <sys/types.h>
#include <sys/socket.h>  //socket bind listen accept connect
#include <netinet/in.h>  //sockaddr_in
#include <stdlib.h> //exit
#include <unistd.h> //close
#include <string.h> 
#include <strings.h> 
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>

#define errlog(errmsg) do{\
							perror(errmsg);\
							printf("%s -- %s -- %d\n", __FILE__, __func__, __LINE__);\
							exit(1);\
						 }while(0)

#define N 128

#endif    /* __NET_H__ */


server.c

#include "net.h"

int main(int argc, const char *argv[])
{
	int sockfd, acceptfd;
	struct sockaddr_in serveraddr, clientaddr;
	socklen_t addrlen = sizeof(serveraddr);
	char buf[N] = { 0 };

	if(argc < 3)
	{
		fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);
		exit(1);
	}

	//第一步:创建套接字
	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		errlog("fail to socket");
	}

	//第二步:填充服务器网络信息结构体
	//inet_addr:将点分十进制ip地址转化为网络字节序的整型数据
	//htons:将主机字节序转化为网络字节序
	//atoi:将数字型字符串转化为整型数据
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
	serveraddr.sin_port = htons(atoi(argv[2]));

	//第三步:将套接字与网络信息结构体绑定
	if(bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0)
	{
		errlog("fail to bind");
	}

	//第四步:将套接字设置为被动监听状态
	if(listen(sockfd, 5) < 0)
	{
		errlog("fail to listen");
	}

	//使用select函数实现io多路复用
	
	//注意:当select函数返回之后,会移除集合里面当前文件描述符以外其他所有的文件描述符	
	fd_set readfds;
	int maxfd;

	//第一步:将集合清空
	FD_ZERO(&readfds);

	maxfd = sockfd;

	while(1)
	{
		//第二步:将需要的文件描述符添加到集合里面
		// 0 sockfd
		FD_SET(0, &readfds);
		FD_SET(sockfd, &readfds);

		//第三步:调用select函数阻塞等待文件描述符准备就绪
		if(select(maxfd + 1, &readfds, NULL, NULL, NULL) < 0)
		{
			errlog("fail to select");
		}

		//根据性质判断到底是哪个文件描述符准备就绪,并执行相应的io操作
		
		if(FD_ISSET(sockfd, &readfds))
		{
			if((acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen)) < 0)
			{
				errlog("fail to accept");
			}

			//打印获取到的客户端的信息
			printf("ip: %s, port: %d\n", inet_ntoa(clientaddr.sin_addr), ntohs(clientaddr.sin_port));
            
            close(acceptfd);
		}
        
		if(FD_ISSET(0, &readfds))
		{
			if(NULL != fgets(buf, N, stdin))
			{
                buf[strlen(buf) - 1] = '\0';

			    printf("buf : %s\n", buf);
			}
    	}
 
        if(0 == strncasecmp(buf, "quit", 4))
             {
                 printf("server is quit!\n");
                 break;
             }
	}

	close(sockfd);

	return 0;
}


client.c

#include "net.h"

int main(int argc, const char *argv[])
{
	int sockfd;
	struct sockaddr_in serveraddr;
	socklen_t addrlen = sizeof(serveraddr);

	if(argc < 3)
	{
		fprintf(stderr, "Usage: %s <ip> <port>\n", argv[0]);
		exit(1);
	}

	//第一步:创建套接字
	if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		errlog("fail to socket");
	}

	//第二步:填充服务器网络信息结构体
	//inet_addr:将点分十进制ip地址转化为网络字节序的整型数据
	//htons:将主机字节序转化为网络字节序
	//atoi:将数字型字符串转化为整型数据
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr(argv[1]);
	serveraddr.sin_port = htons(atoi(argv[2]));

	//第三步:发送客户端的连接请求
	if(connect(sockfd, (struct sockaddr *)&serveraddr, addrlen) < 0)
	{
		errlog("fail to connect");
	}
	
	close(sockfd);
	
	return 0;
}


测试结果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值