Socket網(wǎng)絡(luò)編程指導(dǎo) 1/37 什么是Socket? 2/37 BSD Socket(伯克立套接字)是通過(guò)標(biāo)準(zhǔn)的UNIX文件描述符和其它程序通訊的一個(gè)方法,,目前已經(jīng)被廣泛移植到各個(gè)平臺(tái)。 Socket是獨(dú)立于具體協(xié)議的網(wǎng)絡(luò)編程接口。在ISO模型中,,主要位于會(huì)話層和傳輸層。 Socket的類(lèi)型 3/37 流式套接字(SOCK_STREAM) 數(shù)據(jù)報(bào)式套接字(SOCK_DGRAM) 原始式套接字(SOCK_RAW) 4/37 Socket所在層次示意圖 Application program Stream Socket Interface TCP UDP Datagram Socket Interface Raw Socket Interface IP Physical and data link layers 基本套接字調(diào)用 5/37 創(chuàng)建套接字 socket(); 接受連接 accept(); Socket相關(guān)的數(shù)據(jù)結(jié)構(gòu) 6/37 struct sockaddr_in { }; struct in_addr { in_addr_t s_addr; /* 存儲(chǔ)32bit 的IP地址*/ } 網(wǎng)絡(luò)字節(jié)順序和主機(jī)字節(jié)順序 7/37 Big-Endian Byte Order:字節(jié)的高位在內(nèi)存中放在存儲(chǔ)單元的起始位置 00001010 00010111 00001110 00000110 00001010 00010111 00001110 00000110 Memory Little-Endian Byte Order : 與Big-Endian相反 A A+1 A+2 A+3 8/40 Host byte order( Little-Endian ) 16-bit 32-bit Network byte order(Big-Endian) 16-bit 32-bit htons() ntohs() htonl() ntohl() 網(wǎng)絡(luò)字節(jié)順序和主機(jī)字節(jié)順序的轉(zhuǎn)換 IP地址的轉(zhuǎn)換 9/37 int inet_aton(const char* strptr, struct in_addr *addrptr); 從點(diǎn)狀十進(jìn)制到32位2進(jìn)制的轉(zhuǎn)換,如“202.38.64.185” 到 11001010,,00100110,,01000000,10111001 char *inet_ntoa(struct in_addr inadd); 與inet_aton()的功能相反 相關(guān)的內(nèi)存操作函數(shù) 10/37 void *memset(void *buffer, int c, int count); 把buffer所指內(nèi)存區(qū)域的前count個(gè)字節(jié)設(shè)置成字符c,。 void *memcpy(void *dest, void *src, unsigned int count); 由src所指內(nèi)存區(qū)域復(fù)制count個(gè)字節(jié)到dest所指內(nèi)存區(qū)域,。 Void bzero(void *s, int n ); 置字節(jié)字符串s的前n個(gè)字節(jié)為零。 域名和IP地址的轉(zhuǎn)換 11/37 struct hostent *gethostbyname(const char *name); struct hostent { 建立Socket 12/37 int socket(int domain, int type, int protocol); 參數(shù)說(shuō)明: domain:通信使用的協(xié)議族,即網(wǎng)絡(luò)的類(lèi)型,,對(duì)于 TCP/IP來(lái)說(shuō),,是AF_INET type: SOCK_STREAM / SOCK_DGRAM protocol: 通常為0 返回整形的socket描述符,如果出錯(cuò),,返回-1 Socket的配置 13/37 Socket描述符是一個(gè)指向內(nèi)部數(shù)據(jù)結(jié)構(gòu)的指針,,它指向描述符表入口。調(diào)用Socket()函數(shù)時(shí),,將建立一個(gè)Socket,,為一個(gè)Socket數(shù)據(jù)結(jié)構(gòu)分配存儲(chǔ)空間。 兩個(gè)網(wǎng)絡(luò)程序之間的一個(gè)網(wǎng)絡(luò)連接包括五種信息:通信協(xié)議,、本地主機(jī)地址和端口,、遠(yuǎn)端主機(jī)地址和端口。 在使用socket進(jìn)行網(wǎng)絡(luò)傳輸以前,,必須配置該socket,。 面向連接的socket客戶端調(diào)用connect()函數(shù)在socket數(shù)據(jù)結(jié)構(gòu)中保存本地和遠(yuǎn)端信息,。 無(wú)連接socket的客戶端和服務(wù)端以及面向連接socket的服務(wù)端通過(guò)調(diào)用bind()函數(shù)來(lái)配置本地信息,。 綁定Socket 14/37 int bind(int sockfd,,struct sockaddr_in *my_addr, int addrlen); sockfd是socket()返回的socket描述符; my_addr是指向包含本機(jī)IP地址及端口號(hào)等信息的 sockaddr類(lèi)型的指針,; addrlen一般被設(shè)置為sizeof(struct sockaddr_in)
綁定前sockaddr_in的初始化 15/37 my_addr.sin_family = AF_INET; //選擇網(wǎng)絡(luò)類(lèi)型為TCP/IP my_addr.sin_addr.s_addr = inet_addr(“210.45.64.222"); my_addr.sin_port = htons( 8888 ); //選擇端口8888 addr_len = sizeof(struct sockaddr_in); memset(&my_addr.sin_zero, ‘\0’, sizeof(my_addr.sin_zero)); 建立連接(客戶端) 16/37 面向連接的客戶程序使用connect函數(shù)來(lái)配置socket并與遠(yuǎn)端服務(wù)器建立一個(gè)TCP連接,其函數(shù)原型為: serv_addr是包含遠(yuǎn)端主機(jī)IP地址和端口號(hào)的指針,;addrlen是遠(yuǎn)端地址結(jié)構(gòu)的長(zhǎng)度 成功則返回0,出現(xiàn)錯(cuò)誤時(shí)返回-1 建立連接(服務(wù)器端) 17/37 服務(wù)器監(jiān)聽(tīng)端口:listen函數(shù)使socket處于被動(dòng)的監(jiān)聽(tīng)模式,,并為該socket建立一個(gè)輸入數(shù)據(jù)隊(duì)列,,將到達(dá)的服務(wù)請(qǐng)求保存在此隊(duì)列中,直到程序處理它們,。 backlog:請(qǐng)求連接隊(duì)列的最大長(zhǎng)度 成功返回0,出錯(cuò)返回-1 建立連接(服務(wù)器端) 18/37 accept()函數(shù)讓服務(wù)器接收客戶的連接請(qǐng)求,。在建立好輸入隊(duì)列后,,服務(wù)器就調(diào)用accept函數(shù),然后睡眠并等待客戶的連接請(qǐng)求,。 addr是指向sockaddr_in變量的指針,,該變量存放提出連接請(qǐng)求服務(wù)的主機(jī)的信息 返回新的socket描述符,和請(qǐng)求連接進(jìn)程的地址聯(lián)系起來(lái)在新的socket描述符上進(jìn)行數(shù)據(jù)傳輸操作,。原來(lái)的socket繼續(xù)listen 數(shù)據(jù)傳輸(1) 19/37 send()和recv()這兩個(gè)函數(shù)用于面向連接的socket上進(jìn)行數(shù)據(jù)傳輸,。 send() 返回實(shí)際發(fā)送的字節(jié)數(shù),可能會(huì)少于希望發(fā)送的數(shù)據(jù),。在程序中應(yīng)該將send()的返回值與欲發(fā)送的字節(jié)數(shù)進(jìn)行比較,。當(dāng)返回值與len不匹配時(shí),應(yīng)該進(jìn)行處理,。 數(shù)據(jù)傳輸(2) 20/37 recv()函數(shù)原型為: buf 是存放接收數(shù)據(jù)的緩沖區(qū),;len是緩沖區(qū)的長(zhǎng)度。flags也被置為0,。 recv()返回實(shí)際接收的字節(jié)數(shù),,當(dāng)出現(xiàn)錯(cuò)誤時(shí),返回-1 數(shù)據(jù)傳輸(3) 21/37 sendto()和recvfrom()用于在無(wú)連接的數(shù)據(jù)報(bào)socket方 式下進(jìn)行數(shù)據(jù)傳輸,。由于本地socket沒(méi)有與遠(yuǎn)端機(jī)器建立連接,,所以在發(fā)送數(shù)據(jù)時(shí)要指明目的地址。 sendto()函數(shù)原型為: int sendto(int sockfd, const void *buf,int buflen,, unsigned int flags, const struct sockaddr_in *to, int tolen); 數(shù)據(jù)傳輸(4) 22/37 recvfrom()函數(shù)原型為: recvfrom()函數(shù)返回接收到的字節(jié)數(shù),,當(dāng)出錯(cuò)時(shí)返回-1 結(jié)束傳輸 23/37 close()函數(shù)用于釋放socket,,停止在該socket上的任何數(shù)據(jù)操作: close(sockfd); 也可以調(diào)用shutdown() 來(lái)關(guān)閉該socket 該函數(shù)允許只停止某個(gè)方向上的數(shù)據(jù)傳輸,而一個(gè)方向上的數(shù)據(jù)傳輸繼續(xù)進(jìn)行,。 C/S結(jié)構(gòu) 24/37 服務(wù)器端要先啟動(dòng),提供相應(yīng)服務(wù): 客戶端: 流程圖 25/37 TCP服務(wù)器端 (循環(huán)服務(wù)器) TCP客戶端 socket( ) bind( ) listen( ) accept( ) socket( ) send( ) connect( ) recv( ) recv( ) send( ) close( ) close( ) UDP服務(wù)器端 UDP客戶端 socket( ) bind( ) listen( ) recvfrom( ) sendto( ) socket( ) bind( ) close( ) close( ) 簡(jiǎn)單的例子 26/37 int sockfd, newsockfd,addr_len, sendnum; struct sockaddr_in my_addr, their_addr; char * msg = “welcome”; sockfd = socket( AF_INET, SOCK_STREAM, 0 ); //建立socket my_addr.sin_family = AF_INET; //選擇網(wǎng)絡(luò)類(lèi)型為TCP/IP my_addr.sin_addr.s_addr = inet_addr(“210.45.64.222"); my_addr.sin_port = htons( 8888 ); //選擇端口8888 addr_len = sizeof( struct sockaddr_in); memset(&my_addr.sin_zero, ‘\0’, sizeof(my_addr.sin_zero)); bind(sockfd, (struct sockaddr *)&my_addr, addr_len); //綁定socket listen(sockfd,10); //監(jiān)聽(tīng),等待連接,等待連接隊(duì)列最大長(zhǎng)度為10 簡(jiǎn)單的例子(續(xù)) 27/37 While( 1 ) { newsockfd = accept( sockfd, (struct sockaddr *)&my_addr, addr_len); sendnum = send(newsockfd, msg, strlen(msg)+1, 0); …… close(newsockfd); } close(sockfd); 阻塞與非阻塞(1) 28/37 阻塞函數(shù):指其完成指定的任務(wù)之前不允許程序調(diào)用另一個(gè)函數(shù),,在Windows下還會(huì)阻塞本線程消息的發(fā)送。 eg: recv( ) ,當(dāng)socket工作在阻塞模式的時(shí)候,,如果沒(méi)有數(shù)據(jù)的情況下調(diào)用該函數(shù),則當(dāng)前線程會(huì)被掛起,,直到有數(shù)據(jù)為止,。 非阻塞函數(shù):指操作啟動(dòng)之后,,如果可以立即得到結(jié)果就返回結(jié)果,否則返回表示結(jié)果需要等待的錯(cuò)誤信息,,不等待任務(wù)完成函數(shù)就返回,。 使用非阻塞I/O的方式:select() 例子: while(1){//執(zhí)行循環(huán) 一邊輸出一邊也不要忘了輸入 FD_ZERO(&wt_set); FD_ZERO(&rd_set); FD_CLR(s,&wt_set); FD_CLR(s,&rd_set); FD_SET(s,&wt_set); FD_SET(s,&rd_set); timeout.tv_sec = 0; timeout.tv_usec =500000; z=select(s+1,&rd_set,&wt_set,NULL,&timeout); if(FD_ISSET(s,&rd_set)){//有數(shù)據(jù)可讀 z=recv(s,&recvBuff,sizeof recvBuff-1,0); 29/37 阻塞與非阻塞(2) 30/37 在Berkeley socket函數(shù)部分中,不涉及網(wǎng)絡(luò)I/O,、本地端工作的函數(shù)是非阻塞函數(shù) 在Berkeley socket函數(shù)部分中,,網(wǎng)絡(luò)I/O的函數(shù)是可阻塞函數(shù),,也就是它們可以阻塞執(zhí)行,,也可以不阻塞執(zhí)行。這些函數(shù)都使用了一個(gè)socket,,如果它們使用的socket是阻塞的,,則這些函數(shù)是阻塞函數(shù);如果它們使用的socket是非阻塞的,,則這些函數(shù)是非阻塞函數(shù),。 并發(fā)服務(wù)器 31/37 TCP服務(wù)器端(并發(fā)服務(wù)器) socket( ) bind( ) listen( ) accept( ) send( ) recv( ) close( ) fork( ) //派生新進(jìn)程 close( ) 主進(jìn)程在accept之后派生新進(jìn)程,然后主進(jìn)程繼續(xù)listen,處理新的連接請(qǐng)求 新進(jìn)程自行和客戶端通信,,新進(jìn)程和主進(jìn)程搶占CPU WinSock API 32/37 WinSock是一個(gè)基于Socket模型的API,,在Microsoft Windows操作系統(tǒng)類(lèi)中使用。 它在Berkeley接口函數(shù)的基礎(chǔ)之上,,還增加了基于消息驅(qū)動(dòng)機(jī)制的Windows擴(kuò)展函數(shù),。 Winscok1.1只支持TCP/IP網(wǎng)絡(luò),WinSock2.0增加了對(duì)更多協(xié)議的支持。 Windows下的Socket編程(1) 33/37 和linux下基本相同,,需要包含winsock2.h 需要使用Ws_32.lib,,可以用以下語(yǔ)句通告程序編譯時(shí)調(diào)用該庫(kù): #pragma comment(lib,"Ws2_32.lib") ; WinSock以DLL的形式提供,在調(diào)用任何WinSock API之前,,必須調(diào)用函數(shù)WSAStartup()進(jìn)行初始化,,最后,調(diào)用函數(shù)WSACleanUp()作清理工作,。 Windows下的Socket編程(2) 34/37 WSADATA wsd; //設(shè)置WINSOCK的版本 WORD wVersionRequested=MAKEWORD(2,2); WSAStartup(wVersionRequested,&wsd) ; //初始化 ,。。,。,。。,。,。。,。 WSACleanUp(); Windows下的Socket編程(3) 35/37 MFC提供了兩個(gè)類(lèi)CAsyncSocket和CSocket來(lái)封裝WinSock API,,提供了更簡(jiǎn)單的網(wǎng)絡(luò)編程接口,。 CAsyncSocket在較低層次上封裝了WinSock API,缺省情況下,,使用該類(lèi)創(chuàng)建的socket是非阻塞的socket,,所有操作都會(huì)立即返回,如果沒(méi)有得到結(jié)果,,返回WSAEWOULDBLOCK,,表示是一個(gè)阻塞操作。 Windows下的Socket編程(4) 36/37 CSocket是CAsyncSocket的派生類(lèi),,缺省情況下使用該類(lèi)創(chuàng)建的socket是非阻塞的socket,,但是CSocket的網(wǎng)絡(luò)I/O是阻塞的,它在完成任務(wù)之后才返回,。 CSocket的阻塞不是建立在“阻塞”socket的基礎(chǔ)上,,而是在“非阻塞”socket上實(shí)現(xiàn)的阻塞操作 網(wǎng)絡(luò)編程作業(yè)要求 37/37 不分組,每人獨(dú)立完成,。 基于C/S或P2P結(jié)構(gòu),,使用UDP或TCP協(xié)議皆可。 最好使用基本SOCKET API,不反對(duì)使用CAsyncSocket和 CSocket類(lèi),,但不準(zhǔn)使用和傳輸相關(guān)的控件,。 期末提交設(shè)計(jì)文檔,源碼,,及可執(zhí)行文件,。 提交時(shí)間為12月份,具體提交日期及提交方式待定,。 可選題目I BBS發(fā)帖程序 通過(guò)term方式(202.38.64.3:23)或者通過(guò)web方式 完成在test版發(fā)一貼的功能(多發(fā)會(huì)被永久封賬號(hào)) Bbs賬號(hào)和密碼使用命令行參數(shù)或其他方式設(shè)置,,不要直接寫(xiě)在程序里 對(duì)于term方式下的程序,要求能監(jiān)視程序運(yùn)行過(guò)程(也就是說(shuō)在程序運(yùn)行的時(shí)候要把服務(wù)器的輸出打印到屏幕) Referrence: http協(xié)議:http://en./wiki/HTTP 38/37 可選題目II 完成一個(gè)HTTP服務(wù)器 使用HTTP 1.1協(xié)議 支持最大至少10個(gè)并發(fā)連接(fork創(chuàng)建子進(jìn)程) 要求服務(wù)器程序運(yùn)行以后,,能在瀏覽器中訪問(wèn)文件,,正常顯示 Web server的根目錄使用命令行參數(shù)或者其他方式制定,不要寫(xiě)在程序代碼里 文件不存在時(shí)返回瀏覽器404錯(cuò)誤 39/37 40/37 謝謝 |
|
來(lái)自: 千杯不醉004 > 《我的圖書(shū)館》