Non-blocking Connect

Source: Internet
Author: User
Tags socket error
1. Non-blocking ConnectWhat's the use?
    1. You can make the three-way handshake equal to the processing of general data, not always connect trying to re-connect or spend a RTT time. And the RTT time varies from a few milliseconds to a few seconds, in case there are many connections, whether it is trying to reconnect or spend a RTT time, it will be a fatal delay.
    2. You can use this technique to establish multiple connections at the same time. Common in Web browsers.
    3. Since select the use of waiting connections is established, we can texture not a time limit, which allows us to shorten connect the timeout.
2. Details that must be dealt with:
    1. Handle connect The situation of immediate establishment. (e.g. when we are connecting to the same host)
    2. selcetsome precautions to use with non-blocking connect :

      2.1. When the connection is successfully established, the descriptor becomes writable.
      2.2 When an error is encountered, the descriptor becomes writable and readable.

3. Two examples: (1) Non-blocking connect: Time Acquisition client program
int connect_nonblock (int sockfd, const SA *saptr, socklen_t salen, int nsec)//return-1 Failure {int flags, n, error;    Socklen_t Len;    Fd_set RSet, Wset;    struct Timeval tval;    Flags = FCNTL (SOCKFD, F_GETFL, 0); Fcntl (SOCKFD, F_SETFL, Flags |    O_nonblock);    Error = 0; if ((n = connect (SOCKFD, saptr, Salen)) < 0) {if (errno! = einprogress)//indicates that the connection has been started but has not completed return    (-1);    } if (n = = 0)//indicates that the connection was established to complete the goto done immediately;    Fd_zero (&rset);    Fd_set (SOCKFD, &rset);    Wset = RSet;    Tval.tv_sec = nsec;    tval.tv_usec = 0; if (n = Select (sockfd + 1, &rset, &wset, NULL, nsec? &tval:null)) = = 0)//return 0, timeout, close socket {Close (so        CKFD);        errno = Etimedout;    Return (-1); } if (Fd_isset (SOCKFD, &rset) | |        Fd_isset (SOCKFD, &wset)) {len = sizeof (error);    if (getsockopt (SOCKFD, Sol_socket, So_error, &error, &len) < 0) return (-1); } else Err_quit ("Selcet erROR:SOCKFD not set\n ");d One://until established to return to Fcntl (SOCKFD, F_SETFL, flags);        if (Error) {Close (SOCKFD);        errno = error;    Return (-1); } return 0; Successful connection}
Some notes:

In fact, it is relatively simple, is connect to connect, if you can connect on the connection can, if not connected select to let as general data to deal with it! For the connection, select there are two situations where success is writable, and failure can be read and writable. So that's the problem?

How to judge success or failure? Emmmm, the so-called failure is a mistake, then we directly detect whether there are errors. Use the getsockopt function.

<1> getsockoptFunction Description: Gets the options associated with a socket
       int getsockopt(int socket, int level, int option_name,           void *restrict option_value, socklen_t *restrict option_len);
    1. getcoksoptAnd setsockopt both are used only for sockets

    2. levelSpecifies the code that interprets options in the system or is a generic socket code, or code that is specific to a protocol.

    3. option_valueThe current value of the obtained option is stored in *option_value , in option_len the *option_value size of.

    4. option_nameRepresents an option.

return value:
RETURN VALUE       Upon  successful  completion,  getsockopt()  shall  return 0; otherwise, −1 shall be returned and errno set to indicate the       error.
    1. BerkeleySystem: *option_value return pending error in, function returns 0
    2. SolarisSystem: will be set errno to pending error, function returns-1

So in our code, we're going to handle both of these cases.

<2> Test:
int main(int argc, char **argv){    int sockfd, n;    char recvline[MAXLINE + 1] = {0};    struct sockaddr_in servaddr;    if (argc != 2)        err_quit("usage: a.out <IPaddress>");    if ((sockfd = Socket(AF_INET, SOCK_STREAM, 0)) < 0)        err_sys("Socket error");    bzero(&servaddr, sizeof(servaddr));    servaddr.sin_family = AF_INET;    servaddr.sin_port = htons(13); /* daytime server */    if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)        err_quit("inet_pton error for %s", argv[1]);    if (Connect_nonblock(sockfd, (SA *)&servaddr, sizeof(servaddr), 10) < 0)        err_sys("connect error");    while ((n = recv(sockfd, recvline, MAXLINE, 0)) > 0)    {        recvline[n] = 0; /* null terminate */        printf("recvline ==  %s\n", recvline);    }    if (n < 0)        err_sys("read error");    return 0;}

(2) Non-blocking Connect: Web client program

Get a home page first, and then get additional network resources for the home page in parallel with multiple connections. Obviously, the parallel connection sequence of such a child is faster than the serial fetch of the resource.

    1. Structural Body Design
#define MAXFILES 20#define SERV "80"struct file{    char *f_name; //资源路径    char *f_host; //主机    int f_fd;//套接字    int f_flags; //当前状态,有四种值,分别是 { 0, F_CONNECTING, F_READING, F_DONE }}file[MAXFILES];
    1. General idea:
// 假设我们下载 10 资源初始化 struct file files[10];先成功建立第一个连接(获取主页)while(xxx) {   使用非阻塞I/O, 同时建立多个连接,每一个 f_flags = F_CONNECTING.   select 监听套接字   for (f in files) { // 遍历所有文件     if (f.f_flags == F_CONNECTING) {       // 检查连接是否成功或失败。使用我们上面用到的知识,主要是 getsockopt 函数       如果连接成功,则发起 GET 请求,同时 f_flags = F_READING.       如果连接失败,f_flags = F_DONE;     }     else if (f.f_flags == F_READING) {       // 下载资源       nr = read(f.f_fd, buf);       if (nr == 0) {         对端关闭, f.f_flags = F_DONE;       }     }   }}

web.hFile

#ifndef _WEB_H#define _WEB_H#include "../myhead.h"#define MAXFILES 20#define SERV "80"struct file{    char *f_name;    char *f_host;    int f_fd;    int f_flags;}file[MAXFILES];#define F_CONNECTING 1#define F_READING 2#define F_DONE 4#define GET_CMD "GET %s HTTP/1.0\r\n\r\n"int nconn, nfiles, nlefttoconn, nlefttoread, maxfd;fd_set rset, wset;/*nconn:当前打开的连接数,不超过第一个命令行参数nlefttoread:待读取的文件数量nlefttoconn:尚未连接的文件数nfiles:文件数量*/#endif

web.cFile

#include "web.h" struct addrinfo *host_serv (const char *host, const char *serv, int family, int socktype), void Home_pages (c Onst Char *host, const char *fname); void start_connect (struct file *fptr);    Non-blocking connection; void Write_get_cmd (struct file *fptr); int tcp_connect (const char *host, const char *serv) {int sockfd, n;    struct Addrinfo hints, *res, *ressave;    Bzero (&hints, sizeof (struct addrinfo));    hints.ai_family = Af_unspec;    Hints.ai_socktype = Sock_stream; if ((n = getaddrinfo (host, serv, &hints, &res))! = 0) err_quit ("Tcp_connect error for%s,%s,%s:%s", host    , serv, Gai_strerror (n));    Ressave = res;        do {sockfd = Socket (res->ai_family, Res->ai_socktype, Res->ai_protocol);        if (SOCKFD < 0) continue;        if (Connect (SOCKFD, res->ai_addr, res->ai_addrlen) = = 0) break;    Close (SOCKFD);    } while ((res = res->ai_next) = NULL); if (res = = NULL) Err_sys ("Tcp_coonnect error for%s,%s ", host, serv);    Freeaddrinfo (Ressave); return (SOCKFD);}    struct Addrinfo *host_serv (const char *host, const char *serv, int family, int socktype) {int n;    struct addrinfo hints, *res;    Bzero (&hints, sizeof (struct addrinfo));    Hints.ai_flags = Ai_canonname;    hints.ai_family = family;    Hints.ai_socktype = Socktype;                 if ((n = getaddrinfo (host, serv, &hints, &res))! = 0) err_quit ("Host_serv error for%s,%s:%s", (host = = NULL)? "(No hostname)": Host, (serv = = NULL)?    "(No Service Name)": Serv, Gai_strerror (n)); return (RES);    void Home_pages (const char *host, const char *fname) {int FD, n;    Char Line[maxline] = {0};    FD = Tcp_connect (host, SERV);    n = snprintf (line, sizeof (line), Get_cmd, fname);    Sendlen (fd, line, n, 0);    for (;;) {if (n = Recvlen (fd, line, MAXLINE, 0) = = 0),//serv closed fprintf (stderr, "recv%d byt    Es from server \ n ", n.); }    fprintf (stderr, "end-of-home-pages\n"); Close (FD);}    void Start_connect (struct file *fptr)//non-blocking connection {int FD, flags, n;    struct Addrinfo *ai;    ai = Host_serv (fptr->f_host, serv, 0, sock_stream);    FD = Socket (ai->ai_family, Ai->ai_socktype, Ai->ai_protocol);    FPTR-&GT;F_FD = FD;    fprintf (stderr, "Start_connect for%s, fd%d \ n", Fptr->f_name, FD);    Flags = FCNTL (FD, F_GETFL, 0); Fcntl (FD, F_SETFL, Flags |    O_nonblock); if ((n = connect (FD, AI-&GT;AI_ADDR, Ai->ai_addrlen)) < 0) {if (errno! = einprogress)//einprogress socket is non-        The socket was blocked and the connection request did not complete immediately err_sys ("Nonblocking connect Error", __line__);        Fptr->f_flags = f_connecting;        Fd_set (FD, &rset);        Fd_set (FD, &wset);    if (fd > maxfd) maxfd = FD;    } else if (n >= 0) {/* Connect is already do */Write_get_cmd (FPTR);    }}void write_get_cmd (struct file *fptr) {int n;    Char Line[maxline]; n = snprintf (line,sizeof (line), Get_cmd, fptr->f_name);    Writen (fptr->f_fd, line, N);    fprintf (stderr, "send%d bytes for%s \n\n\n", N, Fptr->f_name); Fptr->f_flags = f_reading; /* Clears f_connecting */Fd_set (FPTR-&GT;F_FD, &rset); /* would read server ' s reply */if (fptr->f_fd > maxfd) maxfd = FPTR-&GT;F_FD;}    int main (int argc, char **argv) {int i, FD, N, maxconn, flags, error;    Char Buf[maxline] = {0};    Fd_set RS, WS;        if (ARGC < 5) {fprintf (stderr, "Use:web conns hostname homepages files ...");    return 0;    } maxconn = Atoi (argv[1]);    nfiles = min (argc-4, maxfiles);        for (i = 0; i < nfiles; i++) {file[i].f_name = argv[i + 4];        File[i].f_host = argv[2];    file[i].f_flags = 0;    } fprintf (stderr, "nfiles = =%d \ n", nfiles); Home_pages (Argv[2], argv[3]);    Establishment of the first connection Fd_zero (&rset);    Fd_zero (&wset);    MAXFD =-1;    Nlefttoread = Nlefttoconn = Nfiles;    Nconn = 0; /*nconn:The number of connections currently open, no more than the first command-line parameter nlefttoread: number of files to read nlefttoconn: Number of files not yet connected Nfiles: number of files */while (Nlefttoread > 0) {while        (Nconn < maxconn && nlefttoconn > 0) {/* 4find a file to read */for (i = 0; i < nfiles; i++) if (file[i].f_flags = = 0            ) break;            if (i = = nfiles) err_quit ("Nlefttoconn =%d but nothing found", nlefttoconn);            Start_connect (&file[i]);            nconn++;        nlefttoconn--;        } rs = RSet;        WS = WSET;        n = Select (maxfd + 1, &rs, &AMP;WS, NULL, NULL);            for (i = 0; i < nfiles; i++) {flags = File[i].f_flags;            if (flags = = 0 | | Flags & F_DONE) continue;            FD = FILE[I].F_FD; if (Flags & f_connecting && (Fd_isset (FD, &rs) | |                Fd_isset (FD, &AMP;WS))) {n = sizeof (error); if (GetsoCkopt (FD, Sol_socket, So_error, &error, &n) < 0 | | Error! = 0) {err_ret ("nonblocking Connect failed for%s", fi                    Le[i].f_name);                File[i].f_flags = F_done; }/* 4connection established */fprintf (stderr, "connection established for%s\n", FILE[I].F                _NAME);       FD_CLR (FD, &wset); /* No more writeability Test */Write_get_cmd (&file[i]);            /* Write () the GET command */} else if (Flags & f_reading && fd_isset (FD, &rs)) {if (n = Read (fd, buf, sizeof (BUF)) = = 0) {fprintf (stderr, "E                    Nd-of-file on%s\n ", file[i].f_name);                    Close (FD); File[i].f_flags = F_done;                    /* Clears f_reading */FD_CLR (FD, &rset);                    nconn--; Nlefttoread--;                } else {fprintf (stderr, "read%d bytes from%s\n", N, File[i].f_name); }}}} exit (0);}
Test:

This is 3 the case when the maximum number of parallel connections is:

Appendix: 1. Connect function Description (summary UNP connect)
    1. connectTCPthe three-way handshake that is fired, and is returned only if the connection is established successfully or after an error.
    2. When called on a non-blocking socket connect , connect An error is returned immediately EINPROGRESS , but the three-way handshake continues. Then we pass select to detect that the connection succeeds or fails.

    3. If connect the connection fails, the socket is no longer available and must be closed! cannot be called again for such a socket connect .

Discuss:

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.