|
On Wed, 16 Aug 2006, Robert Upshall wrote:
Whenever I set the port to be non-blocking, the connect fails. Have you ever experienced that before?
When you say "fails" do you mean the connect() API returns -1? If so, that's what it's supposed to do. It doesn't really mean that it failed, it means that it didn't complete.
The purpose of non-blocking sockets is to guarantee that the an API you call won't "block." The term "block" means that it stops the execution of your program until some event happens. An event might be data arriving from a remote server, or a connection completing, etc. In a typical socket programming scenario, a program will "block on the connect() API" in other words, the program will stop executing when it runs he connect() API and wait until the connection has been established.
In non-blocking mode, you're telling the socket that you never want it to block. In other words, you never want to wait for something to complete!
When you call connect() in non-blocking mode, it always returns -1. Why? Because connecting to a server requires data to be sent to the server, an requires it to wait for the server to respond. Since it can't get the servers response without blocking, it can't complete.
So... how do you fix it?Well, if you don't need to set a timeout on the connect() API, you can simply move the code that changes the socket to non-blocking. Don't make it non-blocking until AFTER the connection is complete.
If you DO want to have a timeout on the connect() API, then you should follow these steps:
a) Create the socket b) Put it in non-blocking c) Connect() -- this'll always return -1 d) Check errno -- if it's EINPROGRESS, all is well. If not, something has gone wrong with the connect() API. e) Wait for the connection to complete. To do that, call the select() API and put the descriptor in the "write set". If the socket isn't writable in your timeout period, then the connection timed out. If the socket IS writable, either it succeeded or an error occurred. f) Use the getsockopt() API with SOL_SOCKET and SO_ERROR to find out if an error occurred while it was connecting. If an error did occur, use the err number returned by getsockopt() (instead of errno) to report the problem.Here's a subprocedure that I wrote that uses the connect() API in non-blocking mode to perform a "connect with timeout":
*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * tconnect(): Connect w/timeout * * host = (input) host to connect to * port = (input) port number to connect to * maxsecs = (input) maximum seconds to wait for connect * * Returns a connected socket, or -1 upon failure *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ P tconnect B export D tconnect PI 10I 0 D host 255A const D port 5U 0 value D maxsecs 10I 0 value D ErrMsg 80A D ipaddr s 10U 0 D sock s 10I 0 D flags s 10I 0 D connto ds likeds(sockaddr_in) D rc s 10I 0 D writeset s like(fdset) D rcsize s 10I 0 D timeout ds likeds(timeval) D errno s 10I 0 based(p_errno) /free // ------------------------------------------------ // Look up host // ------------------------------------------------ ipaddr = inet_addr(%trimr(host)); if (ipaddr = INADDR_NONE); p_hostent = gethostbyname(%trimr(host)); if (p_hostent = *NULL); errMsg = 'host not found!'; else; ipaddr = h_addr; endif; endif; // ------------------------------------------------ // Create a socket and put it in non-blocking // mode. // ------------------------------------------------ sock = socket(AF_INET: SOCK_STREAM: IPPROTO_IP); if (sock = -1); p_errno = sys_errno(); ErrMsg = %str(strerror(errno)); return -1; endif; flags = fcntl(sock: F_GETFL); flags = %bitor(flags: O_NONBLOCK); fcntl(sock: F_SETFL: flags); // ------------------------------------------------ // Start the connection process // connect will always return -1, but if all // is well, errno will be set to EINPROGRESS // ------------------------------------------------ connto = *allx'00'; connto.sin_family = AF_INET; connto.sin_addr = ipaddr; connto.sin_port = port; rc = connect(sock: %addr(connto): %size(connto)); p_errno = sys_errno(); if (rc = -1 and errno<>EINPROGRESS); ErrMsg = %str(strerror(errno)); callp close(sock); return -1; endif; // ------------------------------------------------ // Wait for connection to complete // ------------------------------------------------ FD_ZERO(writeSet); FD_SET(sock: writeSet); timeout.tv_sec = maxsecs; timeout.tv_usec = 0; rc = select( sock+1 : *NULL : %addr(writeSet) : *NULL : %addr(timeout) ); if (rc = -1); p_errno = sys_errno(); errMsg = %str(strerror(errno)); callp close(sock); return -1; endif; if (rc = 0); errMsg = 'Time out before connection completed.'; callp close(sock); return -1; endif; // ------------------------------------------------ // Check if an error occurred // ------------------------------------------------ rc = 0; rcsize = %size(rc); getsockopt(sock: SOL_SOCKET: SO_ERROR: %addr(rc): rcsize); if (rc <> 0); ErrMsg = %str(strerror(rc)); callp close(sock); return -1; endif; // ------------------------------------------------ // Hurray! Connection is completed! // ------------------------------------------------ return sock; /end-free P EThe FD_SET, FD_ISSET, FD_ZERO, etc, subprocedures are part of the SOCKUTIL service program. My tutorial contains instructions for how to download, compile and use them.
As an Amazon Associate we earn from qualifying purchases.
This mailing list archive is Copyright 1997-2024 by midrange.com and David Gibbs as a compilation work. Use of the archive is restricted to research of a business or technical nature. Any other uses are prohibited. Full details are available on our policy page. If you have questions about this, please contact [javascript protected email address].
Operating expenses for this site are earned using the Amazon Associate program and Google Adsense.