Chinaunix首页 | 论坛 | 博客
  • 博客访问: 369474
  • 博文数量: 174
  • 博客积分: 130
  • 博客等级: 入伍新兵
  • 技术积分: 587
  • 用 户 组: 普通用户
  • 注册时间: 2011-01-12 19:39
文章分类

全部博文(174)

文章存档

2018年(2)

2016年(10)

2015年(6)

2014年(31)

2013年(92)

2012年(33)

我的朋友

分类: 系统运维

2013-09-17 00:11:47

原文地址:非阻塞connect + select 作者:fishmwei

28
22.非阻塞connect
文章分类:互联网
connect非阻塞套接口时候,一般使用在以下几种情况:
 
1.三路握手需要时间,这个要视具体的网络情况而定。当然也有可能失败。在三路握手的时候我们并不需要在原地等待三路握手的完成,可以用这些时间来完成其它事情,然后当这些事情完成后,再去检测连接是否建立(也就是三路握手是否完成)。
 
2.可以用这种技术来同时建立多个连接。(WEB浏览器中很常用)。
 
3.connect超时需要很长时间才会通知,如果我们认为超过0.1秒以后就算超时(不管它是不是真的超时),这是就可以使用非阻塞式I/O结合 select来完成。
 
当采用非阻塞式I/O来使用connect时候,要判断一个连接是否建立则比较复杂,需要按照以下几个步骤来完成
 
1.即使是使用非阻塞式的connect操作,connect依然可能正确返回,也就是说非阻塞的connect 也有可能三路连接完成后返回,这种情况一般发生在服务器和主机在同一个机器上,所以第一步要判断connect是否正确返回,如果正确返回则请做正确返回的处理,否则进入步骤2
 
2.设置fd_set,(如果没看明白,请先看select函数介绍),让select函数同时监听套接字的读写2个属性,如果既可读也可写则进入步骤3,如果可写但不可读进入步骤4.
 
3.如果到达这步,我们需要调用getsockopt进一步判断。这里涉及到一个移植问题,getsockopt如果发生错误,源自Berkeley的实现会返回0,如果是solaris,则会返回-1。建议是2个都处理(如果看不明白请先看getsockopt函数,套接口选项)。根据getsockopt通过参数返回的erron的值,如果值为0则表示链接建立完成,如果不为0, 则说明链接建立没有完成。
 
4.如果能到达这里,则说明连接建立完成。
 
最后,即使最后你得出链接没有建立完成,也只是说:可能三路握手的过程还是没有完成。
 
代码:
服务器
#include "/programe/net/head.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#define MAXSIZE 100
//如果这样写,最后返回的套接口应该是会进入步骤3,也就是套接口既可读也可写,如果想进入步骤4,就不要想套接口中写入数据
int main(int argc, char ** argu) {
        int listenfd, connfd;
        struct sockaddr_in servaddr;
        char buf[MAXSIZE + 1];
        char buf2[] = "hello world\n";
        listenfd = socket(AF_INET, SOCK_STREAM, 0);
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        servaddr.sin_port = htons(atoi(argu[1]));
        bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
        listen(listenfd, 10);
        for(;;) {
                connfd = accept(listenfd, (struct sockaddr *)NULL, NULL);
                write(connfd, buf2, sizeof(buf2));
                close(connfd);
        }
}
 
客户端
#include "/programe/net/head.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "unistd.h"
#include "fcntl.h"
#include "sys/select.h"
#define MAXSIZE 100
int main(int argc, char ** argv) {
        int sockfd, n;
        int my;
        char send_buf[MAXSIZE + 1];
        char recv_buf[MAXSIZE + 1];
        struct sockaddr_in servaddr;
        int error = 0;
        if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
                printf("create socket error\n");
                exit(1);
        }
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(atoi(argv[1]));
        if(inet_pton(AF_INET, "192.168.1.235" , &servaddr.sin_addr) < 0) {
                printf("inet_pton error\n");
                exit(1);
        }
        int val = fcntl(sockfd, F_GETFL, 0);
        fcntl(sockfd, F_SETFL, val | O_NONBLOCK); //设置套接口非阻塞
        int connect_flag = connect(sockfd, (struct sockaddr *)&servaddr, sizeof(struct sockaddr_in));
        sleep(2); //connect会立即返回,不同于之前的要阻塞到链接完成才会返回,这里可以做你想在等待连接完成的时间想做的事情,我这里只是让进程睡眠了一段时间
        if(connect_flag >= 0) {
                printf("connect success\n"); //即使是非阻塞套接口,connect还是有可能正确返回的,这种情况要处理
                goto done; //不建议使用goto
        }
        fd_set rest, west;
        FD_ZERO(&rest);
        FD_ZERO(&west);
        FD_SET(sockfd, &rest);
        FD_SET(sockfd, &west);
        int maxpd = sockfd + 1;
        int flag = select(maxpd, &rest, &west, NULL, NULL);//监听套接的可读和可写条件
        if(flag < 0) {
                printf("select error\n");//慢系统调用可能会错误返回,这个以前提过
                exit(1);
        }
        if(FD_ISSET(sockfd, &rest) && FD_ISSET(sockfd, &west)) {//如果套接口及可写也可读,需要进一步判断
                socklen_t len = sizeof(error);
                if(getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
                        exit(1);//获取SO_ERROR属性选项,当然getsockopt也有可能错误返回
                printf("error = %d\n", error);
                if(error != 0) {//如果error不为0, 则表示链接到此没有建立完成
                        printf("connect failed\n");
                        exit(1);
                }
                //如果error为0,则说明链接建立完成
        }
        if(FD_ISSET(sockfd, &west) && !FD_ISSET(sockfd, &rest)) { //如果套接口可写不可读,则链接完成
                printf("connect success\n");
        }
done:
        int recv_buf_len  = read(sockfd, recv_buf, MAXSIZE);
        recv_buf[recv_buf_len] = '\0';
        printf("get message:%s", recv_buf);
        close(sockfd);
        exit(0);
}
阅读(1093) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~