阻塞式/非阻塞式IO

阻塞式/非阻塞式IO

【知识点】
非阻塞式IO 的两种设置方法
(1) 函数fcntl( ),设置 O_NONBLOCK 选项
int flag=fcntl(sockfd,F_GETFL,0);检查文件标志位
fcntl(sockfd,F_SETFL,flag|O_NONBLOCK);设置文件标志位
(2) 函数ioctl( ),设置FIONBIO 选项
int nIO=1;设置非阻塞IO
ioctl(sockfd,FIONBIO,&nIO);

完成y=a+b
a 由客户端从标准输入获得,消耗时间有用户自行掌握。
b 由服务端随机生成,消耗时间t 由服务端的随机确定(不超过1 分钟)。
要求:
(1) 客户端使用非阻塞式IO 进行接收数据。
(2) 客户端向服务器发出请求数据b
(3) 客户端从标准输入获取一个整型
(4) 客户端从服务端获得随机数。
(5) 客户端计算并显示计算结果。
(6) 服务端接到请求后,产生随机消耗时间t。
(7) 服务端sleep(t)后返回一个随机数给客户端。
(8) 可参考以下流程图进行实现。

主要代码:

客户端:(nonbcli.c)
#include	"my.h"  //调试代码中的头文件,包含许多函数对出错的处理
#define max(a,b) a>b?a:b
int a,b;
int flag;
void str_cli(FILE *fp, int sockfd)
{
	Write(sockfd,"fornumber",10);
	flag=fcntl(sockfd,F_GETFL,0);	
	/* 设置非阻塞标志 */
	fcntl( sockfd,F_SETFL,flag | O_NONBLOCK );
	int			maxfdp1, stdineof,sockread;
	fd_set		rset;
	char		buf[MAXLINE];
	int		n;
     
	sockread=0;
	stdineof = 0;
	FD_ZERO(&rset);
	for ( ; ; ) {
		if (stdineof == 0)
		FD_SET(fileno(fp), &rset);
	    if(sockread==0)
		FD_SET(sockfd, &rset);
		maxfdp1 = max(fileno(fp), sockfd) + 1;
		Select(maxfdp1, &rset, NULL, NULL, NULL);

		if (FD_ISSET(sockfd, &rset)) {	/* socket is readable */
			if ( (n = Read(sockfd, &b, sizeof(b))) < 0) {
				if (errno != EWOULDBLOCK)
					printf("read error(socket)\n");
			}
			sockread=1;
			if (stdineof == 1)
					break;		/* normal termination */
			
		}

		if (FD_ISSET(fileno(fp), &rset)) {  /* input is readable */
			if ( (n = Read(fileno(fp), buf, MAXLINE)) < 0) {
				if (errno != EWOULDBLOCK)
					printf("read error(fp)\n");
			}
			FD_CLR(fileno(fp), &rset);
			stdineof = 1;
			buf[n]='\0';
			a=atoi(buf);
			printf("read from stdin:%d\n",a);
			if(sockread==1)break;
		}
	}
}
int main(int argc, char **argv)
{
	int					sockfd;
	struct sockaddr_in	servaddr;
	
	if (argc != 2)
		err_quit("usage: tcpcli <IPaddress>");

	sockfd = Socket(AF_INET, SOCK_STREAM, 0);

	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(8000);
	if((inet_pton(AF_INET, argv[1], &servaddr.sin_addr))<0)
		perror("inet_pton");
    flag=fcntl(fileno(stdin),F_GETFL,0);
    fcntl(fileno(stdin),F_SETFL,flag|O_NONBLOCK);
	
	Connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
    
	str_cli(stdin, sockfd);		/* do it all */

	printf("a+b = %d+%d = %d\n",a,b,a+b);
	exit(0);
}
服务器端:(nonbserv.v)
#include <stdio.h>
#include <stdlib.h> 
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>/*for close*/
#include <fcntl.h>  
#include <sys/wait.h>
#include <netdb.h>
#include <string.h> /*for memset*/
#include <signal.h> 
#include <errno.h>
#include <time.h>
int main(int argc,char **argv){
	int n,b,t,listenfd,connfd;  
	struct sockaddr_in my_addr; 
	struct sockaddr_in remote_addr; 
    socklen_t len = sizeof(remote_addr); 
	char buf[1024];  
	memset(&my_addr,0,sizeof(my_addr)); 
	my_addr.sin_family=AF_INET;
	my_addr.sin_addr.s_addr=INADDR_ANY;
	my_addr.sin_port=htons(8000); 
	
	if((listenfd=socket(PF_INET,SOCK_STREAM,0))<0){  
		perror("socket");
		return 1;
	}
	int on = 1; 
    if((setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0)  {  
	perror("setsockopt failed");
       exit(1);  
    } 
	if (bind(listenfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))<0){
		perror("bind");
		return 1;
	}
	listen(listenfd,5);
	for(;;){
		printf("waiting...\n");
        connfd = accept(listenfd, (struct sockaddr *)&remote_addr,&len);
		printf("new client come.\n");
        if(connfd<0){
          perror("accept error");
		}
		n = recv(connfd, buf, sizeof(buf), 0);
		printf("recv_buf: %s\n", buf); // 打印数据 
		srand((int)time(0));//生成2个随机数
		b=rand()%1000;//不要太大
		sleep(5);
        
		if((send(connfd, &b, sizeof(b), 0))<0) // 给客户端回数据  
            perror("send");
		printf("send b: %d\n",b);
		printf("client closed!\n"); 
        close(connfd);    //关闭已连接套接字   
	}
	close(listenfd);
    return 0;
}

运行结果:
客户端:
在这里插入图片描述
服务器端:
在这里插入图片描述
分析:
客户端是在TCP多路复用代码上的修改,主要是将标准输入和套接口设置成了非阻塞IO来接收数据,还多设置了一个套接口读的标识符sockread,sockfd套接口上读过之后就置sockread为1。服务器端则是普通的TCP循环服务器,多使用了一个获取随机数的函数srand()。
如上面的截图所示,服务器开启后等待客户端的连接请求。客户端开启后请求连接,并发送一个要求(字符串:fornumber)。此时客户端使用select等待标准输入和套接口可读,服务器在获取随机数准备发往客户端。当我们从客户端的标准输入输入a(a=13),没过多久也收到了服务器端发来的数据b(b=628),客户端将计算结果显示。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值