Introduction
A\"socket\"isaloosetermusedtodescribe\"anendpointforcommunication.\"ThetraditionalBerkleySocketAPIisasetofCfunctioncallsusedtosupportnetworkcommunication.TheSocketsAPIisnotspecifictoTCP/IP.Therefore,developingTCP/IPnetworkapplicationsrequiresslightlymoreoverheadofprogrammingandunderstandingtoaccountforthegenericparametersofthelibrary'sfunctioncalls.Onceunderstood,Socketprogrammingisaseasyasreadingandwritingtodiskfiles.
ThematerialpresentedhereissomewhatspecifictoC,UNIX,andTCP/IP.HoweverthegeneralformatfortheSocketAPIhasbeenportedtosuchlanguagesasJavaandPerl.InadditionsomeUNIXvendorssupportotherprotocolssuchasIPX,SNA,andDEC-NETwiththeirsocketlibraries.TheMicrosoftWindowsversionoftheSocketAPI(\"WinSock\")isalsoverysimilar.It'sbelievedthatonceasoftwaredevelopergainsagoodunderstandingofUNIX/CSockets,heorshecanquicklyunderstandimplementationsforotherlanguagesandoperatingsystems.
IncludeFiles
WhenwritingCorC++programsthatusethesocketlibrarygoaheadandincludealltheseheaderfiles:
UNIX:
#include CompilingandLinking UndermostversionsofUNIX(Linux,BSD,SunOS,IRIX)compilingisdoneasusual:gccmy_socket_program.c-omy_socket_program However,Solarisrequiresthedevelopertoexplicitlylinkthesocketandnetworkserviceslibrarywiththeprogram: ccmy_socket_program-omy_socket_program-lsocket-lnsl TheSolarisCcompilerthatisusuallylocatedin/opt/SUNWspro/bin,isrecommendedovergcc. ApplicationsandTCP/IP ProgramswrittenbyasoftwaredevelopermayuseeitherTCPorUDPforcommunicatingwithremotehostsontheInternet.BothareservicesthatworkontopoftheIPnetworkprotocol.TCPisareliable\"streams\"servicethatrequiresaconnectionestablishmentphasebetweenahostmakinganactiveconnectiontoaremoteserverhostmakingapassiveone.UDPisanunreliabledatagramserviceanddoesnotrequireanyconnectionestablishmentbeforesending.ThegeneralorderoflibrarycallsforaUDPcommunicationsessionisasfollows:socket()bind() sendto()and/orrecvfrom()close() ForTCPclients,theorderoflibrarycallsisasfollows:socket()bind()connect() send()and/orrecv()close() ForTCPserverprograms,theorderoflibrarycallsisasfollows:socket()bind() listen()accept() send()and/orrecv()close() SocketAddressStructures Fromanapplicationprogrammingpointofview,theonlydifferencesbetweennetworkprotocolsaretheaddressschemesused.Otherwise,operationssuchasconnect,send,receive,anddisconnectareprobablytheonlythingsadeveloperhastothinkaboutwhendesigninganetworkapplication.ForTCP/IP,anidealAPIwouldbeonethatunderstoodIPaddressesandportnumbers.Sincethesocketlibraryisdesignedtobeusedformultipleprotocols,addressesarereferencedbyageneric structureasfollows:structsockaddr{ unsignedshortsa_family;charsa_data[14];};Thesa_familyfieldspecifiesthetypeofprotocol.ForTCP/IP,thisfieldisalwayssettoAF_INET.Theremaining14bytes(sa_data)ofthisstructurearealwaysprotocoldependent.ForTCP/IP,IPaddressesandportnumbersareplacedinthisfield.Tofacilitateoperatingwiththesefields,aspecifictypeofsocketaddressstructureisusedinsteadoftheoneabove.structsockaddr_in{shortsin_family;unsignedshortsin_port;structin_addrsin_addr;charsin_zero[8];}; Ifit'snotalreadyapparent,thesestructuresarecompatiblewitheachother.Theybothare16bytesinsize.Itisalsoreadilyseenthatthefirsttwobytesofeachstructurearethefamilyfield.Thus,astructsockaddr_incanalwaysbecasttoastructsockaddr.Asockaddr_instructurecontainsanin_addrstructureasamemberfield.Ithasthefollowingformstructin_addr{ unsignedlongs_addr;}; Browsingtheheaderfilerevealsthatthisreallyisn'ttheformofthestructure.It'sreallyaverycomplicateduniondesignedtoholdanIPaddressinavarietyofways.Regardless,thein_addrstructisexactly4byteslong,whichisthesamesizeasanIPaddress.Inthesockaddr_instructure,thesin_portfieldisa16-bitunsignedvalueusedtorepresentaportnumber.It'simportanttorememberthatthesefieldsalwaysneedtobesetandinterpretedinnetworkbyteorder.Forexample: structsockaddr_insin;sin.sin_family=AF_INET;sin.sin_port=htons(9999) sin.sin_addr.s_addr=inet_addr(\"128.227.224.3\"); Intheabovecodeexample,thestructuresin,holdstheIPaddress,128.227.224.3,andreferencestheportnumber9999.Twoutilityfunctionsareusedtosetthesevalues.Thefunctionhtonsreturnstheintegerargumentpassedintoitinnetworkbyteorder.Thefunctioninet_addrconvertsthestringargumentfromadotted-quadintoa32-bitinteger.Itsreturnvalueisalsoinnetworkbyteorder. Thestructureabovecouldbeusedtoreferenceahostandapplicationinwhichadatagramistobedelivered.Theusesofthesockaddr_instructurewillbecoveredinmoredetailbelow. socket Thesocketlibrarycallhasthefollowingprototype:intsocket(intfamily,inttype,intprotocol); Inshort,thisfunctioncreates\"anendpointforcommunication\".Thereturnvaluefromthisfunctionisahandletoasocket.Thisnumberispassedasaparametertoalmostalloftheotherlibrarycalls. SincethefocusofthisdocumentisonTCP/IPbasedsockets,thefamilyparametershouldbesettoAF_INET.ThetypeparametercanbeeitherSOCK_STREAM(forTCP),orSOCK_DGRAM(forUDP).Theprotocolfieldisintendedforspecifyingaspecificprotocolincasethenetworkmodelsupportdifferenttypesofstreamanddatagrammodels.However,TCP/IPonlyhasoneprotocolforeach,sothisfieldshouldalwaysbesetto0.Examples: TocreateaUDPsocket: ints; s=socket(AF_INET,SOCK_DGRAM,0);TocreateaTCPsocket: ints; s=socket(AF_INET,SOCK_STREAM,0); bind Beforesendingandreceivingdatawithasocket,itmustfirstbeassociatedwithalocalsourceportandanetworkinterfaceaddress.ThemappingofasockettoaTCP/UDPsourceportandIPaddressiscalleda\"binding\". Itmaybethecasewherethesocketisbeingusedasaserver,andthusmustbeabletolistenforclientrequestsonaspecificport.Itcanalsobethecasethataclientprogramdoesn'tneedaspecificsourceport,sinceallit'sconcernedaboutdoingissendingandreceivingmessageswitha specificportontheremotehost. Furthercomplicationsarisewhentherearemorethanonenetworkdevicesonthehostrunningtheprogram.Sothequestionofsendingthrough\"whichnetwork\"mustbeansweredaswell.Thebindfunctioncallisusedtodeclarethemappingbetweenthesocket,theTCP/UDPsourceport,andthenetworkinterfacedevice.Theprototypeforbindisasfollows: bind(intsocket,structsockaddr*address,intaddress_length); Thefirstargumentisasockethandle(thenumberreturnedfromthesocketfunctioncall).Thesecondargumentisasocketaddressstructure.WithTCP/IP,thisisreallyasockaddr_instructure.Thesin_portfieldoftheaddressargumentisthelocalsourceportnumberassociatedwiththissocket.Thatis,forevery\"send\"operationwiththissocket,thesourceportfieldintheTCP/UDPheadergetssetwiththisvalue.Ifspecifyinganexactsourceportisnotrequired,settingthisvaluetoINADDR_ANY(0)allowstheoperatingsystemtopickanyavailableportnumber.Thesin_addrfieldspecifieswhichnetworkinterfacedevicetouse.SincemosthostsonlyhaveonenetworkinterfaceandonlyoneIPaddress,thisfieldshouldbesetwiththehost'sownIPaddress.However,thesocketlibraryprovidesnoimmediatewayofforahosttodetermineit'sownIPaddress!However,specifyingthevalueofINADDR_ANY(0)inthisfieldtellstheoperatingsystemtopickanyavailableinterfaceandaddress. Theaddressofthesockaddr_instructureispassedintothebindcall,sothatthesocketwillnowbereadytocommunicatewithremotehosts.Thethirdparameterpassedtobindisthelengthofthesockaddr_instructure.Example: structsockaddr_insin;ints; s=socket(AF_INET,SOCK_DGRAM,0);sin.sin_family=AF_INET;sin.sin_port=htons(9999); sin.sin_addr.s_addr=INADDR_ANY;bind(s,(structsockaddr*)&sin,sizeof(sin)); /*sisnowausableUDPsocket.Sourceportis9999*/ Itisrecommendedthatthereturnfrombindbechecked.Bindwillfailbyreturning-1iftheportthatisbeingrequestedforuseisalreadytaken.WhenbindiscalledonaUDPsocket,thesocketisnowreadytosendandreceivedatagrams.ForTCPsockets,thesocketisnowreadyforthe connectoracceptcalls.UDPSockets OnceaUDPsockethasbeencreatedandboundtoalocalsourceport,itisnowcapableofbeingusedforsendingandreceivingdatagrams.Thefunctionsforsendingandreceivingdatagramsaresendtoandrecvfom.Sendtohasthefollowingprototype: intsendto(intsocket,char*buffer,intlength,intflags,structsockaddr*destination_address,intaddress_size); WheresocketisaUDPsocketthathasbeencreatedandboundtoasourceport.bufferisapointertoanarrayofbytesthataretobesentoverthenetwork.Thelengthfieldspecifieshowlongthisarrayis.Theflagsfieldisnormally0. Thedestinationaddressisalsoasockaddrstructure.Asockaddr_instructurecanbecastedintothisfield.Usethesin_addrfieldtospecifythedestinationIPaddressandsin_portforthedestinationport.Forexample: structsockaddr_insin;sin.sin_family=AF_INET; sin.sin_port=htons(12345);//htonsfornetworkbyteordersin.sin_addr.s_addr=inet_addr(\"128.227.22.43\");char*msg=\"Hello,World\"; sendto(s,msg,strlen(msg)+1,0,(structsockaddr*)sin,sizeof(sin)); Intheaboveexample,sisassumedtobeacreatedUDPsocketthathasalreadyboundtoalocalport.Whensendtoiscalled,aUDPdatagramissenttothehostat128.227.22.43.It'sassumedthereisaprocesswithasocketboundtoport12345waitingonarecvfromcalltoreceivethecontentsofthemessagebeingsent.Thesendtofunctionreturnsthenumberofbytessent,or-1ifanerroroccurred.WithUDPsockets,it'snotusuallynecessarytochecktoseehowmanybytesweresentbecausethisinformationisspecifiedinthelengthfield.Recvfromhasthefollowingprototype: intrecvfrom(intsocket,char*buffer,intlength,intflags,structsockaddr*sender_address,int*address_size); Recvfromissimilartosendto.Bufferisapointertoabytearraythatistobefilledwiththecontentsofthedatagram.Thelengthargumentspecifiesthemaximumlengthtocopyintobuffer.Thisistopreventbufferover-runerrorsincasethedatagramislargerthanexpected.Theflagsfieldisnormally0.Thesender_addressargumentisapointertoasocketaddressstructurethatgetsfilledwithacopyofthesender'sIPaddressandsourceport.Theaddress_sizeparametermustbeinitializedtothesizeofthesockaddrstructurebeingused.Onreturnitwillholdthenumberofbytesthatwerecopiedintothesender_addressstructure. Recvfromreturnsthenumberofbytescopiedintothebytearraypointedtobybuffer.Ifthebufferspacespecifiedinlengthislessthanthatoftheoriginaldatagram,onlylengthbyteswillbecopiedintobuffer,andtherestwillbelost.Forexample:structsockaddr_insin;charmsg[10000];intret;intsin_length; sin_length=sizeof(sin); ret=recvfrom(s,msg,10000,0,(structsockaddr*)sin,&sin_length);printf(\"%dbytesreceivedfrom%s(port%d)\\n\ret,inet_ntoa(sin.sin_addr),sin.sin_port); Intheaboveexample,recvfromwillwaituntilitreceivesadatagramonthelocalportassociatedwiththesockets.Theprintfstatementwilllistinformationregardingthesize,sourceIPaddress,andsourceportofthedatagramreceived. Foranyopensocketthathasbeensuccessfullybindedtoaport,theapplicationmaycallsendtoandrecvfromusingthatsocketasmanytimesasitneedsto. Fragmentationiscompletelytransparenttotheapplicationsthataresendingandreceivingdatagrams. TCPSockets listen()/accept()send()recv() close Whenthedatatransfersessionisover,simplycallcloseonthesocketasyouwouldafile: close(s);//sisacreatedsocket ForUDPsockets,thiswillreleasetheownershiponthelocalportthatisboundtothissocket.ForTCP,thiswillinitiateatwo-wayshutdownbetweenbothhostsbeforegivingupportownership.IfaTCPsocketcallsclose,anypendingorsubsequentrecvcallsbytheremotehostwillresultinrecvreturning0toindicateaconnectionshutdownontheotherendhasoccurred.Attemptingtocallsendonasocketthatisconnectedtoahostthathascalledclosewillresultinsendreturning-1.Unlessit'sknownapriorithattheremotehosthasonlycalledshutdown,itisrecommendedthattheapplicationcallcloseonit'ssocketsothattheTCPconnectionwillbeproperlyterminatedonbothsides.shutdown TCPsocketscanalsoengageinahalf-closeoperationusingtheshutdownfunctioncall.It'sprototypeisasfollows:shutdown(intsocket,inthow); Ifthehowfieldis0,thiswilldisallowfurtherreading(recv)fromthesocket.Ifthehowfieldis1,subsequentwrites(send)willbedisallowed.Thesocketwillstillneedtobepassedtoclose.RelationshipBetweenSocketsandFileDescriptors Sockethandlesareintegervalues.InUNIX,sockethandlescanbepassedtomostofthelow-levelPOSIXI/Ofunctions.Forexample:read(s,buffer,buffer_len); Intheaboveexample,scouldbeeitherasocketorfilehandle.Callingreadonanopensocketisequivalenttorecvandrecvfrom.However,ifthesocketisUDP,theninformationaboutthesenderofthedatagramwillnotbereturned.Similarlythewritefunctioncallisequivalenttosendandsendto.UDPsocketsmaycallconnecttousesendandwrite.It'salwaysrecommendedthatthesocketlibraryfunctionsbeusedinsteadofthefileI/Oequivalents.UtilityFunctions Thereareseverallibrarycallsthatarenotactuallypartofthesocketlibraryfamily,butareneverthelessusedinsocketprogramming.Belowisabriefdescriptionofeach. unsignedintinet_addr(char*str);IfthestringcontainedinstrrepresentsanIPaddressitdotted-quadnotation,inet_addrwillreturnit'sequivalent32-bitvalueinnetworkbyteorder.Thisvaluecanbepassedintothesin_addr.s_addrfieldofasocketaddr_instructure.Ifthestringcannotbeinterpretedasadotted-quad,-1isreturned(castedasanunsignedinteger). char*inet_ntoa(structin_addrip);Convertsthe32-bitvaluewhichisassumedtobeinnetworkbyteorderandcontainediniptoastring.Thepointerreturnedbyinet_ntoacontainsthisstring.However,subsequentcallstoinet_ntoawillalwaysreturnthesamepointer,socopyingthestringtoanotherbufferisrecommendedbeforecallingagain. intgethostname(char*name,intlength);Copiesthename(uptolengthbytes)ofthehostnameofthelocalcomputerintothecharacterarraypointedtobyname.structhostent*gethostbyname(char*strHost);IfthestringcontainedinstrHostrepresentsahostname(suchas\"rain\"or\"rain.cise.ufl.edu\"),gethostbynamewillreturnapointertoahostentstructurecontainingadditionalinformationaboutthehostincludingadditionalnamesandIPaddressesassociatewiththathost.GethostbynamewilldoesalltheworkoflookingupaddressentriesinlocaldatabasefilesaswellasmakingDNSqueries.NULLisreturnedifthehostnameisunknown.Theformatforthehostentstructureisasfollows: structhostent{ char*h_name;/*officialnameofhost*/char**h_aliases;/*aliaslist*/ shorth_addrtype;/*hostaddresstype*/shorth_length;/*lengthofaddress*/char**h_addr_list;/*listofaddresses*/ #defineh_addrh_addr_list[0]/*address,forbackwardcompat*/}; Inshort,thefirstIPaddressiscontainedwithinthefirst4bytesofthefirstentryinh_addr_list.h_addrcanbeusedtoreferencethisvalue.Usinggethostbynameandinet_addr,averygoodresolverfunctioncanbewrittentoconvertstringstheusertypesasInternetaddressesintoequivalent32-bitnumbersforsocketcalls.unsignedintresolve(char*ip_addr){ structhostent*hp;unsignedintip; hp=gethostbyname(ip_addr);if(!hp){ ip=inet_addr(ip_addr);if((int)ip==-1)return-1;elsereturnip;} //hp->h_lengthshouldequalto4memcpy(&ip,hp->h_addr,4);returnip;} unsignedlonghtonl(unsignedlongul); unsignedlongntohl(unsignedlongul);unsignedshortntohs(unsignedshortus);unsignedshorthtons(unsignedshortus); Thesefunctionsareveryusefulforconvertingintegervaluestoandfromnetworkbyteorder.Onbig-endianmachinessuchasSunSparcsandMotorolaprocessors,thesefunctionssimplyreturnthevaluepassedasanargument.OnlittleendianmachinessuchastheIntelx86andanysystemrunningWindowsNT,thesecallswillperformbyteswappingoperations.Onmostmachines,htonsisequivalenttontohs.Thismaynotbetrueforfuture64-bitsystemsorotherarchitectures.intselect(intnfds,fd_set*readfds,fd_set*writefds,fd_set*exceptfds,conststructtimeval*timeout);Whenanapplicationcallsrecvorrecvfromitisblockeduntildataarrivesforthatsocket.Anapplicationcouldbedoingotherusefulprocessingwhiletheincomingdatastreamisempty.Anotherpotentialproblemsituationiswhenanapplicationreceivesdatafrommultiplesockets.Callingrecvorrecvfromonasocketthathasnodatainit'sinputqueuepreventsimmediatereceptionofdatafromothersockets.Theselectfunctioncallsolvesthisproblembyallowingtheprogramtopollallthesockethandlestoseeiftheyareavailablefornon-blockingreadingandwritingoperations. Adescriptionoftheargumentsinselectisasfollows: nfds-Somesocketimplementationsignorethisargument.It'svalueshouldbeequalto1+(thesockethandlewiththehighestvalue.) readfds-Apointertoasetoffileandsocketdescriptorsthataretobepolledfornon-blockingreadingandwritingoperations.CanbeNULLtoindicateanemptyset. writefds,exceptfds-Sameasreadfds,exceptthesesetscontainthefile/sockethandlestopollfornon-blockingwritingoperationsanderrordetection.CanbeNULLtoindicateanemptyset.timeout-Apointertoatimevalstructthatspecifieshowlongtheselectcallshouldpollthe descriptorsforanavailableI/Ooperation.Ifthetimeoutvalueis0,thenselectwillreturnimmediately.IfthetimeoutargumentisNULL,thenselectwillblockuntilatleastonefile/sockethandleisreadyforanavailableI/Ooperation.OtherwiseselectwillreturnaftertheamountoftimeinthetimeouthaselapsedORwhenatleastonefile/socketdescriptorisreadyforanI/Ooperation.ThereturnvaluefromselectisthenumberofhandlesspecifiedinthefiledescriptorsetsthatarereadyforI/O.Ifthetimelimitspecifiedbythetimeoutfieldisreached,selectreturn0.Thefollowingmacrosexistformanipulatingafiledescriptorset: FD_CLR(s,*set)Removesthedescriptorsfromset. FD_ISSET(s,*set)Nonzeroifsisamemberoftheset,zerootherwise.FD_SET(s,*set)Addsdescriptorstoset. FD_ZERO(*set)InitializesthesettotheNULLset.Example:fd_setfds; structtimevaltv; //sockisanintializedsockethandletv.tv_sec=2; tv.tv_usec=500000; //tvnowrepresents2.5seconds FD_ZERO(&fds); FD_SET(sock,&fds);//addssocktothefiledescriptorset/*wait2.5secondsforanydatatobereadfromanysinglesocket*/select(sock+1,&fds,NULL,NULL,&tv);if(FD_ISSET(sock,&fds)) recvfrom(s,buffer,buffer_len,0,&sa,&sa_len);else /*dosomethingelse*/ Conclusions Developerswhousethefunctioncallsdescribedinthisdocumentshouldalwayscheckthereturnvalueforeach.ConsultingtheUNIXon-linemanualpages(\"man\")foracompletedescriptionofeachfunctioncallisrecommendedaswell. 因篇幅问题不能全部显示,请点此查看更多更全内容