|
Scott, Thanks for the info. This should give me what I need. I'm looking forward to getting past CLIENTEX1 and through the rest of your tutorial. And I'm planning on using hostnames instead of IP addresses, but the Redbook examples don't so I wasn't either at that point. Mike E. Scott Klement <klemscot@klement To: rpg400-l@midrange.com s.com> cc: Sent by: Subject: Re: sockets connect() behaving strangely on local network rpg400-l-admin@mi drange.com 08/26/2002 04:12 AM Please respond to rpg400-l On Sun, 25 Aug 2002 meovino@estes-express.com wrote: > > This sounds logical as well, but I'm really doing most of my testing with > the SCLIENT4 and SSERVER4 programs in the redbook, and they take dotted > IP addresses, not hostnames. > So it's not DNS that's causing the delays (I thought that was unlikely anyway). Tho, you really should have your clients use hostnames instead of addresses. The idea behind that is that when the hosts get moved to a different area of the network or internet, and their IP addresses are forced to change, you should only have to change the address in one place... the DNS server. > I used CLIENTEX1 to see if I got the same results, and I did. > No big surprise there. They're both running the same connect() API on the same network interfaces connected to the same networks. They should behave the same. > > > My question to you is: Why does it matter? What are you trying to > > accomplish? > > We are attempting to write a sockets client and server to allow us to > communicate with our sister companies that are all on a VPN into our > LAN. We're the most IT savvy of the group, so we're writing all of > this. This is going to be used on the fly to get data to use in CGI > programs, so two minutes is too long for a timeout. I'd like to know > that the connection has failed immediately. As I said before... you can't know "immediately". Either something has to send back an ICMP indicating failure (which could be very quick, or could take a few seconds to send back) or you have to wait for a timeout... That's the nature of network programming. What you CAN do is use a smaller timeout value if 2 minutes is too extreme. You do this by using non-blocking sockets and the select() API. > > > Maybe I've got a solution for you... > > I have a feeling you do. I've seen you write about non-blocking sockets > elsewhere on this lilst. SCLIENT4 uses them, but it does not set the > socket to non-blocking until *after* the connect. I copied the code > that sets the socket to non-blocking before the connect, and the > connect() gives me the return code of -1 immediately when I have > disabled the interface. Unfortunately, it also gives me a return code > of -1 when the interface is up. > Correct. That's exactly what it SHOULD do. Did you even bother to read about non-blocking and understand what it does? Here's a quick explanation: Non-blocking tells the API that you never want to wait for a socket operation. In other words, when you call connect(), accept(), recv(), send() APIs they always return immediately. The connect() API can only return two possible results. A 0 which means the connection has been established, or a -1 which means that it hasn't. Making a connection takes time. It has to send setup datagrams to the remote host, and the remote host has to send back datagrams. They have to negotiate some parameters, and then the connection can be started. So, on a non-blocking socket, when you call connect() it nearly always returns a -1 indicating that the connection has not been made... it's theoretically possible that the connection could be established so fast that it succeeds without blocking, but I've never seen it happen. So, what you need to do in non-blocking mode is: Check errno to see why the socket failed. Chapter 4 of my socket tutorial will explain how to use errno to get the error message. If the connect() API gives you an error message of EINPROGESS it means that the socket is in non-blocking mode, and the connect() is happening asynchronusly. When that happens, you can call the select() API and wait for your socket to become writable. You can give select() any time-out value that you like. So, if you wanted your timeout to be 30 seconds instead of 2 mins, you'd tell select() to wait until either the socket is writable, or until 30 seconds has gone by. When select finishes, you could check to see if the socket ended up writable... if so, you know the connection was made. If not, a timeout occurred, and you should call close() for that socket to cancel the connect() operation. > > Does your tutorial have an example of a non-blocking connect() that will > handle this situation. I've only gotten to CLIENTEX1 so far. But I > didn't want to go too much farther without finding out if what's > happening with the two different timeouts was a symptom of a problem > with our network that would keep *anything* from working properly. > Sounds like that's not the case. > Not in so many words. But it does explain what non-blocking means. And it does show you how to retrieve error numbers in chapter 4. And it does explain how to call select() in chapter 6. It doesn't put them together to specifically show you how to do a time-out on a connect(), however. I am currently writing a training manual that WILL explain this (as well as other places you might want to do timeouts) but that's not finished yet. For the time being, I can give you a code sample. This code uses the /copy members from my sockets tutorial, as well as the sockutilr4 service program which is also in my tutorial. You'll want to get those from my web site to use this code: * * Example of calling the connect() API with a timeout * H DFTACTGRP(*NO) ACTGRP(*NEW) H BNDDIR('QC2LE') BNDDIR('SOCKTUT/SOCKUTIL') D/copy socktut/qrpglesrc,socket_h D/copy socktut/qrpglesrc,errno_h D/copy socktut/qrpglesrc,sockutil_h ********************************************************* * end of IFS API call definitions ********************************************************* D die PR D peMsg 256A const D cmd PR ExtPgm('QCMDEXC') D command 200A const D length 15P 5 const D sock S 10I 0 D port S 5U 0 D flags S 10I 0 D host s 32A D addr s 10U 0 D connto S like(sockaddr_in) D err S 10I 0 D connfds S like(fdset) D timeout S like(timeval) C************************************************* C* The user will supply a hostname and file C* name as parameters to our program... C************************************************* c *entry plist c parm host c eval *inlr = *on C************************************************* C* For this example, we'll look up the port C* number of the HTTP service: C************************************************* c eval p_servent = getservbyname('http':'tcp') c if p_servent = *NULL c callp die('Can''t find the HTTP service!') c return c endif c eval port = s_port C************************************************* C* Get the 32-bit network IP address for the host C* that was supplied by the user: C************************************************* c eval addr = inet_addr(%trim(host)) c if addr = INADDR_NONE c eval p_hostent = gethostbyname(%trim(host)) c if p_hostent = *NULL c callp die('Unable to find that host!') c return c endif c eval addr = h_addr c endif C************************************************* C* Create a socket C************************************************* c eval sock = socket(AF_INET: SOCK_STREAM: c IPPROTO_IP) c if sock < 0 c callp die('socket(): ' + %str(strerror(errno))) c return c endif c************************************************* C* Put the socket in non-blocking mode: c************************************************* c eval flags = fcntl(sock: F_GETFL) c eval flags = flags + O_NONBLOCK c if fcntl(sock: F_SETFL: flags) < 0 c callp die('fcntl(): ' + %str(strerror(errno))) c return c endif c************************************************* C* Create a socket address structure that C* describes the host & port we wanted to C* connect to C************************************************* c eval p_sockaddr = %addr(connto) c eval sin_family = AF_INET c eval sin_addr = addr c eval sin_port = port c eval sin_zero = *ALLx'00' C************************************************* C* Start the connection process. C************************************************* c if connect(sock: %addr(connto): c %size(connto) ) < 0 c eval err = errno c if err <> EINPROGRESS c callp close(sock) c callp die('connect(): '+%str(strerror(err))) c return c endif c endif C************************************************* C* wait up to 30 sec for connection to be made C************************************************* c callp FD_ZERO(connfds) c callp FD_SET(sock: connfds) c eval p_timeval = %addr(timeout) c eval tv_sec = 30 c eval tv_usec = 0 c if select(sock+1: *NULL: c %addr(connfds): c *NULL: c %addr(timeout)) < 0 c eval err = errno c callp close(sock) c callp die('select(): '+%str(strerror(err))) c return c endif c if FD_ISSET(sock: connfds) = *OFF c callp close(sock) c callp die('connect(): time out value reached') c return c endif C* Here we are connected. We can use send() & recv() to C* send data here... c callp close(sock) c return *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * This ends this program abnormally, and sends back an escape. * message explaining the failure. *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ P die B D die PI D peMsg 256A const D SndPgmMsg PR ExtPgm('QMHSNDPM') D MessageID 7A Const D QualMsgF 20A Const D MsgData 256A Const D MsgDtaLen 10I 0 Const D MsgType 10A Const D CallStkEnt 10A Const D CallStkCnt 10I 0 Const D MessageKey 4A D ErrorCode 32766A options(*varsize) D dsEC DS D dsECBytesP 1 4I 0 INZ(256) D dsECBytesA 5 8I 0 INZ(0) D dsECMsgID 9 15 D dsECReserv 16 16 D dsECMsgDta 17 256 D wwMsgLen S 10I 0 D wwTheKey S 4A c eval wwMsgLen = %len(%trimr(peMsg)) c if wwMsgLen<1 c return c endif c callp SndPgmMsg('CPF9897': 'QCPFMSG *LIBL': c peMsg: wwMsgLen: '*ESCAPE': c '*PGMBDY': 1: wwTheKey: dsEC) c return P E /define ERRNO_LOAD_PROCEDURE /copy socktut/qrpglesrc,errno_h _______________________________________________ This is the RPG programming on the AS400 / iSeries (RPG400-L) mailing list To post a message email: RPG400-L@midrange.com To subscribe, unsubscribe, or change list options, visit: http://lists.midrange.com/cgi-bin/listinfo/rpg400-l or email: RPG400-L-request@midrange.com Before posting, please take a moment to review the archives at http://archive.midrange.com/rpg400-l.
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.