将阻塞模式的socket连结变为非阻塞模式

发信人: landyhorse (马儿), 信区: NetPRG       
标  题: ---将阻塞模式的socket连结变为非阻塞模式---
发信站: BBS 水木清华站 (Fri Dec  7 15:45:40 2001)



前两天在linux下作了这方面的应用,
当时也是找了好半天才搞出来,
看到有人问,就讲讲基本原理,和一段代码,免得大家再走弯路
该方法最适合线程使用
 
1。原理(来自linuxforum)
  
创建socket后,先把connect()用的socket的模式保存,
再将其设成nonblock模式(使用fcntl函数),
然后进行connect()。
connect()会return两种情况,一是success,
那就restore socket的以前mode, again use fcntl(), 然后
就算connect好了,二则是-1, 那么就要看errno, 如果是
EINPROGRESS,则就用把socket加到FD_SET里,然后用select()
select socket for writting.
因为select()可以用timeout, 然后如果select() return的话,
如果return 0, 那就是timeout, -1就要看你根据errno如何处理了,
如果是>0, 则用FD_ISSET()看是否是socket connect好了,这时
还有个condition是connection refused, 对方送了你一个RST,
你需要用getsockopt(sockfd, SOL_SOCKET,SO_ERROR,....)去
查一下,如果没有问题那就是connect success了
  

2.例程代码(来自白云黄鹤)
   
  /************************************************************************
 * tcpconnect.c, by digger, 带超时限制的非阻塞式connect                 *
 ************************************************************************/

# include       <stdio.h>
# include       <string.h>
# include       <sys/types.h>
# include       <sys/socket.h>
# include       <sys/time.h>
# include       <fcntl.h>
# include       <unistd.h>
# include       <netinet/in.h>
# include       <arpa/inet.h>
# include       <netdb.h>
# include       <errno.h>

/************************************************************************
 * 函数: tcpconnect( )                                                  *
 * 输入: char * server_addr, u_short server_port, int limit_seconds     *
 * 成功: 返回创建并连接服务端成功的socket文件号, 失败: 返回-1           *
 ************************************************************************/

int tcpconnect( char * server_addr,     /* 服务端地址, 域名或IP地址     */
                u_short server_port,    /* 服务端口                     */
                int limit_seconds       /* 超时限制, 单位为秒           */
              )
{
        struct  hostent         * hp;           /* host entry pointer */
        struct  sockaddr_in     server;         /* server address structure */
        int     sock;                           /* socket to create */
        fd_set  fdW;                            /* fileds to be select */
        struct  timeval timeout;                /* timeout parameter */
        int     fflag;                          /* file flag for fcntl */
        int     errcode;                        /* error code for getsockopt */
        int     errlen;                         /* length of errcode */

        /* fill in server address structure with ip address and port */

        bzero(&server, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(server_port);
        if ((hp = gethostbyname(server_addr)) == NULL) {
                if ((server.sin_addr.s_addr = inet_addr(server_addr)) == -1) {
                        fprintf(stderr, "%s: unknown host/n", server_addr);
                        exit(1);
                }
        } else {
                bcopy(hp->h_addr_list[0], &server.sin_addr, hp->h_length);
        }

        /* create client socket and connect it to server */

        if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
                perror("socket");
                return(-1);
        }

        /* get original sock flag */

        if ((fflag = fcntl(sock, F_GETFL, 0)) < 0) {
                perror("fcntl get file flag");
                exit(-1);
        }

        /* set O_NDELAY flag to sock */

        if (fcntl(sock, F_SETFL, fflag|O_NDELAY) < 0) {
                perror("fcntl set file flag O_NDELAY");
                exit(-1);
        }

        /* connect to server using nonblock mode */

        connect(sock, (struct sockaddr *)&server, sizeof(server));

        /* set timeout structure */

        timeout.tv_sec = limit_seconds;
        timeout.tv_usec = 0;

        FD_ZERO(&fdW);
        FD_SET(sock, &fdW);

CNNT_AGAIN:

        switch(select(sock + 1, NULL, &fdW, NULL, &timeout)) {

        case    -1:
                if (errno == EINTR) goto CNNT_AGAIN;    /* call interrupted */
                close(sock);                            /* select error */
                exit(-1);

        case    0:
                close(sock);
                return(-1);

        default:
                if (FD_ISSET(sock, &fdW)) {     /* check if socket writable */
                        errlen = sizeof(errcode);
                        getsockopt(sock, SOL_SOCKET, SO_ERROR,
                                (char *)&errcode,&errlen);
                        if(errcode == 0) {      /* check if connect right */
                                /* reset original sock flag */
                                if (fcntl(sock, F_SETFL, fflag) < 0) {
                                        perror("fcntl reset file flag");
                                        exit(-1);
                                }
                                return(sock);
                        } else {
                                close(sock);
                                return(-1);
                        }
                }
        }


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值