Time to put try our new found UDP knowledge out with a sample UDP server program. This program will receive datagrams containing a user-id and some text, and then will send that user a message using the SNDMSG command. Once the message has been sent, it will send back a datagram that acknowledges that the message was sent correctly.
So, the pseudocode for our server looks like this:
Create a UDP socket
Bind the socket to a port. Maybe port 4000, that's worked pretty well so far, yes?
Wait for any datagrams to be received. (we can use select() to give us a timeout, so we can check for system shut down)
convert the datagram to EBCDIC.
substring out a userid & message
Call the SNDMSG command to send the message
Send back an acknowledgement packet
GO back to step 3.
Note: Unlikely TCP, we CANNOT use a simple telnet client to test our server program. We'll have to write a client program to play with our server -- which we'll do in the next topic.
Here's our server:
File SOCKTUT/QRPGLESRC, Member: UDPSERVER
H DFTACTGRP(*NO) ACTGRP(*NEW) H BNDDIR('QC2LE') BNDDIR('SOCKTUT/SOCKUTIL') D/copy socktut/qrpglesrc,socket_h D/copy socktut/qrpglesrc,sockutil_h D/copy socktut/qrpglesrc,errno_h D Cmd PR ExtPgm('QCMDEXC') D command 200A const D length 15P 5 const D translate PR ExtPgm('QDCXLATE') D length 5P 0 const D data 32766A options(*varsize) D table 10A const D die PR D peMsg 256A const D sock S 10I 0 D err S 10I 0 D bindto S * D len S 10I 0 D dtalen S 10I 0 D msgfrom S * D fromlen S 10I 0 D timeout S * D tolen S 10I 0 D readset S like(fdset) D excpset S like(fdset) D user S 10A D msg S 150A D buf S 256A D rc S 10I 0 c eval *inlr = *on C* Reserve space for the address that we receive a C* datagram from. c eval fromlen = %size(sockaddr_in) c alloc fromlen msgfrom C* Create a timeval structure so we can check for shutdown C* every 25 seconds: c eval tolen = %size(timeval) c alloc tolen timeout c eval p_timeval = timeout c eval tv_sec = 25 c eval tv_usec = 0 C* Create a socket to do our UDP stuff: c eval sock = socket(AF_INET: SOCK_DGRAM: c IPPROTO_IP) c if sock < 0 c callp die('socket(): '+%str(strerror(errno))) c return c endif C* Create a sockaddr struct to tell the C* bind() API which port to use. c eval len = %size(sockaddr_in) c alloc len bindto c eval p_sockaddr = bindto c eval sin_family = AF_INET c eval sin_addr = INADDR_ANY c eval sin_port = 4000 c eval sin_zero = *ALLx'00' C* bind to the port. c if bind(sock: bindto: len) < 0 c eval err = errno c callp close(sock) c callp die('bind(): '+%str(strerror(err))) c return c endif c dow 1 = 1 C* Use select to determine when data is found c callp fd_zero(readset) c callp fd_zero(excpset) c callp fd_set(sock: readset) c callp fd_set(sock: excpset) c eval rc = select(sock+1: %addr(readset): c *NULL: %addr(excpset): timeout) C* If shutdown is requested, end program. c shtdn 99 c if *in99 = *on c callp close(sock) c return c endif C* If select timed out, go back to select()... c if rc = 0 c iter c endif C* Receive a datagram: c eval dtalen = recvfrom(sock: %addr(buf): c %size(buf): 0: msgfrom: fromlen) C* Check for errors c if dtalen < 0 c eval err = errno c callp close(sock) c callp die('recvfrom(): '+%str(strerror(err))) c return c endif C* Skip any invalid messages c if dtalen < 11 c iter c endif C* Skip any messages from invalid addresses c if fromlen <> %size(sockaddr_in) c eval fromlen = %size(sockaddr_in) c iter c endif C* Translate to EBCDIC c callp Translate(dtalen: buf: 'QTCPEBC') c* send message to user c eval user = %subst(buf:1: 10) c eval dtalen = dtalen - 10 c eval msg = %subst(buf:11:dtalen) c callp(e) Cmd('SNDMSG MSG(''' + %trimr(msg) + c ''') TOUSR(' + %trim(user) + ')': 200) c* make an acknowledgement c if %error c eval buf = 'failed' c eval dtalen = 6 c else c eval buf = 'success' c eval dtalen = 7 c endif c* convert acknowledgement to ASCII c callp Translate(dtalen: buf: 'QTCPASC') c* send acknowledgement to ASCII c if sendto(sock: %addr(buf): dtalen: 0: c msgfrom: fromlen) < 0 c eval err = errno c callp close(sock) c callp die('sendto(): '+%str(strerror(err))) c return c endif c enddo *+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ * 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