UDP、TCP客戶-服務(wù)員程序設(shè)計_第1頁
UDP、TCP客戶-服務(wù)員程序設(shè)計_第2頁
UDP、TCP客戶-服務(wù)員程序設(shè)計_第3頁
UDP、TCP客戶-服務(wù)員程序設(shè)計_第4頁
UDP、TCP客戶-服務(wù)員程序設(shè)計_第5頁
已閱讀5頁,還剩6頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)

文檔簡介

1、一、實驗?zāi)康?#160;1、理解進程通信的原理及通信過程    2、掌握基本的網(wǎng)絡(luò)編程方法二、實驗要求1、進一步掌握UDP及TCP協(xié)議的工作原理    2、掌握SOCKET編程的基本方法    3、學(xué)習(xí)應(yīng)用C語言與WinSock2進行簡單的無連接的網(wǎng)絡(luò)程序設(shè)計,實現(xiàn)網(wǎng)絡(luò)數(shù)據(jù)傳輸    4、學(xué)習(xí)應(yīng)用C語言與WinSock2進行簡單的面向連接的網(wǎng)絡(luò)程序設(shè)計,實現(xiàn)網(wǎng)絡(luò)數(shù)據(jù)傳輸三、實驗原理 1、關(guān)于使用套接字編程的一些基本概念(a)半相關(guān)與全相

2、關(guān)半相關(guān)在網(wǎng)絡(luò)中用一個三元組可以在全局唯一標(biāo)志一個進程: (協(xié)議,本地地址,本地端口號)這樣一個三元組,叫做一個半相關(guān)(half-association),它指定連接的每半部分。            全相關(guān)            一個完整的網(wǎng)間進程通信需要由兩個進程組成,并且只能使用同一種高層協(xié)議。 也就是說,不可能通信的一端用TCP協(xié)議,而另一端用UDP協(xié)議。因此一個完整的

3、網(wǎng)間通信需要一個五元組來標(biāo)識:            (協(xié)議,本地地址,本地端口號,遠地地址,遠地端口號)這樣一個五元組,叫做一個相關(guān)(association),即兩個協(xié)議相同的半相關(guān)才  能組合成一個合適的相關(guān),或完全指定組成一連接。(b)TCP/IP協(xié)議的地址結(jié)構(gòu)為:              struct sockaddr_in &

4、#160;                short sin_family;     /*AF_INET*/                  u_short sin_port;    /*16位端口號,網(wǎng)絡(luò)字

5、節(jié)順序*/ struct in_addr sin_addr;     /*32位IP地址,網(wǎng)絡(luò)字節(jié)順序*/                  char sin_zero8;            /*保留*/       

6、0;      (c)套接字類型           TCP/IP的socket提供下列三種類型套接字。流式套接字(SOCK_STREAM)提供了一個面向連接、可靠的數(shù)據(jù)傳輸服務(wù),數(shù)據(jù)無差錯、無重復(fù)地發(fā)送,且按發(fā)送順序接收。內(nèi)設(shè)流量控制,避免數(shù)據(jù)流超限;數(shù)據(jù)被看作是字節(jié)流,無長度限制。文件傳送協(xié)議(FTP)即使用流式套接字。數(shù)據(jù)報式套接字(SOCK_DGRAM)     提供了一個無連接服務(wù)。數(shù)據(jù)包以獨立包

7、形式被發(fā)送,不提供無錯保證,數(shù)據(jù)可能丟失或重復(fù),并且接收順序混亂。網(wǎng)絡(luò)文件系統(tǒng)(NFS)使用數(shù)據(jù)報式套接字。原始式套接字(SOCK_RAW)      該接口允許對較低層協(xié)議,如IP、ICMP直接訪問。常用于檢驗新的協(xié)議實現(xiàn)或訪問現(xiàn)有服務(wù)中配置的新設(shè)備。  (d)基本套接字系統(tǒng)調(diào)用為了更好地說明套接字 編程原理,下面給出幾個基本套接字系統(tǒng)調(diào)用說明。          (1) 創(chuàng)建套接字socket() 應(yīng)用程序在使用套接字前,首先必須擁

8、有一個套接字,系統(tǒng)調(diào)用socket()向應(yīng)用程序提供創(chuàng)建套接字的手段,其調(diào)用格式如下:              SOCKET socket(int af, int type, int protocol);該調(diào)用要接收三個參數(shù):af、type、protocol。參數(shù)af指定通信發(fā)生的區(qū) 域,UNIX系統(tǒng)支持的地址族有:AF_UNIX、AF_INET、AF_NS等,而DOS、WINDOWS中僅支持AF_INET,它是網(wǎng)際網(wǎng)區(qū)域。因此,地址族與協(xié)議族相同。參數(shù)type描

9、述要建立的套接字的類型。參數(shù)protocol 說明該套接字使用的特定協(xié)議,如果 調(diào)用者不希望特別指定使用的協(xié)議,則置為0,使用默認的連接模式。根據(jù)這三 個參數(shù)建立一個套接字,并將相應(yīng)的資源分配給它, 同時返回一個整型套接字號。因此,socket()系統(tǒng)調(diào)用實際上指定了相關(guān)五元組中的“協(xié)議”這一元。(2) 指定本地地址bind()當(dāng)一個套接字用socket()創(chuàng)建后,存在一個名字空間(地址族),但它沒有被命名。bind()將套接字地址(包括本地主機地址和本地端口地址)與所創(chuàng)建的套接字號聯(lián)系起來,即將名字賦予套接字,以指定本地半相關(guān)。其調(diào)用格式如下: int bind(SOCKET s, cons

10、t struct sockaddr FAR * name, int namelen); 參數(shù) s 是由 socket() 調(diào)用返回的并且未作連接的套接字描述符(套接字號)。參數(shù)name是賦給套接字s的本地地址(名字),其長度可變,結(jié)構(gòu)隨通信域的不同而不同。namelen表明了name的長度。如果沒有錯誤發(fā)生,bind()返回0。否則返回值SOCKET_ERROR。 地址在建立套接字通信過程中起著重要作用,作為一個網(wǎng)絡(luò)應(yīng)用程序設(shè)計者對套接字地址結(jié)構(gòu)必須有明確認識。(3) 建立套接字連接connect()與accept()這兩個系統(tǒng)調(diào)用用于完成一個完整相關(guān)的建立,其中connect()用于建立連接

11、。無連接的套接字進程也可以調(diào)用connect(),但這時在進程之間沒有實際的報文交換,調(diào)用將從本地操作系統(tǒng)直接返回。這樣做的優(yōu)點是程序員不必為每一數(shù)據(jù)指定目的地址,而且如果收到的一個數(shù)據(jù)報,其目的端口未與任套接字建立“連接”,便能判斷該端口不可操作。而accept()用于使服務(wù)器等待來自某客戶進程的實際連接。connect()的調(diào)用格式如下:               int connect(SOCKET s,const struct sockaddr FAR *

12、 name,int namelen); 參數(shù)s是欲建立連接的本地套接字描述符。參數(shù)name指出說明對方套接字 地址結(jié)構(gòu)的指針。對方套接字地址長度由namelen說明。如果沒有錯誤發(fā)生,connect()返回0。否則返回值SOCKET_ERROR。在面向連接的協(xié)議中,該調(diào)用導(dǎo)致本地系統(tǒng)和外部系統(tǒng)之間連接實際建立。由于地址族總被包含在套接字地址結(jié)構(gòu)的前兩個字節(jié)中,并通過socket()調(diào)用與某個協(xié)議族相關(guān)。因此bind()和connect()無須協(xié)議作為參數(shù)。 accept()的調(diào)用格式如下:SOCKET accept(SOCKET s,struct sockaddr FAR* addr,int

13、FAR* addrlen); 參數(shù)s為本地套接字描述符,在用做accept() 調(diào)用的參數(shù)前應(yīng)該先調(diào)用過 listen()。addr 指向客戶方套接字地址結(jié)構(gòu)的指針, 用來接收連接實體的地址。addr的確切格式由套接字創(chuàng)建時建立的地址族決定。addrlen 為客戶方套 接字地址的長度(字節(jié)數(shù))。如果沒有錯誤發(fā)生,accept()返回一個SOCKET類型的值,表示接收到的套接字的描述符。否則返回值INVALID_SOCKET。 accept()用于面向連接服務(wù)器。參數(shù)addr和addrlen 存放客戶方的地址信息。調(diào)用前,參數(shù)addr 指向一個初始值為空的地址結(jié)構(gòu),而 addrlen 的初始值為

14、0; 調(diào)用accept() 后,服務(wù)器等待從編號為s的套接字上接受客戶連接請求,而連接請求是由客戶方的connect()調(diào)用發(fā)出的。當(dāng)有連接請求到達時, accept()調(diào)用將請求連接隊列上的第一個客戶方套接字地址及長度放入addr 和addrlen,并創(chuàng)建一個與s有相同特性的新套接字號。新的套接字可用于處理服務(wù)器并發(fā)請求。四個套接字系統(tǒng)調(diào)用,socket()、bind()、connect()、accept(),可以 完成一個完全五元相關(guān)的建立。socket()指定五元組中的協(xié)議元,它的用法與是否為客戶或服務(wù)器、是否面向連接無關(guān)。bind()指定五元組中的本地二元,即本地主機地址和端口號,其用

15、法與是否面向連接有關(guān):在服務(wù)器方,無論是否面向連接,均要調(diào)用 bind() ;在客戶方,若采用面向連接,則可以不調(diào)用 bind(),而通過connect()自動完成。若采用無連接,客戶方必須使用bind()以獲得一個唯一的地址。 以上討論僅對客戶/服務(wù)器模式而言,實際上套接字的使用是非常靈活 的,唯一需遵循的原則是進程通信之前,必須建立完整的相關(guān)。(4) 監(jiān)聽連接listen()此調(diào)用用于面向連接服務(wù)器,表明它愿意接收連接。listen()需在accept()之前調(diào)用,其調(diào)用格式如下:          

16、     int listen(SOCKET s, int backlog); 參數(shù)s標(biāo)識一個本地已建立、尚未連接的套接字號, 服務(wù)器愿意從它上面 接收請求。 backlog 表示請求連接隊列的最大長度, 用于限制排隊請求的個數(shù),目前允許的最大值為5。如果沒有錯誤發(fā)生,listen()返回0。否則它返回 SOCKET_ERROR。listen()在執(zhí)行調(diào)用過程中可為沒有調(diào)用過bind() 的套接字s完成所必須的連接,并建立長度為backlog的請求連接隊列。調(diào)用listen()是服務(wù)器接收一個連接請求的四個步驟中的第三步。它在調(diào)用socket() 分配一

17、個流套接字,且調(diào)用bind()給s賦于一個名字之后調(diào)用,而且一定要在accept()之前調(diào)用。(5) 數(shù)據(jù)傳輸send()與recv()當(dāng)一個連接建立以后,就可以傳輸數(shù)據(jù)了。常用的系統(tǒng)調(diào)用有 send() 和 recv()。send() 調(diào)用用于在參數(shù)s指定的已連接的數(shù)據(jù)報或流套接字上發(fā)送輸出數(shù)據(jù),格式如下:               int send(SOCKET s, const char FAR *buf, int len, int flags); 參數(shù)s為

18、已連接的本地套接字描述符。buf 指向存有發(fā)送數(shù)據(jù)的緩沖區(qū)的 指針,其長度由 len 指定。flags 指定傳輸控制方式,如是否發(fā)送帶外數(shù)據(jù)等。如果沒有錯誤發(fā)生,send()返回總共發(fā)送的字節(jié)數(shù)。否則它返回 SOCKET_ERROR。ecv()調(diào)用用于在參數(shù)s指定的已連接的數(shù)據(jù)報或流套接字上接收輸入數(shù)據(jù),格式如下:               int recv(SOCKET s, char FAR *buf, int len, int flags); 參數(shù)s 為已連

19、接的套接字描述符。buf指向接收輸入數(shù)據(jù)緩沖區(qū)的指針,其長度由len 指定。flags 指定傳輸控制方式,如是否接收帶外數(shù)據(jù)等。如果沒有錯誤發(fā)生,recv()返回總共接收的字節(jié)數(shù)。如果連接被關(guān)閉,返回0。否 則它返回SOCKET_ERROR。            (6)輸入/輸出多路復(fù)用select()select()調(diào)用用來檢測一個或多個套接字的狀態(tài)。對每一個套接字來說,這個調(diào)用可以請求讀、寫或錯誤狀態(tài)方面的信息。請求給定狀態(tài)的套接字集合由一個fd_set結(jié)構(gòu)指示。在返回時,此結(jié)構(gòu)被更新,以

20、反映那些滿足特定條件的套接字的子集,同時, select()調(diào)用返回滿足條件的套接字的數(shù)目,其調(diào)用格式如下:               int select(int nfds, fd_set FAR * readfds, fd_set FAR * writefds,            fd_set FAR * exceptfds, const stru

21、ct timeval FAR * timeout); 參數(shù)nfds指明被檢查的套接字描述符的值域,此變量一般被忽略。參數(shù)readfds指向要做讀檢測的套接字描述符集合的指針,調(diào)用者希望從中讀取數(shù)據(jù)。 參數(shù) writefds 指向要做寫檢測的套接字描述符集合的指針。exceptfds指向要檢測是否出錯的套接字描述符集合的指針。timeout指向select()函數(shù)等待的最大時間,如果設(shè)為NULL則為阻塞操作。select()返回包含在fd_set結(jié)構(gòu)中已準 備好的套接字描述符的總數(shù)目,或者是發(fā)生錯誤則返回SOCKET_ERROR。(7)關(guān)閉套接字closesocket()closesocket(

22、)關(guān)閉套接字s,并釋放分配給該套接字的資源;如果s涉及一個打開的TCP連接,則該連接被釋放。closesocket()的調(diào)用格式如下:              BOOL closesocket(SOCKET s); 參數(shù)s待關(guān)閉的套接字描述符。如果沒有錯誤發(fā)生,closesocket()返回0。否則返回值SOCKET_ERROR。 2、用于無連接協(xié)議(如UDP)的SOCKET系統(tǒng)調(diào)用流程框圖:3、用于面向連接協(xié)議(如TCP)的SOCKET系統(tǒng)調(diào)用流程框圖:四、實驗步驟 練習(xí)

23、一:使用UDP協(xié)議的無連接客戶-服務(wù)員程序設(shè)計。 根據(jù)實驗原理中介紹的內(nèi)容,設(shè)計一個無連接的客戶-服務(wù)員系統(tǒng),實現(xiàn)二者之間的數(shù) 據(jù)傳遞。下面是一個簡單的UDP客戶-服務(wù)員程序的實例,作為參考。 說明:下述服務(wù)員程序已經(jīng)在服務(wù)器端運行,在仿真機端僅需編寫相應(yīng)的客戶程序并運行。 #include <stdio.h> #include <winsock.h> #define SERV_UDP_PORT 6000 /*服務(wù)員進程端口號,視具體情況而定*/ #define SERV_HOST_ADDR "8" /*服務(wù)員地址,視具體情況而定

24、*/ /* 宏定義用來打印錯誤消息*/ #define PRINTERROR(s) fprintf(stderr,"n%: %dn", s, WSAGetLastError() / /數(shù)據(jù)報通信的服務(wù)員端子程序 / / void DatagramServer(short nPort) SOCKET theSocket; /*創(chuàng)建一個數(shù)據(jù)報類型的socket*/ theSocket = socket(AF_INET, / 地址族 SOCK_DGRAM, / socket類型 IPPROTO_UDP); / 協(xié)議類型:UDP /*錯誤處理*/ if (theSocket = I

25、NVALID_SOCKET) PRINTERROR("socket()"); return; /*填寫服務(wù)員地址結(jié)構(gòu)*/ SOCKADDR_IN saServer; saServer.sin_family = AF_INET; saServer.sin_addr.s_addr = INADDR_ANY; / 由WinSock指定地址 saServer.sin_port = htons(nPort); / 服務(wù)員進程端口號 /*將服務(wù)員地址與已創(chuàng)建的socket綁定*/ int nRet; nRet = bind(theSocket, / Socket 描述符 (LPSOCK

26、ADDR)&saServer, / 服務(wù)員地址 sizeof(struct sockaddr) / 地址長度 ); /*錯誤處理*/ if (nRet = SOCKET_ERROR) PRINTERROR("bind()"); closesocket(theSocket); return; /* 等待來自客戶端的數(shù)據(jù)*/ SOCKADDR_IN saClient; char szBuf1024; int nLen; while(1) /*準備接收數(shù)據(jù)*/ memset(szBuf, 0, sizeof(szBuf); nRet = recvfrom(theSocke

27、t, / 已綁定的socket szBuf, / 接收緩沖區(qū) sizeof(szBuf), / 緩沖區(qū)大小 0, / Flags (LPSOCKADDR)&saClient, / 接收客戶端地址的緩沖區(qū) &nLen); / 地址緩沖區(qū)的長度 /*打印接收到的信息*/ printf("nData received: %s", szBuf); /* 發(fā)送數(shù)據(jù)給客戶端*/ strcpy(szBuf, "From the Server"); sendto(theSocket, / 已綁定的socket szBuf, / 發(fā)送緩沖區(qū) strlen(

28、szBuf), / 發(fā)送數(shù)據(jù)的長度 0, / Flags (LPSOCKADDR)&saClient, / 目的地址 nLen); / 地址長度 closesocket(theSocket); return; / /數(shù)據(jù)報服務(wù)員端主程序 / / void main() WORD wVersionRequested = MAKEWORD(1,1); WSADATA wsaData; int nRet; short nPort; nPort = SERV_UDP_PORT; /* 初始化Winsock*/ nRet = WSAStartup(wVersionRequested, &

29、wsaData); if (wsaData.wVersion != wVersionRequested) fprintf(stderr,"n Wrong versionn"); return; /*調(diào)用數(shù)據(jù)服務(wù)員子程序*/ DatagramServer(nPort); /*結(jié)束WinSock*/ WSACleanup(); 上述服務(wù)員程序已經(jīng)在服務(wù)器端運行,請學(xué)生認真閱讀分析后,然后根據(jù)實驗原理二 中介紹的內(nèi)容,在仿真機端編寫相應(yīng)的無連接的客戶端程序并運行。從而實現(xiàn)客戶和服務(wù) 器間的數(shù)據(jù)傳輸。 在仿真機一端運行客戶端進程,在監(jiān)控機端捕獲數(shù)據(jù)并進行分析。 練習(xí)二:使用TCP協(xié)

30、議的面向連接的客戶-服務(wù)員程序設(shè)計。 根據(jù)實驗原理中介紹的內(nèi)容,設(shè)計一個面向連接的客戶-服務(wù)員系統(tǒng),實現(xiàn)二者之間的 數(shù)據(jù)傳遞。 下面是一個簡單的TCP客戶-服務(wù)員程序的服務(wù)員程序: 面向連接的服務(wù)員程序: #include <PROCESS.H> #include <windows.h> #include <winsock.h> #include <sys/types.h> #include <fcntl.h> #include <wsipx.h> #include <wsnwlink.h> #include

31、<stdio.h> #define SERV_TCP_PORT 6000 /*服務(wù)員進程端口號,視具體情況而定*/ #define SERV_HOST_ADDR "0" /*服務(wù)員IP,視具體情況而定*/ int sockfd; / /線程用來處理客戶端的請求 / /服務(wù)員主進程每與某客戶端建立一個連接之后,便啟動一個新的線程來處理接下/ /客戶端的請求,參數(shù)為服務(wù)員與該客戶端的連接點:socket / / DWORD ClientThread(void *pVoid) int nRet; char szBuf1024; memset(szB

32、uf, 0, sizeof(szBuf); /*接收來自客戶端的數(shù)據(jù)信息*/ nRet = recv(SOCKET )pVoid, / 與客戶端連接的socket szBuf, / 接收緩沖區(qū) sizeof(szBuf), / 緩沖區(qū)長度 0); / Flags /*錯誤處理*/ if (nRet = INVALID_SOCKET) printf("recv()"); closesocket(sockfd); closesocket(SOCKET)pVoid); return 0; /*顯示接收到的數(shù)據(jù)*/ printf("nData received: %sn&

33、quot;, szBuf); /* 發(fā)送數(shù)據(jù)給客戶端*/ strcpy(szBuf, "From the Server"); / 發(fā)送內(nèi)容 nRet = send(SOCKET)pVoid, / 與客戶端連接的socket szBuf, / 數(shù)據(jù)緩沖區(qū) strlen(szBuf), / 數(shù)據(jù)長度 0); / Flags /*結(jié)束連接,釋放socket*/ closesocket(SOCKET)pVoid); return 0; / /服務(wù)員主程序: / /在一個眾所周知的端口上等待客戶的連接請求 / /有請求到來時建立與客戶端的連接,并啟動一個線程處理該請求 / / int

34、 main() int clilen; int pHandle=-1; struct sockaddr_in serv_addr; SOCKET socketClient; DWORD ThreadAddr; HANDLE dwClientThread; SOCKADDR_IN SockAddr; /*初始化Winsock API,即連接Winsock庫*/ WORD wVersionRequested = MAKEWORD(1, 1); WSADATA wsaData; if (WSAStartup(wVersionRequested, &wsaData) printf("

35、WSAStartup failed %sn", WSAGetLastError(); return -1; /*打開一個TCP SOCKET */ if(sockfd=socket(AF_INET, SOCK_STREAM,0)<0) printf("server:can't open stream sockern"); /*綁定本地地址,以便客戶端連接*/ memset(char *)&serv_addr,0,sizeof(struct sockaddr_in); serv_addr.sin_family=AF_INET; serv_addr.sin_addr.s_addr=htonl(INADDR_ANY); serv_addr.sin_p

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論