《TCP通信程序设计.docx》由会员分享,可在线阅读,更多相关《TCP通信程序设计.docx(19页珍藏版)》请在taowenge.com淘文阁网|工程机械CAD图纸|机械工程制图|CAD装配图下载|SolidWorks_CaTia_CAD_UG_PROE_设计图分享下载上搜索。
1、TCP/IP 通信程序设计1、试验目的初步把握 C 语言 TCP/IP 通信程序的设计。2、试验环境1、Windows2022/NT/XP 操作系统。2、TCP/IP 协议。3、编程工具:MicrosoftVisualC+2022。3、相关学问3.1 TCP/IP 协议族表 1TCP/IP 协议族应用层FTP,DNS, ,TELNET,SMTP 等TCPUDPICMPIGMPIPARPRARP网络接口层TCP 具有以下特点:1、面对连接。端到端的 TCP 连接会关注连接的状态,而网络的中间路由器只关心 IP 分组的转发。2、牢靠数据传递。TCP 使用挨次号、承受直接应答方式,并在必要时通过重传
2、来保证发自源端的数据能成功地被传递到目的地。3、流量掌握。接收方向发送方发送一个接收窗口值,告知发送方接收方能够处理多少数据。在收到接收方发来的应答前,TCP 发送方最多只能发送等于该窗口值的数据量。4、拥塞掌握。用于防止 TCP 发送方发送的信息量超过网络中链路或路由器的最大处理力量。流量掌握和拥塞掌握结合起来,使得TCP 主机能快速而公正地调整其发送速率,10/19以到达与网络及接收方的处理力量相匹配。3.2 端口与 Socket在进程通信的意义上,网络通信的最终地址不仅网络层供给的 IP 地址,还应包括描述进程的协议端口protocolport 。假设没有端口,传输层就无法知道数据应当交
3、付给应用层的哪个进程。因此,端口标示了应用层的进程。TCP 和 UDP 分别供给了 216个不同的端口值。端口分为两类:1、周知端口well-knowport ,其值为 0-1023,由 ICANN 负责安排见 RFC1700。其中 TCP 和 UDP 均规定小于 256 的端口作为保存端口。2、临时端口,也称本地安排。进程需要访问传输效劳时,向本地操作系统提出动态申请,操作系统返回一个本地唯一的端口号,进程通过适宜的系统调用将自己和相应的端口号联系起来。Socket 由 4BSDUNIX 首先提出,目的是解决网络通信问题。Socket 的英文原义是“插座”,Socket 与 交换机的插座格外
4、类似,进程通信前,双方各创立一个端点,每一个 Socket 有一个本地唯一的 Socket 号,由操作系统安排。Socket 与 IP 地址、IP 地址的关系如图 1 所示。IP地址端口号202.114.22.98080202.114.22.98080Socket图 1Socket 与 IP 地址、端口号由于 TCP 面对连接的特性,假设多台主机或一台主机的多个进程连接同一台效劳器,则必需创立多个连接,如图 2 所示。端口8080连接1202.113.121.168112.113.111.16端口8084连接23端口8080接连端口8080112.113.111.16图 2 与同一台主机建立三
5、个连接TCP/IP 标准指定了一个概念层接口,包含了一系列过程和函数。标准建议了每个过程和函数所需要的参数及其所执行操作的语义,但没有进一步指定数据表示的细节。具体的接口通常由操作系统来定义,只要完成 TCP/IP 标准中的功能,可以有不同的细节选择。这样,不同的操作系统的应用程序编程接口是各不一样的。例如,广泛使用的 BerkeleySoftwareDistributionUNIX 的 Socket 接口、Windows 的接口定义 Winsock、SystemV 的接口定义 TLI 接口等。3.3 Socket 的操作方式Socket 有两种主要的操作方式:面对连接和面对无连接。面对连接的
6、 BSDUNIX Socket 的工作流程如图 3 所示,而面对无连接的 BSDUNIXSocket 的工作流程如图4 所示。到底用哪种模式是由应用程序的需要打算的。假设要求牢靠性,用面对连接的操作就会好一些。对于面对无连接的 C/S 模式,Socket 不需要连接目的地的 Socket,它只是简洁地投出数据报。无连接的操作简洁高效,但数据的安全性不佳。socketbindlisten客户机acceptsocket堵塞,等待客户连接恳求建立连接connect恳求数据readwrite处理恳求write应答数据readcloseclose效劳器图 3 面对连接的 C/S 时序图socketbin
7、d客户机listensocket堵塞,等待客户连接恳求建立连接connect恳求数据readwrite处理恳求write应答数据readcloseclose效劳器图 4 面对无连接的 C/S 时序图4、TCP 通信程序设计4.1 编程要点由于 TCP 协议要求效劳器和客户端建立连接,所以效劳器需要通过 Listen 方法监听客户端的恳求。当客户端发出连接恳求后,效劳器在 ConnectionRequest 大事中调用 Accept 方法承受恳求,从而与客户端建立连接。只有双方建立连接后,才能进展数据的收发。假设在通信过程中任一方断开连接,则通信过程终止。4.2 客户端程序客户端程序遵循以下步骤
8、: 1建立客户端 Socket 连接。2得到 Socket 读和写的流。3操作流。4关闭流。 5关闭 Socket。客户端的源程序代码如下:/ModuleName:Client.c/Description:/Thissampleistheechoclient.ItconnectstotheTCPserver,/sendsdata,andreadsdatabackfromtheserver./Compile:/cl-oClientClient.cws2_32.lib/CommandLineOptions:/client-p:x-s:IP-n:x-o/-p:xRemoteporttosendto/
9、-s:IPServer”sIPaddressorhostname/-n:xNumberoftimestosendmessage/-oSendmessagesonly;don”treceive/ #include#include #include#pragmacomment(lib,“ws2_32“) #defineDEFAULT_COUNT20#defineDEFAULT_PORT5150#defineDEFAULT_BUFFER2048#defineDEFAULT_MESSAGE“Thisisatestoftheemergency broadcastingsystem“charszServe
10、r128,/Servertoconnectto szMessage1024;/MessagetosendtoseverintiPort=DEFAULT_PORT;/Portonservertoconnectto DWORDdwCount=DEFAULT_COUNT;/Numberoftimestosendmessage BOOLbSendOnly=FALSE;/Senddataonly;don”treceive/Function:usage:/Description:/Printusageinformationandexit/ voidusageprintf(“usage:client-p:x
11、-s:IP-n:x-onn“); printf(“-p:xRemoteporttosendton“);printf(“-s:IPServer”sIPaddressorhostnamen“); printf(“-n:xNumberoftimestosendmessagen“); printf(“-oSendmessagesonly;don”treceiven“); ExitProcess(1);/Function:ValidateArgs/Description:/Parsethecommandlinearguments,andsetsomeglobalflags/toindicatewhata
12、ctionstoperform/ voidValidateArgs(intargc,char*argv)inti;for(i=1;i3)iPort=atoi(&argvi3); break;case”s”:/Server if(strlen(argvi)3)strcpy(szServer,&argvi3); break;case”n”:/Numberoftimestosendmessage if(strlen(argvi)3)dwCount=atol(&argvi3); break;case”o”:/Onlysendmessage;don”treceive bSendOnly=TRUE;bre
13、ak; default:usage; break;/Function:main/Description:/Mainthreadofexecution.InitializeWinsock,parsethe/commandlinearguments,createasocket,connecttothe/server,andthensendandreceivedata./ intmain(intargc,char*argv)WSADATAwsd;SOCKETsClient;charszBufferDEFAULT_BUFFER;intret,i; structsockaddr_inserver; st
14、ructhostent*host=NULL;/ParsethecommandlineandloadWinsock/ ValidateArgs(argc,argv);if(WSAStartup(MAKEWORD(2,2),&wsd)!=0)printf(“FailedtoloadWinsocklibrary!n“); return1;strcpy(szMessage,DEFAULT_MESSAGE);/Createthesocket,andattempttoconnecttotheserver/ sClient=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); i
15、f(sClient=INVALID_SOCKET)printf(“socketfailed:%dn“,WSAGetLastError); return1;server.sin_family=AF_INET; server.sin_port=htons(iPort); server.sin_addr.s_addr=inet_addr(szServer);/Ifthesuppliedserveraddresswasn”tintheform/“aaa.bbb.ccc.ddd“it”sahostname,sotrytoresolveit/ if(server.sin_addr.s_addr=INADD
16、R_NONE)host=gethostbyname(szServer); if(host=NULL)printf(“Unabletoresolveserver:%sn“,szServer); return1;CopyMemory(&server.sin_addr,host-h_addr_list0, host-h_length);if(connect(sClient,(structsockaddr*)&server, sizeof(server)=SOCKET_ERROR)printf(“connectfailed:%dn“,WSAGetLastError); return1;/Sendand
17、receivedata/ for(i=0;idwCount;i+)ret=send(sClient,szMessage,strlen(szMessage),0); if(ret=0)break; elseif(ret=SOCKET_ERROR)printf(“sendfailed:%dn“,WSAGetLastError); break;printf(“Send%dbytesn“,ret); if(!bSendOnly)ret=recv(sClient,szBuffer,DEFAULT_BUFFER,0); if(ret=0)/Gracefulclosebreak; elseif(ret=SO
18、CKET_ERROR)printf(“recvfailed:%dn“,WSAGetLastError); break;szBufferret=”0”; printf(“RECV%dbytes:”%s”n“,ret,szBuffer);closesocket(sClient);WSACleanup; return0;下面解释与 TCP 套接字编程相关的内容。1、加载 Winsock 库WSAStartup(MAKEWORD(2,2),&wsd);其中,第一个参数是 Winsock 的版本 2.2,其次个参数承受返回的库版本信息。2、创立套接字socket(AF_INET,SOCK_STREAM,
19、IPPROTO_TCP);该调用要接收三个参数:af、type、protocol。参数 af 指定通信发生的区域, UNIX 系统支持的地址族有:AF_UNIX、AF_INET、AF_NS 等,而 DOS、WINDOWS 中仅支持 AF_INET,它是网际网区域。因此,地址族与协议族一样。参数 type 描述要建立的套接字的类型。参数 protocol 说明该套接字使用的特定协议,假设调用者不期望特别指定使用的协议,则置为 0,使用默认的连接模式。依据这三个参数建立一个套接字,并将相应的资源安排给它,同时返回一个整型套接字号。因此, socket系统调用实际上指定了相关五元组中的“协议”这一元
20、。3、建立套接字连接connect用于建立连接。无连接的套接字进程也可以调用 connect,但这时在进程之间没有实际的报文交换,调用将从本地操作系统直接返回。这样做的优点是程序员不必为每一数据指定目的地址,而且假设收到的一个数据报,其目的端口未与任何套接字建立“连接”,便能推断该端口不行操作。connect的调用格式如下:connect(SOCKETs,conststructsockaddrFAR*name,intnamelen);参数 s 是欲建立连接的本地套接字描述符。参数 name 指出说明对方套接字地址构造的指针。对方套接字地址长度由 namelen 说明。假设没有错误发生,conn
21、ect返回 0。否则返回 SOCKET_ERROR。在面对连接的协议中,该调用导致本地系统和外部系统之间连接实际建立。由于地址族总被包含在套接字地址构造的前两个字节中,并通过 socket调用与某个协议族相关。因此 bind和 connect无须协议作为参数。4、数据传输 -send 与 recv当一个连接建立以后,就可以传输数据了。常用的系统调用有 send和 recv。send调用用于在参数 s 指定的已连接的数据报或流套接字上发送输出数据,格式如下:send(SOCKETs,constcharFAR*buf,intlen,intflags);参数 s 为已连接的本地套接字描述符。buf
22、指向存有发送数据的缓冲区的指针, 其长度由 len 指定。flags 指定传输掌握方式,如是否发送带外数据等。假设没有错误发生,send返回总共发送的字节数。否则它返回 SOCKET_ERROR。recv调用用于在参数 s 指定的已连接的数据报或流套接字上接收输入数据,格式如下:recv(SOCKETs,charFAR*buf,intlen,intflags);参数 s 为已连接的套接字描述符。buf 指向接收输入数据缓冲区的指针,其长度由 len 指定。flags 指定传输掌握方式,如是否接收带外数据等。假设没有错误发生, recv返回总共接收的字节数。假设连接被关闭,返回0。否则它返回SO
23、CKET_ERROR。5、关闭套接字 -closesocketclosesocket关闭套接字 s,并释放安排给该套接字的资源;假设 s 涉及一个翻开的 TCP 连接,则该连接被释放。closesocket的调用格式如下:closesocket(SOCKETs);参数 s 待关闭的套接字描述符。假设没有错误发生,closesocket返回 0。否则返回值 SOCKET_ERROR。4.3 效劳器端程序效劳器端程序遵循以下根本步骤: 1建立一个效劳器 Socket 并开头监听。2使用 accept取得的连接。3建立输入和输出流。4在已有的协议上产生会话。5关闭客户端流和 Socket。6关闭效劳
24、器 Socket。效劳器的处理过程是并发的,它为每个客户恳求安排一个线程,而不是来一个处理一个。所以看起来它在同时处理多个恳求。效劳器端的源程序代码如下:/ModuleName:Server.c/Description:/ThisexampleillustratesasimpleTCPserverthataccepts/incomingclientconnections.Onceaclientconnectionis/established,athreadisspawnedtoreaddatafromthe/clientandechoitback(iftheechooptionisnot/di
25、sabled)./Compile:/cl-oServerServer.cws2_32.lib/Commandlineoptions:/server-p:x-i:IP-o/-p:xPortnumbertolistenon/-i:strInterfacetolistenon/-oReceiveonly,don”techothedataback/#include#include #include#pragmacomment(lib,“ws2_32“)#defineDEFAULT_PORT5150#defineDEFAULT_BUFFER4096intiPort=DEFAULT_PORT;/Portt
26、olistenforclientson BOOLbInterface=FALSE,/ListenonthespecifiedinterfacebRecvOnly=FALSE;/Receivedataonly;don”techoback charszAddress128;/Interfacetolistenforclientson/Function:usage/Description:/Printusageinformationandexit/ voidusageprintf(“usage:server-p:x-i:IP-onn“);printf(“-p:xPortnumbertolisteno
27、nn“); printf(“-i:strInterfacetolistenonn“); printf(“-oDon”techothedatabacknn“); ExitProcess(1);/Function:ValidateArgs/Description:/Parsethecommandlinearguments,andsetsomeglobalflags/toindicatewhatactionstoperform/ voidValidateArgs(intargc,char*argv)inti;for(i=1;i3)strcpy(szAddress,&argvi3); break;ca
28、se”o”: bRecvOnly=TRUE;break;default:usage; break;/Function:ClientThread/Description:/Thisfunctioniscalledasathread,andithandlesagiven/clientconnection.Theparameterpassedinisthesocket/handlereturnedfromanacceptcall.Thisfunctionreads/datafromtheclientandwritesitback./ DWORDWINAPIClientThread(LPVOIDlpP
29、aram)SOCKETsock=(SOCKET)lpParam;charszBuffDEFAULT_BUFFER;intret,nLeft, idx;while(1)/Performablockingrecvcall/ ret=recv(sock,szBuff,DEFAULT_BUFFER,0); if(ret=0)/Gracefulclosebreak; elseif(ret=SOCKET_ERROR)printf(“recvfailed:%dn“,WSAGetLastError); break;szBuffret=”0”; printf(“RECV:”%s”n“,szBuff);/Ifwe
30、selectedtoechothedataback,doit/ if(!bRecvOnly)nLeft=ret; idx=0;/Makesurewewriteallthedata/ while(nLeft0)ret=send(sock,&szBuffidx,nLeft,0); if(ret=0)break; elseif(ret=SOCKET_ERROR)printf(“sendfailed:%dn“, WSAGetLastError);break;nLeft-=ret; idx+=ret;return0;/Function:main/Description:/Mainthreadofexec
31、ution.InitializeWinsock,parsethe/commandlinearguments,createthelisteningsocket,bind/tothelocaladdress,andwaitforclientconnections./ intmain(intargc,char*argv)WSADATAwsd;SOCKETsListen, sClient;intiAddrSize; HANDLEhThread;DWORDdwThreadId; structsockaddr_inlocal,client;ValidateArgs(argc,argv); if(WSASt
32、artup(MAKEWORD(2,2),&wsd)!=0)printf(“FailedtoloadWinsock!n“); return1;/Createourlisteningsocket/ sListen=socket(AF_INET,SOCK_STREAM,IPPROTO_IP); if(sListen=SOCKET_ERROR)printf(“socketfailed:%dn“,WSAGetLastError); return1;/Selectthelocalinterfaceandbindtoit/ if(bInterface)elselocal.sin_addr.s_addr=in
33、et_addr(szAddress); if(local.sin_addr.s_addr=INADDR_NONE)usage;local.sin_addr.s_addr=htonl(INADDR_ANY);local.sin_family=AF_INET; local.sin_port=htons(iPort);if(bind(sListen,(structsockaddr*)&local, sizeof(local)=SOCKET_ERROR)printf(“bindfailed:%dn“,WSAGetLastError); return1;listen(sListen,8);/Inacon
34、tinousloop,waitforincomingclients.Onceone/isdetected,createathreadandpassthehandleofftoit./ while(1)iAddrSize=sizeof(client); sClient=accept(sListen,(structsockaddr*)&client,&iAddrSize); if(sClient=INVALID_SOCKET)printf(“acceptfailed:%dn“,WSAGetLastError); break;printf(“Acceptedclient:%s:%dn“, inet_
35、ntoa(client.sin_addr),ntohs(client.sin_port);hThread=CreateThread(NULL,0,ClientThread,(LPVOID)sClient,0,&dwThreadId); if(hThread=NULL)printf(“CreateThreadfailed:%dn“,GetLastError); break;CloseHandle(hThread);closesocket(sListen);WSACleanup; return0;与客户端程序相比,效劳器端在以下几个方面存在差异。1、指定本地地址bind当一个套接字用 socket
36、创立后,存在一个名字空间(地址族),但它没有被命名。bind将套接字地址包括本地主机地址和本地端口地址与所创立的套接字号联系起来,马上名字赐予套接字,以指定本地半相关。其调用格式如下:bind(SOCKETs,conststructsockaddrFAR*name,intnamelen);参数 s 是由 socket调用返回的并且未作连接的套接字描述符(套接字号)。参数name 是赋给套接字 s 的本地地址名字,其长度可变,构造随通信域的不同而不同。namelen 说明白 name 的长度。假设没有错误发生,bind返回 0。否则返回值 SOCKET_ERROR。地址在建立套接字通信过程中起着
37、重要作用,作为一个网络应用程序设计者对套接字地址构造必需有明确生疏。例如,UNIXBSD 有一组描述套接字地址的数据构造,其中使用 TCP/IP 协议的地址构造为:structsockaddr_in shortsin_family;/*AF_INET*/u_shortsin_port;/*16位端口号,网络字节挨次*/ structin_addrsin_addr;/*32位 IP 地址,网络字节挨次*/charsin_zero8;/* 保存*/2、监听连接 -listen此调用用于面对连接效劳器,说明它情愿接收连接。listen需在 accept之前调用,其调用格式如下:listen(SOCK
38、ETs,intbacklog);参数 s 标识一个本地已建立、尚未连接的套接字号,效劳器情愿从它上面接收恳求。backlog 表示恳求连接队列的最大长度,用于限制排队恳求的个数,目前允许的最大值为 5。假设没有错误发生,listen返回 0。否则它返回 SOCKET_ERROR。listen在执行调用过程中可为没有调用过 bind的套接字 s 完成所必需的连接,并建立长度为 backlog 的恳求连接队列。调用 listen是效劳器接收一个连接恳求的四个步骤中的第三步。它在调用socket安排一个流套接字,且调用 bind给 s 赋于一个名字之后调用,而且肯定要在 accept之前调用。3、a
39、ccept调用accept(SOCKETs,structsockaddrFAR*addr,intFAR*addrlen);accept用于面对连接效劳。参数 addr 和 addrlen 存放客户方的地址信息。调用前,参数 addr 指向一个初始值为空的地址构造,而 addrlen 的初始值为 0;调用 accept后,效劳器等待从编号为 s 的套接字上承受客户连接恳求,而连接恳求是由客户方的 connect调用发出的。当有连接恳求到达时,accept调用将恳求连接队列上的第一个客户方套接字地址及长度放入 addr 和 addrlen,并创立一个与 s 有一样特性的套接字号。的套接字可用于处理
40、效劳器并发恳求。参数 s 为本地套接字描述符,在用做 accept调用的参数前应领先调用过listen。addr 指向客户方套接字地址构造的指针,用来接收连接实体的地址。addr 的精准格式由套接字创立时建立的地址族打算。addrlen 为客户方套接字地址的长度字节数。假设没有错误发生,accept返回一个 SOCKET 类型的值,表示接收到的套接字的描述符。否则返回值 INVALID_SOCKET。5、试验任务1、两人一组,分别编写网络程序 Server 和 Client,以实现最简洁的 TCP 通信。说明:假设使用供给的 Server.c 和 Client.c,则在文件菜单中读入程序后,可
41、直接“Build”成执行文件运行。当询问“Thisbuildcommandrequiresanactiveproject workspace ? ”时,单击“确定”按钮即可。2、在 VC+6.0 集成环境下单步调试程序,参考Winsock 根底,弄清楚程序中相关函数的用法、每个数据构造的含义。6、提交试验报告1. 画出你所写程序的框图。2. 在报告中说明你所修改后程序的任何独特之处。3.试验日期:第 18 周星期五(2022-7-2)晚上 7:00 10:00 。试验地点:南 3 楼 网络中心机房。4.试验报告(含框图、修改说明及程序源代码)通过电子文档形式提交:以“学号-姓名-TCP 通信”为文件名,发送至:。截至时间:2022-7-917:00整。