AAnn IInnttrroodduuccttiioonn ttoo SSoocckkeett PPrrooggrraammmmiinngg _L_a_s_t _E_d_i_t _M_a_r_c_h _2_3_, _1_9_9_2 Reg Quinton Computing and Communications Services The University of Western Ontario London, Ontario N6A 5B7 Canada 11.. SSoocckkeett PPrrooggrraammmmiinngg This course is directed at Unix application programmers who want to develop client/server applications in the TCP/IP domain. Fundamental concepts are covered including network address- ing, well known services, sockets and ports. Sample appli- cations are examined with a view to developing similar applications that serve other contexts. This course requires an understanding of C programming and an appreciation of the programming environment (ie. compil- ers, loaders, libraries, Makefiles and the RCS revision con- trol system). BBEEWWAARREE:: If C code scares you, then you'll get some con- cepts but you might be in the wrong course. Our example is the UWO wwhhooiiss((ll)) service -- client and server sources available in: jjuulliiaann::~~ffttpp//ppuubb//uunniixx//nneettwwoorrkkiinngg//rrwwhhooiiss 11 UUWWOO//SSoocckkeett 22.. EExxiissttiinngg SSeerrvviicceess On a Unix machine there are usually lots of TCP/IP services installed and running (tons on julian!). [[11::1177ppmm jjuulliiaann]] nneettssttaatt --aa AAccttiivvee IInntteerrnneett ccoonnnneeccttiioonnss ((iinncclluuddiinngg sseerrvveerrss)) PPrroottoo RReeccvv--QQ SSeenndd--QQ LLooccaall AAddddrreessss FFoorreeiiggnn AAddddrreessss ((ssttaattee)) ttccpp 00 00 jjuulliiaann..22771177 vvnneett..iibbmm..ccoomm..ssmmttpp EESSTTAABBLLIISSHHEEDD ttccpp 00 00 jjuulliiaann..ssmmttpp uuaaccsscc22..aallbbaannyy..eedd..5555004499 TTIIMMEE__WWAAIITT ttccpp 00 1133 jjuulliiaann..nnnnttpp wwaattsseerrvv11..wwaatteerrlloo..33550077 EESSTTAABBLLIISSHHEEDD ttccpp 00 00 jjuulliiaann..nnnnttpp gglleeeepp..ccssdd..uuwwoo..ccaa..33441133 EESSTTAABBLLIISSHHEEDD ttccpp 00 00 jjuulliiaann..tteellnneett uuwwoonneett--sseerrvveerr22..cc..5555331166 EESSTTAABBLLIISSHHEEDD ttccpp 00 00 jjuulliiaann..llooggiinn nnoo88ssuunn..ccssdd..uuwwoo..cc..11002233 EESSTTAABBLLIISSHHEEDD ttccpp 00 00 jjuulliiaann..22663344 XXssttnn1155..ggaauull..ccssdd....66000000 EESSTTAABBLLIISSHHEEDD _e_t_c_._._. ttccpp 00 00 **..pprriinntteerr **..** LLIISSTTEENN ttccpp 00 00 **..ssmmttpp **..** LLIISSTTEENN ttccpp 00 00 **..wwaaiissjj **..** LLIISSTTEENN ttccpp 00 00 **..aaccccoouunntt **..** LLIISSTTEENN ttccpp 00 00 **..wwhhooiiss **..** LLIISSTTEENN ttccpp 00 00 **..nnnnttpp **..** LLIISSTTEENN _e_t_c_._._. uuddpp 00 00 **..nnttpp **..** uuddpp 00 00 **..ssyysslloogg **..** uuddpp 00 00 **..xxddmmccpp **..** UUWWOO//SSoocckkeett 22 22..11.. NNeettssttaatt OObbsseerrvvaattiioonnss Inter Process Communication, IPC, is between host.port pairs (or host.service). A process pair uses the connection -- client and server applications. Two protocols on IP -- TCP (Transmission Control Protocol) and UDP (User Datagram Prototocol). We'll be looking in more detail at TCP services and will not look at UDP at all. TCP services are connection orientated (like a stream, pipe or tty) while UDP services are connectionless (more like telegrams or letters). We recognize many of the services -- SMTP (Simple Mail Transfer Protocol, E-mail), NNTP (Network News Transfer Pro- tocol service, Usenet News), NTP (Network Time Protocol), and SYSLOG is the BSD service implemented by //eettcc//ssyyssllooggdd. The nneettssttaatt display shows many TCP services as EESSTTAABBLLIISSHHEEDD (there is a connection between client.port and server.port) and others in a LLIISSTTEENN state (a server application is lis- tening at a port for client connections). 33 UUWWOO//SSoocckkeett 33.. HHoosstt nnaammeess aanndd IIPP nnuummbbeerrss Hosts have names (eg. jjuulliiaann..uuwwoo..ccaa) but IP addressing is by number (eg. [129.100.2.12]). In the old days name/number translations were tabled in //eettcc//hhoossttss. [[1100::2255aamm ssuunnccoonn]] ppaaggee //eettcc//hhoossttss ## SSuunn HHoosstt DDaattaabbaassee ## 112277..00..00..11 llooccaallhhoosstt 112299..110000..11..7755 ssuunnccoonn..ccccss..uuwwoo..ccaa These days name to number translations are implemented by the Domain Name Service (or DNS) -- see nnaammeedd((88)). [[1100::2266aamm ssuunnccoonn]] ppaaggee //eettcc//rreessoollvv..ccoonnff nnaammeesseerrvveerr 112299..110000..22..1122 nnaammeesseerrvveerr 112299..110000..77..110000 nnaammeesseerrvveerr 112299..110000..22..1133 ddoommaaiinn ccccss..uuwwoo..ccaa [[1100::2266aamm ssuunnccoonn]] nnssllooookkuupp wwhhoohhoosstt SSeerrvveerr:: jjuulliiaann..uuwwoo..ccaa AAddddrreessss:: 112299..110000..22..1122 NNaammee:: jjuulliiaann..uuwwoo..ccaa AAddddrreessss:: 112299..110000..22..1122 AAlliiaasseess:: wwhhoohhoosstt..uuwwoo..ccaa UUWWOO//SSoocckkeett 44 33..11.. PPrrooggrraammmmiinngg CCaallllss Programmers don't scan //eettcc//hhoossttss nor do they communicate with the DNS. The C library routines ggeetthhoossttbbyynnaammee((33)) and ggeetthhoossttbbyyaaddddrr((33)) each return a pointer to an object with the following structure: ssttrruucctt hhoosstteenntt {{ cchhaarr **hh__nnaammee;; //** ooffffiicciiaall nnaammee ooff hhoosstt **// cchhaarr ****hh__aalliiaasseess;; //** aalliiaass lliisstt **// iinntt hh__aaddddrrttyyppee;; //** hhoosstt aaddddrreessss ttyyppee **// iinntt hh__lleennggtthh;; //** lleennggtthh ooff aaddddrreessss **// cchhaarr ****hh__aaddddrr__lliisstt;; //** lliisstt ooff aaddddrreesssseess **// }};; ##ddeeffiinnee hh__aaddddrr hh__aaddddrr__lliisstt[[00]] //** bbaacckkwwaarrdd ccoommppaattiibbiilliittyy **// The structure hh__aaddddrr__lliisstt is a list of IP numbers (recall that a machine might have several interfaces, each will have a number). Good programmers would try to connect to each address listed in turn (eg. some versions of ffttpp). Lazy programmers (like me) just use hh__aaddddrr -- the first address listed. 55 UUWWOO//SSoocckkeett Client applications connect to a host.port (cf. nneettssttaatt out- put) for a service. PPrroottoo RReeccvv--QQ SSeenndd--QQ LLooccaall AAddddrreessss FFoorreeiiggnn AAddddrreessss ((ssttaattee)) ttccpp 00 00 jjuulliiaann..22771177 vvnneett..iibbmm..ccoomm..ssmmttpp EESSTTAABBLLIISSHHEEDD ttccpp 00 1133 jjuulliiaann..nnnnttpp wwaattsseerrvv11..wwaatteerrlloo..33550077 EESSTTAABBLLIISSHHEEDD The connection is usually prefaced by translating a hostname name into an IP number (but if you knew the IP number you could _c_a_r_e_f_u_l_l_y skip that step). iinntt ttccppooppeenn((hhoosstt,,sseerrvviiccee)) cchhaarr **sseerrvviiccee,, **hhoosstt;; {{ ssttrruucctt hhoosstteenntt **hhpp;; _e_t_c_._._. iiff ((((hhpp==ggeetthhoossttbbyynnaammee((hhoosstt)))) ==== NNUULLLL)) _e_r_r_o_r_._._. _C_a_r_e_f_u_l_l_y because the IP address is a structure of 4 octets. Watch out for byte ordering. An uunnssiiggnneedd lloonngg isn't the same octet sequence on all machines. See hhttoonnll((33)) and nnttoohhll((33)) for host to net conver- sions. UUWWOO//SSoocckkeett 66 44.. SSeerrvviicceess aanndd PPoorrttss Services have names (eg. ssmmttpp the Simple Mail Transfer Pro- tocol). Ports have numbers (eg. ssmmttpp is a service on port 25). The mapping from service names to port numbers is listed in //eettcc//sseerrvviicceess. [[11::2222ppmm jjuulliiaann]] ppaaggee //eettcc//sseerrvviicceess ## $$AAuutthhoorr:: rreeggggeerrss $$ ## $$DDaattee:: 11999922//0022//1133 1155::5588::4444 $$ ## ## NNeettwwoorrkk sseerrvviicceess,, IInntteerrnneett ssttyyllee _e_t_c_._._. ffttpp 2211//ttccpp tteellnneett 2233//ttccpp ssmmttpp 2255//ttccpp mmaaiill wwhhooiiss 4433//ttccpp nniiccnnaammee ddoommaaiinn 5533//ttccpp nnaammeesseerrvveerr ddoommaaiinn 5533//uuddpp nnaammeesseerrvveerr ttffttpp 6699//uuddpp ffiinnggeerr 7799//ttccpp nnnnttpp 111199//ttccpp rreeaaddnneewwss uunnttpp nnttpp 112233//uuddpp ssnnmmpp 116611//uuddpp xxddmmccpp 117777//uuddpp xxddmm _e_t_c_._._. 77 UUWWOO//SSoocckkeett 44..11.. PPrrooggrraammmmiinngg CCaallllss The C library routines ggeettsseerrvvbbyynnaammee((33)), and ggeettsseerrvvbbyy-- ppoorrtt((33)) each return a pointer to an object with the follow- ing structure containing the broken-out fields of a line in //eettcc//sseerrvviicceess. ssttrruucctt sseerrvveenntt {{ cchhaarr **ss__nnaammee;; //** ooffffiicciiaall nnaammee ooff sseerrvviiccee **// cchhaarr ****ss__aalliiaasseess;; //** aalliiaass lliisstt **// iinntt ss__ppoorrtt;; //** ppoorrtt sseerrvviiccee rreessiiddeess aatt **// cchhaarr **ss__pprroottoo;; //** pprroottooccooll ttoo uussee **// }};; Client applications connect to a service port. Usually this is prefaced by translating a service name (eg. ssmmttpp) into the port number (but if you knew the port number you could _c_a_r_e_f_u_l_l_y skip that step). iinntt ttccppooppeenn((hhoosstt,,sseerrvviiccee)) cchhaarr **sseerrvviiccee,, **hhoosstt;; {{ ssttrruucctt sseerrvveenntt **sspp;; _e_t_c_._._. iiff ((((sspp==ggeettsseerrvvbbyynnaammee((sseerrvviiccee,,""ttccpp"")))) ==== NNUULLLL)) _e_r_r_o_r_._._. UUWWOO//SSoocckkeett 88 55.. SSoocckkeett AAddddrreessssiinngg A Socket Address is a host.port pair (communication is between host.port pairs). The structure is ssoocckkaaddddrr__iinn, the address family is AAFF__IINNEETT: iinntt ttccppooppeenn((hhoosstt,,sseerrvviiccee)) cchhaarr **sseerrvviiccee,, **hhoosstt;; {{ iinntt uunniitt;; ssttrruucctt ssoocckkaaddddrr__iinn ssiinn;; ssttrruucctt sseerrvveenntt **sspp;; ssttrruucctt hhoosstteenntt **hhpp;; _e_t_c_._._. iiff ((((sspp==ggeettsseerrvvbbyynnaammee((sseerrvviiccee,,""ttccpp"")))) ==== NNUULLLL)) _e_r_r_o_r_._._. iiff ((((hhpp==ggeetthhoossttbbyynnaammee((hhoosstt)))) ==== NNUULLLL)) _e_r_r_o_r_._._. bbzzeerroo((((cchhaarr **))&&ssiinn,, ssiizzeeooff((ssiinn))));; ssiinn..ssiinn__ffaammiillyy==AAFF__IINNEETT;; bbccooppyy((hhpp-->>hh__aaddddrr,,((cchhaarr **))&&ssiinn..ssiinn__aaddddrr,,hhpp-->>hh__lleennggtthh));; ssiinn..ssiinn__ppoorrtt==sspp-->>ss__ppoorrtt;; _e_t_c_._._. The code is filling in the port and host address in the Socket Address structure -- the address of the remote host.port where we want to connect. 99 UUWWOO//SSoocckkeett There's a generic Socket Address structure, a ssoocckkaaddddrr, used for communication in arbitrary domains. //** ffrroomm:: //uussrr//iinncclluuddee//ssyyss//ssoocckkeett..hh **// ssttrruucctt ssoocckkaaddddrr {{ uu__sshhoorrtt ssaa__ffaammiillyy;; //** aaddddrreessss ffaammiillyy **// cchhaarr ssaa__ddaattaa[[1144]];; //** uupp ttoo 1144 bbyytteess ooff ddiirreecctt aaddddrreessss **// }};; The ssoocckkaaddddrr__iinn structure is for Internet Socket Addresses. An instance of the generic socket address. //** ffrroomm:: //uussrr//iinncclluuddee//nneettiinneett//iinn..hh **// ssttrruucctt ssoocckkaaddddrr__iinn {{ sshhoorrtt ssiinn__ffaammiillyy;; //** AAFF__IINNEETT **// uu__sshhoorrtt ssiinn__ppoorrtt;; //** sseerrvviiccee ppoorrtt **// ssttrruucctt iinn__aaddddrr ssiinn__aaddddrr;; //** hhoosstt nnuummbbeerr **// cchhaarr ssiinn__zzeerroo[[88]];; }};; The ffaammiillyy defines the interpretation of the data. In other domains addressing will be different -- services in the UNIX domain are names (eg. //ddeevv//pprriinntteerr). UUWWOO//SSoocckkeett 1100 66.. FFiillee DDeessccrriippttoorrss aanndd SSoocckkeettss 66..11.. FFiillee DDeessccrriippttoorrss File Descriptors are the fundamental I/O object. You rreeaadd((22)) and wwrriittee((22)) to file descriptors. iinntt cccc,, ffdd,, nnbbyytteess;; cchhaarr **bbuuff;; cccc == rreeaadd((ffdd,, bbuuff,, nnbbyytteess));; cccc == wwrriittee((ffdd,, bbuuff,, nnbbyytteess)) The rreeaadd attempts to read nnbbyytteess of data from the object referenced by the file descriptor ffdd into the buffer pointed to by bbuuff. The wwrriittee does a write to the file descriptor from the buffer. Unix I/O is a byte stream. File descriptors are nnuummbbeerrss used for I/O. Usually the result of ooppeenn((22)) and ccrreeaatt((22)) calls. All Unix applications run with ssttddiinn as file descriptor 0, ssttddoouutt as 1, and ssttddeerrrr as 3. But ssttddiinn is a FFIILLEE (see ssttddiioo((33))) not a file descriptor. If you want a stdio stream on a file descriptor use ffddooppeenn((33)). 1111 UUWWOO//SSoocckkeett 66..22.. SSoocckkeettss A Socket is a Unix file descriptor created by the ssoocckkeett((22)) call -- you don't ooppeenn((22)) or ccrreeaatt((22)) a socket. Cf. ppiippee((22)) creates file descriptors. iinntt ss,, ddoommaaiinn,, ttyyppee,, pprroottooccooll;; ss == ssoocckkeett((ddoommaaiinn,, ttyyppee,, pprroottooccooll)) _e_t_c_._._. cccc == rreeaadd((ss,, bbuuff,, nnbbyytteess));; The ddoommaaiinn parameter specifies a communications domain (or address family). For IP use AF_INET. The ttyyppee parameter specifies the semantics of communication. For TCP/IP use SOCK_STREAM (for UDP/IP use SOCK_DGRAM). A SOCK_STREAM is a sequenced, reliable, two-way connection based byte stream. If a data cannot be successfully trans- mitted within a reasonable length of time the connection is considered broken and I/O calls will indicate an error. The pprroottooccooll specifies a particular protocol to be used with the socket -- for TCP/IP use 0. But see //eettcc//pprroottooccoollss to get really confused. UUWWOO//SSoocckkeett 1122 77.. CClliieenntt CCoonnnneecctt A client application creates a ssoocckkeett((22)) and ccoonnnneecctt((22)) to a service. iinntt ttccppooppeenn((hhoosstt,,sseerrvviiccee)) cchhaarr **sseerrvviiccee,, **hhoosstt;; {{ iinntt uunniitt;; ssttrruucctt ssoocckkaaddddrr__iinn ssiinn;; ssttrruucctt sseerrvveenntt **sspp;; ssttrruucctt hhoosstteenntt **hhpp;; iiff ((((sspp==ggeettsseerrvvbbyynnaammee((sseerrvviiccee,,""ttccpp"")))) ==== NNUULLLL)) _e_r_r_o_r_._._. iiff ((((hhpp==ggeetthhoossttbbyynnaammee((hhoosstt)))) ==== NNUULLLL)) _e_r_r_o_r_._._. bbzzeerroo((((cchhaarr **))&&ssiinn,, ssiizzeeooff((ssiinn)))) _e_t_c_._._. iiff ((((uunniitt==ssoocckkeett((AAFF__IINNEETT,,SSOOCCKK__SSTTRREEAAMM,,00)))) << 00)) _e_r_r_o_r_._._. iiff ((ccoonnnneecctt((uunniitt,,&&ssiinn,,ssiizzeeooff((ssiinn)))) << 00)) _e_r_r_o_r_._._. rreettuurrnn((uunniitt));; }} The result returned is a file descriptor. 1133 UUWWOO//SSoocckkeett 77..11.. CClliieenntt CCoommmmuunniiccaattiioonn Having connected a socket to a server to establish a file descriptor communication is with the usual Unix I/O calls. Many programmers turn file descriptors into ssttddiioo((33)) streams so they can use fputs, fgets, fprintf, etc. -- use ffddooppeenn((33)). mmaaiinn((aarrggcc,,aarrggvv)) iinntt aarrggcc;; cchhaarr **aarrggvv[[]];; {{ iinntt uunniitt,,ii;; cchhaarr bbuuff[[BBUUFFSSIIZZ]];; FFIILLEE **ssoocckkiinn,,**ssoocckkoouutt;; iiff ((((uunniitt==ttccppooppeenn((WWHHOOHHOOSSTT,,WWHHOOPPOORRTT)))) << 00)) _e_r_r_o_r_._._. ssoocckkiinn==ffddooppeenn((uunniitt,,""rr""));; ssoocckkoouutt==ffddooppeenn((uunniitt,,""ww""));; _e_t_c_._._. ffpprriinnttff((ssoocckkoouutt,,""WWHHOOIISS %%ss\\nn"",,aarrggvv[[ii]]));; _e_t_c_._._. wwhhiillee ((ffggeettss((bbuuff,,BBUUFFSSIIZZ,,ssoocckkiinn)))) _e_t_c_._._. UUWWOO//SSoocckkeett 1144 77..22.. SSttddiioo BBuuffffeerrss Stdio streams have powerful manipulation tools (eg. ffssccaannff is amazing). But beware, streams are _b_u_f_f_e_r_e_d! This means a well placed fffflluusshh((33)) is often required to flush a buffer to the peer. ffpprriinnttff((ssoocckkoouutt,,""WWHHOOIISS %%ss\\nn"",,aarrggvv[[ii]]));; fffflluusshh((ssoocckkoouutt));; wwhhiillee ((ffggeettss((bbuuff,,BBUUFFSSIIZZ,,ssoocckkiinn)))) _e_t_c_._._. Many client/server protocols are client driven -- the client sends a command and expects an answer. The server won't see the command if the client doesn't flush the output. Likewise, the client won't see the answer if the server doesn't flush it's output. Watch out for client and server blocking -- both waiting for input from the other. 1155 UUWWOO//SSoocckkeett 88.. SSeerrvveerr AApppplliiccaattiioonnss A system offers a service by having an application running that is listening at the service port for a connection. If there is no application listening at the service port then the machine doesn't offer that service. The SSMMTTPP service is provided by an application listening on port 25. On Unix systems this is usually the sseennddmmaaiill appli- cation which is started at boot time. [[22::2200ppmm jjuulliiaann]] ppss --aaggxx || ggrreepp sseennddmmaaiill 441199 ?? SSWW 00::0033 //uussrr//lliibb//sseennddmmaaiill --bbdd --qq1155mm 1188443388 ?? IIWW 00::0011 //uussrr//lliibb//sseennddmmaaiill --bbdd --qq1155mm [[22::2288ppmm jjuulliiaann]] nneettssttaatt --aa || ggrreepp ssmmttpp ttccpp 00 00 jjuulliiaann..33115555 aaccaadd33..aallaasskkaa..eedduu..ssmmttpp SSYYNN__SSEENNTT ttccpp 00 00 **..ssmmttpp **..** LLIISSTTEENN In the example we have a process listening to the ssmmttpp port (for inbound mail) and another process talking to the ssmmttpp port on acad3.alaska.edu (ie. sending mail to that system). UUWWOO//SSoocckkeett 1166 88..11.. SSeerrvveerr BBiinndd A Server uses bbiinndd((22)) to establish the local host.port assignment. Only required for servers -- applications which aacccceepptt((22)) connections from a host.port. ssttrruucctt sseerrvveenntt **sspp;; ssttrruucctt ssoocckkaaddddrr__iinn ssiinn;; iiff ((((sspp==ggeettsseerrvvbbyynnaammee((sseerrvviiccee,,""ttccpp"")))) ==== NNUULLLL)) _e_r_r_o_r_._._. ssiinn..ssiinn__ffaammiillyy==AAFF__IINNEETT;; ssiinn..ssiinn__ppoorrtt==sspp-->>ss__ppoorrtt;; ssiinn..ssiinn__aaddddrr..ss__aaddddrr==hhttoonnll((IINNAADDDDRR__AANNYY));; iiff ((((ss==ssoocckkeett((AAFF__IINNEETT,,SSOOCCKK__SSTTRREEAAMM,,00)))) << 00)) _e_r_r_o_r_._._. iiff ((bbiinndd((ss,, &&ssiinn,, ssiizzeeooff((ssiinn)))) << 00)) _e_r_r_o_r_._._. hhttoonnll converts a long to the right sequence (given different byte ordering on different machines). The IP address IINNAADDDDRR__AANNYY means all interfaces. Client applications usually aren't concerned about the local host.port assignment (the ccoonnnneecctt((22)) does a bind for the local address). But rrccpp, rrllooggiinn, etc. do connect from reserved port numbers. 1177 UUWWOO//SSoocckkeett 88..22.. LLiisstteenn aanndd AAcccceepptt To accept connections, a socket is first created with ssoocckkeett((22)), a queue for incoming connections is specified with lliisstteenn((22)) and then the connections are accepted with aacccceepptt((22)). ssttrruucctt sseerrvveenntt **sspp;; ssttrruucctt ssoocckkaaddddrr__iinn ssiinn,,ffrroomm;; iiff ((((sspp==ggeettsseerrvvbbyynnaammee((sseerrvviiccee,,""ttccpp"")))) ==== NNUULLLL)) _e_r_r_o_r_._._. ssiinn..ssiinn__ffaammiillyy==_e_t_c_._._. iiff ((((ss==ssoocckkeett((AAFF__IINNEETT,,SSOOCCKK__SSTTRREEAAMM,,00)))) << 00)) _e_r_r_o_r_._._. iiff ((bbiinndd((ss,, &&ssiinn,, ssiizzeeooff((ssiinn)))) << 00)) _e_r_r_o_r_._._. iiff ((lliisstteenn((ss,,QQUUEELLEENN)) << 00)) _e_r_r_o_r_._._. ffoorr ((;;;;)) {{ iiff ((((gg==aacccceepptt((ff,,&&ffrroomm,,&&lleenn)))) << 00)) _e_r_r_o_r_._._. iiff ((!!ffoorrkk(()))) {{ _c_h_i_l_d _h_a_n_d_l_e_s _r_e_q_u_e_s_t_._._. eexxiitt((00));; }} cclloossee((gg));; }} This is the programming schema used by utilities like sseenndd-- mmaaiill and llppdd -- they create their socket and listen for con- nections. UUWWOO//SSoocckkeett 1188 99.. IInneettdd SSeerrvviicceess Not all services are started at boot time by running a server application. Eg. you won't usually see a process running for the ffiinnggeerr service like you do for the ssmmttpp ser- vice. Many are handled by the InterNet Daemon iinneettdd((88)). This is a generic service configured by the file //eettcc//iinneettdd..ccoonnff. [[22::3355ppmm jjuulliiaann]] ppaaggee //eettcc//iinneettdd..ccoonnff ## $$AAuutthhoorr:: rreeggggeerrss $$ ## $$DDaattee:: 11999922//0022//1133 1155::5588::4444 $$ ## ## IInntteerrnneett sseerrvveerr ccoonnffiigguurraattiioonn ddaattaabbaassee ffttpp ssttrreeaamm ttccpp nnoowwaaiitt rroooott //uussrr//eettcc//ffttppdd ffttppdd tteellnneett ssttrreeaamm ttccpp nnoowwaaiitt rroooott //uussrr//eettcc//tteellnneettdd tteellnneettdd sshheellll ssttrreeaamm ttccpp nnoowwaaiitt rroooott //uussrr//eettcc//rrsshhdd rrsshhdd llooggiinn ssttrreeaamm ttccpp nnoowwaaiitt rroooott //uussrr//eettcc//rrllooggiinndd rrllooggiinndd eexxeecc ssttrreeaamm ttccpp nnoowwaaiitt rroooott //uussrr//eettcc//rreexxeeccdd rreexxeeccdd uuuuccppdd ssttrreeaamm ttccpp nnoowwaaiitt rroooott //uussrr//eettcc//uuuuccppdd uuuuccppdd ffiinnggeerr ssttrreeaamm ttccpp nnoowwaaiitt nnoobbooddyy //uussrr//eettcc//ffiinnggeerrdd ffiinnggeerrdd _e_t_c_._._. nnnnttpp ssttrreeaamm ttccpp nnoowwaaiitt rroooott //uussrr//lliibb//nneewwssbbiinn//nnnnttppdd nnnnttppdd wwhhooiiss ssttrreeaamm ttccpp nnoowwaaiitt nnoobbooddyy //uussrr//ccccss//lliibb//ddiirreeccttoorryy//rrwwhhooiissdd rrwwhhooiissdd ttnn33227700 ssttrreeaamm ttccpp nnoowwaaiitt nnoobbooddyy //uussrr//ccccss//bbiinn//ttnn33227700 ttnn33227700 aaccccoouunntt ssttrreeaamm ttccpp nnoowwaaiitt nnoobbooddyy //uussrr//ccccss//bbiinn//aaccccoouunnttdd aaccccoouunnttdd 1199 UUWWOO//SSoocckkeett 99..11.. IInneettdd CCoommmmeennttss For each service listed in //eettcc//iinneettdd..ccoonnff the iinneettdd process (this process is started at boot time) executes the socket, bind, listen and accept calls as discussed above. Inetd also handles many of the daemon issues (signal handling, set pro- cess group and controlling tty). IInneettdd spawns the appropriate application (with ffoorrkk((22)) and eexxeecc((22))) when a client connects. The application is started with ssttddiinn and ssttddoouutt connected to the remote port. Any input/output on stdin/stdout are sent/received by the client. This means, any application written to use stdin/stdout can be a server application. Writing a server application should be fairly simple. UUWWOO//SSoocckkeett 2200 99..22.. WWhhooiiss DDaaeemmoonn On julian we have an entry in //eettcc//iinneettdd..ccoonnff for the UWO wwhhooiiss service: [[33::2255ppmm jjuulliiaann]] ggrreepp wwhhooiiss //eettcc//iinneettdd..ccoonnff wwhhooiiss ssttrreeaamm ttccpp nnoowwaaiitt nnoobbooddyy -- //uussrr//ccccss//lliibb//ddiirreeccttoorryy//rrwwhhooiissdd rrwwhhooiissdd This is the UWO wwhhooiiss service (as listed in //eettcc//sseerrvviicceess), on a TCP/IP stream, ran as user nnoobbooddyy, the program to run is listed, and the command line to the program. Note that this is nnoott the standard //uussrr//uuccbb//wwhhooiiss service that talks to nniicc..ddddnn..mmiill. The UWO whois service talks to a different server and implements a different protocol. The program conducts a protocol on stdin/stdout (which is usually connected by a TCP/IP socket to a client applica- tion). 2211 UUWWOO//SSoocckkeett 1100.. RRuunnnniinngg tthhee DDaaeemmoonn You can run the whois daemon (on the server) to see what it does: [[33::2277ppmm jjuulliiaann]] //uussrr//ccccss//lliibb//ddiirreeccttoorryy//rrwwhhooiissdd 222200 DDiirreeccttoorryy SSeerrvviiccee $$RReevviissiioonn:: 11..11 $$ rreeaaddyy aatt jjuulliiaann..uuwwoo..ccaa hheellpp _._._._. _m_y _c_o_m_m_a_n_d 335500 II ddoonn''tt kknnooww mmuucchh bbuutt II ccaann uunnddeerrssttaanndd HHEELLPP,, QQUUIITT,, WWHHOOIISS . wwhhooiiss qquuiinnttoonn _._._._. _m_y _c_o_m_m_a_n_d 335500 MMaattcchheess oonn qquuiinnttoonn ffoollllooww:: RReegg..QQuuiinnttoonn:: RReegg QQuuiinnttoonn <> CCCCSS,,pprrooggrraammmmeerr,,NNSSCC,,221144,,666611--22115511,,66002266,,##1111993300 rreeggggeerrss:: RReegg QQuuiinnttoonn <> CCCCSS,,pprrooggrraammmmeerr,,NNSSCC,,221144,,666611--22115511,,66002266,,##1111993300 . qquuiitt _._._._. _m_y _c_o_m_m_a_n_d 222200 QQuuiitt aacccceepptteedd,, tteerrmmiinnaattiinngg sseessssiioonn [[33::3300ppmm jjuulliiaann]] The program is command driven -- you give commands on stdin, it produces results on stdout. UUWWOO//SSoocckkeett 2222 1100..11.. TThhee CCooddee The program is easy enough: read a line, switch on command and do command. pprriinnttff((""222200 DDiirreeccttoorryy SSeerrvviiccee %%ss rreeaaddyy aatt %%ss\\nn"",, VVEERRSSIIOONN,, nnaammee));; fffflluusshh((ssttddoouutt));; wwhhiillee ((ffggeettss((ssttrriinngg,,BBUUFFSSIIZZ,,ssttddiinn)))) {{ iiff ((iisspprreeffiixx((ssttrriinngg,,""HHEELLPP"")))) pprriinnttff((HHEELLPPMMSSGG));; eellssee iiff ((iisspprreeffiixx((ssttrriinngg,,""QQUUIITT"")))) {{ pprriinnttff((""222200 QQuuiitt aacccceepptteedd,, tteerrmmiinnaattiinngg sseessssiioonn\\nn""));; fffflluusshh((ssttddoouutt));; sslleeeepp((33));; eexxiitt((00));; }} eellssee iiff ((iisspprreeffiixx((ssttrriinngg,,""WWHHOOIISS "")))) {{ ssssccaannff((ssttrriinngg,,""%%**ss%%**[[ ]]%%[[^^ \\rr\\nn]]"",,nnaammee));; pprriinnttff((""335500 MMaattcchheess oonn %%ss ffoollllooww::\\nn"",,nnaammee));; fffflluusshh((ssttddoouutt));; sspprriinnttff((ssttrriinngg,,""%%ss ''%%ss''"",,GGRREEPP,,nnaammee));; ssyysstteemm((ssttrriinngg));; pprriinnttff((""..\\nn""));; }} eellssee pprriinnttff((""555500 ccoommmmaanndd mmaakkeess nnoo sseennssee\\nn""));; fffflluusshh((ssttddoouutt));; }} pprriinnttff((""555500 OOooppss...... yyoouu''vvee ssttooppppeedd ttaallkkiinngg\\nn""));; The protocol is line based. This works well with stdio streams. Also easy to test from a terminal. Compare with line based protocols for NNTP and SMTP. 2233 UUWWOO//SSoocckkeett 1100..22.. CCoonnnneeccttiinngg ttoo tthhee SSeerrvveerr You can make a tteellnneett((11)) connection to the server: [[33::4477ppmm ssuunnccoonn]] ggrreepp wwhhooiiss //eettcc//sseerrvviicceess wwhhooiiss 4433//ttccpp nniiccnnaammee [[33::4477ppmm ssuunnccoonn]] tteellnneett jjuulliiaann 4433 TTrryyiinngg 112299..110000..22..1122 ...... CCoonnnneecctteedd ttoo jjuulliiaann..uuwwoo..ccaa.. EEssccaappee cchhaarraacctteerr iiss ''^^]]''.. 222200 DDiirreeccttoorryy SSeerrvviiccee $$RReevviissiioonn:: 11..11 $$ rreeaaddyy aatt jjuulliiaann..uuwwoo..ccaa hheellpp _._._._. _m_y _c_o_m_m_a_n_d 335500 II ddoonn''tt kknnooww mmuucchh bbuutt II ccaann uunnddeerrssttaanndd HHEELLPP,, QQUUIITT,, WWHHOOIISS . wwhhooiiss qquuiinnttoonn _._._._. _m_y _c_o_m_m_a_n_d 335500 MMaattcchheess oonn qquuiinnttoonn ffoollllooww:: RReegg..QQuuiinnttoonn:: RReegg QQuuiinnttoonn <> CCCCSS,,pprrooggrraammmmeerr,,NNSSCC,,221144,,666611--22115511,,66002266,,##1111993300 rreeggggeerrss:: RReegg QQuuiinnttoonn <> CCCCSS,,pprrooggrraammmmeerr,,NNSSCC,,221144,,666611--22115511,,66002266,,##1111993300 . qquuiitt _._._._. _m_y _c_o_m_m_a_n_d 222200 QQuuiitt aacccceepptteedd,, tteerrmmiinnaattiinngg sseessssiioonn CCoonnnneeccttiioonn cclloosseedd bbyy ffoorreeiiggnn hhoosstt.. [[33::4488ppmm ssuunnccoonn]] UUWWOO//SSoocckkeett 2244 1100..33.. WWhhooiiss CClliieenntt The whois client makes a TCP/IP connection to the server and conducts the kind of protocol you would type if you where to make a connection by hand: [[77::3300aamm zzeebbrraa]] wwhhooiiss qquuiinnttoonn RReegg..QQuuiinnttoonn:: RReegg QQuuiinnttoonn <> CCCCSS,,pprrooggrraammmmeerr,,NNSSCC,,221144,,666611--22115511,,66002266,,##1111993300 rreeggggeerrss:: RReegg QQuuiinnttoonn <> CCCCSS,,pprrooggrraammmmeerr,,NNSSCC,,221144,,666611--22115511,,66002266,,##1111993300 [[77::3300aamm zzeebbrraa]] The client sends the command "WWHHOOIISS qquuiinnttoonn", the server sends back the answer and the client displays the answer to the user. When finished the client sends "QQUUIITT". The server response codes assist in the parsing of the results. The client code is complicated (a bit) by the piping through a pager. 2255 UUWWOO//SSoocckkeett 1111.. FFiinnaall CCoommmmeennttss The wwhhooiiss example uses a line based protocol. The strategy is common but by no means universal. For example, the llppdd protocols use octets (ie. single characters) for the com- mands. Inetd servers are the simplest to implement. However, this may not be optimal. Especially if the server has to do a lot of work first (eg. loading in a big data base). Stand alone servers have to deal with many ddaaeemmoonn issues -- they should ignore most signals, set a unique process group and get rid of the controlling terminal. Daemons like nnnnttpp could (in theory) handle many clients from a single daemon using interrupt driven I/O. As currently implemented we have an nnnnttpp daemon for each client. You'll note that Socket programmers use aallaarrmm((33)), sseettjjmmpp((33)), and ssiiggnnaall((33)) calls. The intent is to prevent a process (client or server) from hanging in a wait for I/O state. UUWWOO//SSoocckkeett 2266 1111..11.. NNoottee WWeellll The best way to code a client/server program is to reuse code from an existing service. There's lots of public domain examples to work from -- nnnnttpp, llppdd, sseennddmmaaiill, and even our wwhhooiiss service. A simple solution that works is much better than a fancy solution that doesn't -- KISS. Protocols have to be simple! Presentation issues, ie. the display for the user, should not effect the protocol or server. Again, protocols have to be simple! Don't ever assume the client or server applications are well behaved! 2277 UUWWOO//SSoocckkeett 1122.. SSuuggggeesstteedd RReeaaddiinngg IInnttrroodduuccttoorryy 44..33BBSSDD IInntteerrpprroocceessss CCoommmmuunniiccaattiioonn, by Stuart Sechrest, (in) UNIX Programmer's Supplementary Documents, Vol1, 4.3 Berkeley Software Distribution, PS1:7. AAddvvaanncceedd 44..33BBSSDD IInntteerrpprroocceessss CCoommmmuunniiccaattiioonn, by Samuel J. Leffler _e_t _a_l, (in) UNIX Programmer's Supplementary Docu- ments, Vol1, 4.3 Berkeley Software Distribution, PS1:8. IInnttrroodduuccttiioonn ttoo tthhee IInntteerrnneett PPrroottooccoollss, Computer Science Facilities Group, Rutgers. (on ~ftp/nic). NNeettwwoorrkkiinngg wwiitthh BBSSDD--ssttyyllee SSoocckkeettss, by John Romkey, (in) Unix World, July-Aug. 1989. HHooww ttoo WWrriittee UUnniixx DDaaeemmoonnss, by Dave Lennert, (in) Unix World, Dec. 1988. AA SSoocckkeett--BBaasseedd IInntteerrpprroocceessss CCoommmmuunniiccaattiioonnss TTuuttoorriiaall, Chpt. 10 of SunOS Network Programming Guide. AAnn AAddvvaanncceedd SSoocckkeett--BBaasseedd IInntteerrpprroocceessss CCoommmmuunniiccaattiioonnss TTuuttoo-- rriiaall, Chpt. 11 of SunOS Network Programming Guide. UUWWOO//SSoocckkeett 2288