网络编程之IO模型+多路复用+设置套接字属性

一、IO模型

在网络编程中有时我们需要多用户连接,因此使用IO模型设置非阻塞IO,就很容易解决这个问题

一、IO模型概念

1、阻塞IO          ->      一直等待数据的到达

2、非阻塞IO      ->     询问数据是否到达,如果没有数据,则不会阻塞,会马上退出

 

二、设置非阻塞IO的步骤

1、 建立一个文件描述符  -> 默认都是阻塞!
2、 设置非阻塞属性给文件描述符  -> 文件描述符就是非阻塞!
3、 再调用accept()/read()/recv()/recvfrom()  -> 非阻塞读取。

 

三、设置非阻塞IO

 

接口

#include <unistd.h>

#include <fcntl.h>

 

       int fcntl(int fd, int cmd, ... /* arg */ );

参数

 fd       :需要设置的文件描述符

 cmd   :

     

       F_GETFL (void)       ->   获取该文件描述符的属性 ->  不需要添加(arg)

 

       F_SETFL (int)         ->    设置该文件描述符的属性 -> 再添加一个属性(arg)

              Set  the  file status flags to the value specified by arg.  File

              access mode (O_RDONLY, O_WRONLY, O_RDWR) and file creation flags

              (i.e.,  O_CREAT,  O_EXCL, O_NOCTTY, O_TRUNC) in arg are ignored.

              On Linux this command can change  only  the  O_APPEND,  O_ASYNC,

              O_DIRECT,  O_NOATIME,  and O_NONBLOCK flags.  It is not possible

              to change the O_DSYNC and O_SYNC flags; see BUGS, below.

   

O_NONBLOCK:代表非阻塞的属性

返回

    F_GETFL    成功  文件描述符的属性

   F_SETFL      成功  0

   失败         -1

Example:

#include "head.h"

int main()

{

    //1.创建一个TCP协议的未连接套接字
    int sockfd = socket(AF_INET,SOCK_STREAM,0);

    //2.获取之前的属性
    int state = fcntl(sockfd,F_GETFL);

    //3.设置新属性
    state = state|O_NONBLOCK
    int ret = fcntl(sockfd,F_SETFL,state);
    if(ret == -1)

    {
        printf("set state error!\n");
    }
    return 0;

}

二、多路复用

1、多路复用的概念:

先将需要进行监听的文件描述符加入一个集合中,然后在规定的时间或者在无限的时间等待并且去监听这个集合中的文件描述符。如果在规定的时间内,集合的某个文件描述符有数据到达,则其他没有数据的文件描述符就会剔除到集合之外。我们用户需要监听集合中有没有文件描述符即可。

2、如何实现多路复用

监听    ->       文件描述符的集合     ->     是否加入

用到的几个接口:

 接口

#include <sys/select.h>

#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);

参数

1、nfds        添加到集合中的最大文件描述符+1

2、readfds    监测可读的文件描述符的集合

3、writefds    监测可写的文件描述符的集合   (NULL)

4、exceptfds    监测发生异常的文件描述符的集合(NULL)

5、timeou    监测超时时间

 

     5.1、NULL                         永久等待

     5.2、struct     timeval        设置了超时时间,超过这个时间集

返回

在socket集合中准备好的socket个数

2、 将某个文件描述符从集合里清除

函数: 

  void FD_CLR(int fd, fd_set *set);

参数: 

  1、fd         要清除的文件描述符

  2、set        要从哪个集合清除文件描述符

3、 将某个文件描述符添加到集合里

函数: 

  void FD_SET(int fd, fd_set *set);

参数:  

 1、fd        要添加的文件描述符

 2、set        要将文件描述符添加到哪个集   

4、 清空集合里的文件描述符

函数:  

 void FD_ZERO(fd_set *set);

参数:   

1、set        要将哪个集合里的文件描述符清空

5、 检测某个文件描述符是否在集合里

函数:  

int  FD_ISSET(int fd, fd_set *set);

参数:   

1、fd        要检测的文件描述符 

2、set        要检测的集合

返回值:

1、返回1:    要检测的文件描述符在集合里

2、返回0:    要检测的文件描述符不在集合里

设置超时等待

struct timeval {

               long    tv_sec;         /* seconds  秒*/

               long    tv_usec;        /* microseconds  微妙*/

 };

struct timeval v;  //每次都需要重新设置时间
v.tv_sec = 10;
v.tv_usec = 0;
select(maxfd+1,&rset,NULL,NULL,&v);  //超时则都踢出去

下面这个例子是通过多路复用来实现键盘和触摸屏的同时使用输入功能

/*
	功能: 监听哪个文件描述符有数据
*/

int fd_select(int fd1,int fd2)
{
	int maxfd = fd1 > fd2? fd1 : fd2;
	fd_set rset;
	
	FD_ZERO(&rset);
	FD_SET(fd2,&rset);
	FD_SET(fd1,&rset);
	
	//无限等待
	select(maxfd+1,&rset, NULL,NULL,NULL);
	//判断STDIN_FILENO是否在,在则发消息
	if(FD_ISSET(fd1,&rset))
	{
		return 1;
	}
	if(FD_ISSET(fd2,&rset))   //如果触摸屏在
	{
		return 2;
	}
}

//将键盘的文件描述符和触摸屏的文件描述符监听起来,你就可以去实现两个同时做输入工作了
fd_select(STDIN_FILENO,fd_event);

三、设置套接字的属性

一、如果不设置超时属性,那么套接字本身是无限等待数据的到达

Example:

 int connfd = accept();

 recv() ---->  正常时一直阻塞等待

 

二、设置超时属性

接口

#include <sys/types.h>          /* See NOTES */

#include <sys/socket.h>

 

int setsockopt(int sockfd, int level, int optname,

                                  const void *optval, socklen_t optlen);

参数

 sockfd       套接字描述符

 level          优先级

                   SOL_SOCKET     套接字

                   IPPROTO_IP      IP优先级

                   IPPRO_TCP        TCP优先级

 

 optname    选项名字(下面可以查询)

 optval        使能为1,不使能为0

 optlen        值类型大小

返回

成功    0

失败   -1

实现UDP广播

//server.c

#include "head.h"
int main(int argc, char **argv)
{
	//串口号
	int port = htons(atoi(argv[1]));
	//1、申请套接字
	int sockfd = socket(AF_INET,SOCK_DGRAM,0);
	
	//设置广播属性
	int on = 1;
	setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on));
	
	//2、绑定套接字
	//设置端口,自己IP
	struct sockaddr_in svraddr;
	socklen_t len = sizeof(struct sockaddr);
	bzero(&svraddr,len);
	
	svraddr.sin_family = AF_INET;
	svraddr.sin_port = port;
	svraddr.sin_addr.s_addr = htonl(INADDR_ANY);
	bind(sockfd,(struct sockaddr*)&svraddr,len);
	
	//接收信息
	struct sockaddr_in rcvaddr;
	bzero(&rcvaddr,sizeof(rcvaddr));
	char buf[50];
	while(1)
	{
		bzero(buf,50);
		recvfrom(sockfd,buf,50,0,(struct sockaddr*)&rcvaddr,&len);
		printf("from %s client: %s",(char*)inet_ntoa(rcvaddr.sin_addr),buf);
		if(strncmp(buf,"quit",4) == 0)
			break;
	}
	
	close(sockfd);
	return 0;
}
//client.c

#include "head.h"
int main(int argc, char **argv)
{
	//串口号
	int port = htons(atoi(argv[1]));
	//1、申请套接字
	int sockfd = socket(AF_INET,SOCK_DGRAM,0);
	
	//接收信息
	struct sockaddr_in sndaddr;
	socklen_t sndlen = sizeof(sndaddr);
	bzero(&sndaddr,sndlen);
	
	sndaddr.sin_family = AF_INET;
	sndaddr.sin_port = port;
	inet_pton(AF_INET,argv[2],&sndaddr.sin_addr);
	
	char buf[50];
	while(1)
	{
		bzero(buf,50);
		fgets(buf,50,stdin);
		sendto(sockfd,buf,50,0,(struct sockaddr*)&sndaddr,sndlen);
		if(strncmp(buf,"quit",4) == 0)
			break;
	}
	
	close(sockfd);
	
	return 0;

}

optname:

===========================SOL_SOCKET=================================

optname选项名字                                                                                      optlen的大小

SO_BROADCAST        允许发送广播数据            int

SO_DEBUG             允许调试                int

SO_DONTROUTE      不查找路由               int

SO_ERROR           获得套接字错误             int

SO_KEEPALIVE       保持连接                int

SO_LINGER          延迟关闭连接              struct linger

SO_OOBINLINE       带外数据放入正常数据流         int

SO_RCVBUF         接收缓冲区大小             int

SO_SNDBUF         发送缓冲区大小             int

SO_RCVLOWAT       接收缓冲区下限             int

SO_SNDLOWAT       发送缓冲区下限             int

SO_RCVTIMEO       接收超时                struct timeval

SO_SNDTIMEO       发送超时                   struct timeval

SO_REUSEADDR      允许重用本地地址和端口          int

SO_TYPE           获得套接字类型             int

SO_BSDCOMPAT     与BSD系统兼容              int

 

=========================IPPROTO_IP====================================

IP_HDRINCL       在数据包中包含IP首部          int

IP_OPTINOS       IP首部选项               int

IP_TOS         服务类型

IP_TTL         生存时间                int

IP_ADD_MEMBERSHIP       加入组播                             struct ip_mreq

 

=========================IPPRO_TCP=====================================

TCP_MAXSEG       TCP最大数据段的大小           int

TCP_NODELAY       不使用Nagle算法             int

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值