![基于TCP(UDP)的網(wǎng)絡(luò)socket-編程_第1頁](http://file4.renrendoc.com/view/017e5727f446e12abe836b1838be4f62/017e5727f446e12abe836b1838be4f621.gif)
![基于TCP(UDP)的網(wǎng)絡(luò)socket-編程_第2頁](http://file4.renrendoc.com/view/017e5727f446e12abe836b1838be4f62/017e5727f446e12abe836b1838be4f622.gif)
![基于TCP(UDP)的網(wǎng)絡(luò)socket-編程_第3頁](http://file4.renrendoc.com/view/017e5727f446e12abe836b1838be4f62/017e5727f446e12abe836b1838be4f623.gif)
![基于TCP(UDP)的網(wǎng)絡(luò)socket-編程_第4頁](http://file4.renrendoc.com/view/017e5727f446e12abe836b1838be4f62/017e5727f446e12abe836b1838be4f624.gif)
![基于TCP(UDP)的網(wǎng)絡(luò)socket-編程_第5頁](http://file4.renrendoc.com/view/017e5727f446e12abe836b1838be4f62/017e5727f446e12abe836b1838be4f625.gif)
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
基于TCP(UDP)
的
網(wǎng)絡(luò)安全(socket)編程1.套接字(socket)的引入 為了能夠方便的開發(fā)網(wǎng)絡(luò)應(yīng)用軟件,由美國伯克利大學(xué)在Unix上推出了一種應(yīng)用程序訪問通信協(xié)議的操作系統(tǒng)調(diào)用socket(套接字)。socket的出現(xiàn),使程序員可以很方便地訪問TCP/IP,從而開發(fā)各種網(wǎng)絡(luò)應(yīng)用的程序。 隨著Unix的應(yīng)用推廣,套接字在編寫網(wǎng)絡(luò)軟件中得到了極大的普及。后來,套接字又被引進(jìn)了Windows等操作系統(tǒng),成為開發(fā)網(wǎng)絡(luò)應(yīng)用程序的非常有效快捷的工具。
套接字存在于通信區(qū)域中。通信區(qū)域也叫地址族,它是一個抽象的概念,主要用于將通過套接字通信的進(jìn)程的共有特性綜合在一起。套接字通常只與同一區(qū)域的套接字交換數(shù)據(jù)(也有可能跨區(qū)域通信,但這只在執(zhí)行了某種轉(zhuǎn)換進(jìn)程后才能實(shí)現(xiàn))。
WindowsSockets只支持一個通信區(qū)域:
網(wǎng)際域(AF_INET),這個域被使用網(wǎng)際協(xié)議簇通信的進(jìn)程使用。2.網(wǎng)絡(luò)字節(jié)順序 不同的計算機(jī)存放多字節(jié)值的順序不同,有的機(jī)器在起始地址存放低位字節(jié)(低位先存),有的機(jī)器在起始地址存放高位字節(jié)(高位先存)。 基于Intel的CPU,即我們常用的PC機(jī)采用的是低位先存。為保證數(shù)據(jù)的正確性,在網(wǎng)絡(luò)協(xié)議中需要指定網(wǎng)絡(luò)字節(jié)順序。
TCP/IP協(xié)議使用16位整數(shù)和32位整數(shù)的高位先存格式。客戶機(jī)/服務(wù)器模式
1)在TCP/IP網(wǎng)絡(luò)應(yīng)用中,通信的兩個進(jìn)程間相互作用的主要模式是客戶機(jī)/服務(wù)器模式(client/server),即客戶向服務(wù)器提出請求,服務(wù)器接收到請求后,提供相應(yīng)的服務(wù)。
2)客戶機(jī)/服務(wù)器模式的建立基于以下兩點(diǎn):首先,建立網(wǎng)絡(luò)的起因是網(wǎng)絡(luò)中軟硬件資源、運(yùn)算能力和信息不均等,需要共享,從而造就擁有眾多資源的主機(jī)提供服務(wù),資源較少的客戶請求服務(wù)這一非對等作用。其次,網(wǎng)間進(jìn)程通信完全是異步的,相互通信的進(jìn)程間既不存在父子關(guān)系,又不共享內(nèi)存緩沖區(qū),因此需要一種機(jī)制為希望通信的進(jìn)程間建立聯(lián)系,為二者的數(shù)據(jù)交換提供同步,這就是基于客戶機(jī)/服務(wù)器模式的TCP/IP??蛻魴C(jī)/服務(wù)器模式客戶機(jī)/服務(wù)器模式在操作過程中采取的是主動請求的方式。
首先服務(wù)器方要先啟動,并根據(jù)請求提供相應(yīng)的服務(wù):①打開一個通信通道并告知本地主機(jī),它愿意在某一地址和端口上接收客戶請求。②等待客戶請求到達(dá)該端口。③接收到重復(fù)服務(wù)請求,處理該請求并發(fā)送應(yīng)答信號。接收到并發(fā)服務(wù)請求,要激活一個新的進(jìn)程(或線程)來處理這個客戶請求。新進(jìn)程(或線程)處理此客戶請求,并不需要對其它請求作出應(yīng)答。服務(wù)完成后,關(guān)閉此新進(jìn)程與客戶的通信鏈路,并終止。④返回第二步,等待另一客戶請求。⑤關(guān)閉服務(wù)器。
客戶方:①打開一個通信通道,并連接到服務(wù)器所在主機(jī)的特定端口。②向服務(wù)器發(fā)服務(wù)請求報文,等待并接收應(yīng)答;繼續(xù)提出請求。③請求結(jié)束后關(guān)閉通信通道并終止。WindowsSockets的實(shí)現(xiàn) WindowsSockets是MicrosoftWindows的網(wǎng)絡(luò)程序設(shè)計接口,它是從BerkeleySockets擴(kuò)展而來的,以動態(tài)鏈接庫的形式提供給我們使用。WindowsSockets在繼承了BerkeleySockets主要特征的基礎(chǔ)上,又對它進(jìn)行了重要擴(kuò)充。這些擴(kuò)充主要是提供了一些異步函數(shù),并增加了符合Windows消息驅(qū)動特性的網(wǎng)絡(luò)事件異步選擇機(jī)制。 WindowsSockets1.1和BerkeleySockets都是基于TCP/IP協(xié)議的;WindowsSockets2從WindowsSockets1.1發(fā)展而來,與協(xié)議無關(guān)并向下兼容,可以使用任何底層傳輸協(xié)議提供的通信能力,來為上層應(yīng)用程序完成網(wǎng)絡(luò)數(shù)據(jù)通訊,而不關(guān)心底層網(wǎng)絡(luò)鏈路的通訊情況,真正實(shí)現(xiàn)了底層網(wǎng)絡(luò)通訊對應(yīng)用程序的透明。套接字的類型1)流式套接字(SOCK_STREAM) 提供面向連接、可靠的數(shù)據(jù)傳輸服務(wù),數(shù)據(jù)無差錯、無重復(fù)的發(fā)送,且按發(fā)送順序接收。2)數(shù)據(jù)報式套接字(SOCK_DGRAM) 提供無連接服務(wù)。數(shù)據(jù)包以獨(dú)立包形式發(fā)送,不提供無錯保證,數(shù)據(jù)可能丟失或重復(fù),并且接收順序混亂。3)原始套接字(SOCK_RAW)網(wǎng)絡(luò)安全編程基本函數(shù)1、SOCKET類型
SOCKET是socket套接字類型,在WINSOCK2.H中有如下定義: typedefunsignedintu_int;
typedefu_int
SOCKET;
可知套接字實(shí)際上就是一個無符號整型,它將被Socket環(huán)境管理和使用。
套接字的使用流程:
被創(chuàng)建、設(shè)置、用來發(fā)送和接收數(shù)據(jù),最后會被關(guān)閉。
2WORD類型、MAKEWORD、LOBYTE和HIBYTE宏
WORD類型是一個16位的無符號整型,在WTYPES.H中被定義為: typedefunsignedshortWORD;
目的: 是提供兩個字節(jié)的存儲,在Socket中這兩個字節(jié)可以表示主版本號和副版本號。使用MAKEWORD宏可以給一個WORD類型賦值。
例如: 要表示主版本號2,副版本號0,可以使用以下代碼: WORDwVersionRequested; wVersionRequested=MAKEWORD(2,0); 注意低位內(nèi)存存儲主版本號2,高位內(nèi)存存儲副版本號0,其值為0x0002。 使用宏LOBYTE可以讀取WORD的低位字節(jié),HIBYTE可以讀取高位字節(jié)。3WSAStartup()函數(shù)WSAStartup函數(shù)被用來初始化Socket環(huán)境,它的定義如下:intPASCALFARWSAStartup(WORDwVersionRequired,LPWSADATAlpWSAData);返回值:
整型;若返回值為0,則初始化成功,若不為0則失敗。調(diào)用方式:
PASCAL(即標(biāo)準(zhǔn)類型,PASCAL等于__stdcall)參數(shù):
第一個參數(shù)為WORD類型,指明了Socket的版本號 第二個參數(shù)為WSADATA類型的指針。4WSADATA類型和LPWSADATA類型
WSADATA類型是一個結(jié)構(gòu),描述了Socket庫的一些相關(guān)信息,其結(jié)構(gòu)定義如下:typedefstructWSAData{WORDwVersion;WORDwHighVersion;charszDescription[WSADESCRIPTION_LEN+1];charszSystemStatus[WSASYS_STATUS_LEN+1];unsignedshortiMaxSockets;unsignedshortiMaxUdpDg;charFAR*lpVendorInfo;}WSADATA;typedefWSADATAFAR*LPWSADATA;wVersion: 存儲了Socket的版本類型。LPWSADATA: 是WSADATA的指針類型。 它們不用程序員手動填寫,而是通過Socket的初始化函數(shù)WSAStartup讀取出來。5socket()函數(shù)socket的創(chuàng)建函數(shù),其定義為:SOCKETPASCALFARsocket(intaf,inttype,intprotocol);intaf: 代表網(wǎng)絡(luò)地址族,目前只有一種取值是有效的,即AF_INET,代表internet地址族;inttype: 代表網(wǎng)絡(luò)協(xié)議類型,SOCK_DGRAM代表UDP協(xié)議,SOCK_STREAM代表TCP協(xié)議;intprotocol: 指定網(wǎng)絡(luò)地址族的特殊協(xié)議,目前無用,賦值0即可。
創(chuàng)建成功則返回值為SOCKET,若返回INVALID_SOCKET則失敗。6
bind()函數(shù)intbind(SOCKETs,conststructsockaddrFAR*name,intnamelen);參數(shù):
s:
指定要綁定的套接字
conststructsockaddrFAR*name:
了該套接字的本地地址信息,是指向sockaddr結(jié)構(gòu)的指針變量,由于該地址結(jié)構(gòu)是為所有的地址家族準(zhǔn)備的,這個結(jié)構(gòu)可能(通常會)隨所使用的網(wǎng)絡(luò)協(xié)議不同而不同
Namelen: 指定該地址結(jié)構(gòu)的長度。6
bind()函數(shù)intbind(SOCKETs,conststructsockaddrFAR*name,intnamelen);sockaddr結(jié)構(gòu)定義如下:structsockaddr{u_shortsa_family;charsa_data[14];};sa_family: 指定該地址家族,在這里必須設(shè)為AF_INET。sa_data: 僅僅是表示要求一塊內(nèi)存分配區(qū),起到占位的作用,該區(qū)域中指定與協(xié)議相關(guān)的具體地址信息。由于實(shí)際要求的只是內(nèi)存區(qū),所以對于不同的協(xié)議家族,用不同的結(jié)構(gòu)來替換sockaddr。除了sa_family外,sockaddr是按網(wǎng)絡(luò)字節(jié)順序表示的。在TCP/IP中,我們可以用sockaddr_in結(jié)構(gòu)替換sockaddr,以方便我們填寫地址信息。
續(xù):sockaddr_in的定義如下:
structsockaddr_in{
shortsin_family;
unsignedshortsin_port;
struct
in_addrsin_addr;
charsin_zero[8];};
其中,sin_family表示地址族,對于IP地址,sin_family成員將一直是AF_INET。成員sin_port指定的是將要分配給套接字的端口。成員sin_addr給出的是套接字的主機(jī)IP地址。而成員sin_zero只是一個填充數(shù),以使sockaddr_in結(jié)構(gòu)和sockaddr結(jié)構(gòu)的長度一樣。如果這個函數(shù)調(diào)用成功,它將返回0。如果調(diào)用失敗,這個函數(shù)就會返回一個SOCKET_ERROR,錯誤信息可以通過WSAGetLastError函數(shù)返回。將IP地址指定為INADDR_ANY,允許套接字向任何分配給本地機(jī)器的IP地址發(fā)送或接收數(shù)據(jù)。多數(shù)情況下,每個機(jī)器只有一個IP地址,但有的機(jī)器可能會有多個網(wǎng)卡,每個網(wǎng)卡都可以有自己的IP地址,用INADDR_ANY可以簡化應(yīng)用程序的編寫。將地址指定為INADDR_ANY,允許一個獨(dú)立應(yīng)用接受發(fā)自多個接口的回應(yīng)。如果我們只想讓套接字使用多個IP中的一個地址,就必須指定實(shí)際地址,要做到這一點(diǎn),可以用inet_addr()函數(shù),這個函數(shù)需要一個字符串作為其參數(shù),該字符串指定了以點(diǎn)分十進(jìn)制格式表示的IP地址(如192.168.0.16)。而且inet_addr()函數(shù)會返回一個適合分配給S_addr的u_long類型的數(shù)值。inet_ntoa()函數(shù)會完成相反的轉(zhuǎn)換,它接受一個in_addr結(jié)構(gòu)體類型的參數(shù)并返回一個以點(diǎn)分十進(jìn)制格式表示的IP地址字符串。7setsockopt()函數(shù) 這個函數(shù)用來設(shè)置Socket的屬性,若不能正確設(shè)置socket屬性,則數(shù)據(jù)的發(fā)送和接收會失敗。定義如下:intPASCALFARsetsockopt(SOCKETs,intlevel,intoptname,constcharFAR*optval,intoptlen);返回值:int類型,0代表成功,SOCKET_ERROR代表有錯誤發(fā)生。SOCKETs: 代表要設(shè)置的套接字;intlevel: 代表要設(shè)置的屬性所處的層次,層次包含以下取值:SOL_SOCKET代表套接字層次;IPPROTO_TCP代表TCP協(xié)議層次,IPPROTO_IP代表IP協(xié)議層次(后面兩個我都沒有用過);intoptname: 代表設(shè)置參數(shù)的名稱,SO_BROADCAST代表允許發(fā)送廣播數(shù)據(jù)的屬性,其它屬性可參考MSDN;constcharFAR*optval: 代表指向存儲參數(shù)數(shù)值的指針,注意這里可能要使用reinterpret_cast類型轉(zhuǎn)換;intoptlen: 代表存儲參數(shù)數(shù)值變量的長度。8sockaddr_in、in_addr類型,inet_addr、inet_ntoa函數(shù)sockaddr_in定義了socket發(fā)送和接收數(shù)據(jù)包的地址,定義如下:structsockaddr_in{shortsin_family;u_shortsin_port;
structin_addr
sin_addr;charsin_zero[8];};其中in_addr的定義如下:structin_addr{union{struct{u_chars_b1,s_b2,s_b3,s_b4;}S_un_b;struct{u_shorts_w1,s_w2;}S_un_w;u_longS_addr;}S_un;structin_addr{union{struct{u_chars_b1,s_b2,s_b3,s_b4;}S_un_b;struct{u_shorts_w1,s_w2;}S_un_w;
u_long
S_addr;}S_un; 首先闡述in_addr的含義,很顯然它是一個存儲ip地址的聯(lián)合體,有三種表達(dá)方式:第一種:用四個字節(jié)來表示IP地址的四個數(shù)字;第二種:用兩個雙字節(jié)來表示IP地址;第三種:用一個長整型來表示IP地址。 給in_addr賦值的一種最簡單方法是使用inet_addr函數(shù),它可以把一個代表IP地址的字符串賦值轉(zhuǎn)換為in_addr類型,如addrto.sin_addr.s_addr=inet_addr("192.168.0.2");其反函數(shù)是inet_ntoa():networktoascii
將網(wǎng)絡(luò)地址轉(zhuǎn)換成“.”點(diǎn)隔的字符串格式,即把一個in_addr類型轉(zhuǎn)換為一個字符串。structsockaddr_in{shortsin_family;u_shortsin_port;structin_addrsin_addr;charsin_zero[8];};sockaddr_in的含義比in_addr的含義要廣泛,其各個字段的含義和取值如下:shortsin_family: 代表網(wǎng)絡(luò)地址族,如前所述,只能取值A(chǔ)F_INET;u_shortsin_port: 代表IP地址端口,由程序員指定;structin_addrsin_addr: 代表IP地址;charsin_zero[8]: 是為了保證sockaddr_in與SOCKADDR類型的長度相等而填充進(jìn)來的字段。程序示例:以下代表指明了廣播地址,端口號為7861的一個地址:sockaddr_inaddrto; //發(fā)往的地址memset(&addrto,0,sizeof(addrto));addrto.sin_family=AF_INET; //地址類型為internetworkaddrto.sin_addr.s_addr=INADDR_BROADCAST; //設(shè)置ip為廣播地址addrto.sin_port=htons(7861); //端口號為78619sockaddr()類型 sockaddr類型是用來表示Socket地址的類型,同上面的sockaddr_in類型相比,sockaddr的適用范圍更廣,因?yàn)閟ockaddr_in只適用于TCP/IP地址。Sockaddr的定義如下: structsockaddr{ u_shortsa_family; charsa_data[14]; }; 可知sockaddr有16個字節(jié),而sockaddr_in也有16個字節(jié),所以sockaddr_in是可以強(qiáng)制類型轉(zhuǎn)換為sockaddr的。事實(shí)上也往往使用這種方法。10Sleep()函數(shù) 線程掛起函數(shù),表示線程掛起一段時間。Sleep(1000)表示掛起一秒。定義于WINBASE.H頭文件中。WINBASE.H又被包含于WINDOWS.H中,然后WINDOWS.H被WINSOCK2.H包含。 所以使用Sleep函數(shù)不需要包含其它頭文件。11sendto()函數(shù)在Socket中有兩套發(fā)送和接收函數(shù),一是sendto和recvfrom;二是send和recv。前一套在函數(shù)參數(shù)中要指明地址;而后一套需要先將套接字和一個地址綁定,然后直接發(fā)送和接收,不需綁定地址。sendto的定義如下:intPASCALFARsendto(SOCKETs,constcharFAR*buf,intlen,intflags,conststructsockaddrFAR*to,inttolen);第一個參數(shù)就是套接字;第二個參數(shù)是要傳送的數(shù)據(jù)指針;第三個參數(shù)是要傳送的數(shù)據(jù)長度(字節(jié)數(shù));第四個參數(shù)是傳送方式的標(biāo)識,如果不需要特殊要求則可以設(shè)置為0,其它值請參考MSDN;第五個參數(shù)是目標(biāo)地址,注意這里使用的是sockaddr的指針;第六個參數(shù)是地址的長度;返回值為整型,如果成功,則返回發(fā)送的字節(jié)數(shù),失敗則返回SOCKET_ERROR。12WSAGetLastError()函數(shù) 該函數(shù)用來在Socket相關(guān)API失敗后讀取錯誤碼,根據(jù)這些錯誤碼可以對照查出錯誤原因。13closesocket()函數(shù) 關(guān)閉套接字,其參數(shù)為SOCKET類型。成功返回0,失敗返回SOCKET_ERROR。
14WSACleanup()函數(shù) 卸載Socket庫,釋放系統(tǒng)資源。返回值為0表示成功,SOCKET_ERROR表示失敗。下面是一個用UDP發(fā)送廣播報文的程序示例:#include<winsock2.h>#include<iostream.h>voidmain(){SOCKETsock;//socket套接字charszMsg[]="thisisaUDPtestpackage";//被發(fā)送的字段
//1.啟動SOCKET庫,版本為2.0WORDwVersionRequested;WSADATAwsaData;interr;wVersionRequested=MAKEWORD(2,0);err=WSAStartup(wVersionRequested,&wsaData);if(0!=err)//檢查Socket初始化是否成功{cout<<"Socket2.0初始化失敗,Exit!";return;}//檢查Socket庫的版本是否為2.0if(LOBYTE(wsaData.wVersion)!=2||HIBYTE(wsaData.wVersion)!=0){WSACleanup();return;}
//2.創(chuàng)建socket,sock=socket(AF_INET,//internetwork:UDP,TCP,etcSOCK_DGRAM,//SOCK_DGRAM說明是UDP類型0//protocol);if(INVALID_SOCKET==sock){cout<<"Socket創(chuàng)建失敗,Exit!";return;}//3.設(shè)置該套接字為廣播類型,boolopt=true;setsockopt(sock,SOL_SOCKET,SO_BROADCAST,reinterpret_cast<charFAR*>(&opt),sizeof(opt));//4.設(shè)置發(fā)往的地址sockaddr_inaddrto;//發(fā)往的地址memset(&addrto,0,sizeof(addrto));addrto.sin_family=AF_INET;//地址類型為internetworkaddrto.sin_addr.s_addr=INADDR_BROADCAST;//設(shè)置ip為廣播地址addrto.sin_port=htons(7861);//端口號為7861intnlen=sizeof(addrto);unsignedintuIndex=1;
while(true){Sleep(1000);//程序休眠一秒//向廣播地址發(fā)送消息if(sendto(sock,szMsg,strlen(szMsg),0,(sockaddr*)&addrto,nlen)==SOCKET_ERROR)cout<<WSAGetLastError()<<endl;elsecout<<uIndex++<<":anUDPpackageissended."<<endl;}if(!closesocket(sock))//關(guān)閉套接字{WSAGetLastError();return;}if(!WSACleanup())//關(guān)閉Socket庫{WSAGetLastError();return;}}一、基于TCP(面向連接)的socket編程服務(wù)器端程序:1、加載套接字庫,創(chuàng)建套接字(WSAStartup()/socket()); 2、將套接字綁定到一個本地地址和端口上(bind())。3、將套接字設(shè)置為監(jiān)聽模式等待連接請求(listen());4、當(dāng)請求到來后,接受連接請求,返回一個新的對應(yīng)于此次連接的套接字(accept())。5、用返回的套接字和客戶端進(jìn)行通信(send()/recv());6、返回,等待另一客戶請求。7、關(guān)閉套接字,卸載的套接字庫(closesocket()/WSACleanup())。客戶端程序:1、加載套接字庫,創(chuàng)建套接字(WSAStartup()/socket()); 2、向服務(wù)器發(fā)出連接請求(connect());3、和服務(wù)器端進(jìn)行通信(send()/recv());4、關(guān)閉套接字,卸載的套接字庫(closesocket()/WSACleanup())。 在VC中進(jìn)行WINSOCK的API編程開發(fā)的時候,需要在項(xiàng)目中使用下面的三個文件,否則會出現(xiàn)編譯錯誤:1.WINSOCK2.H: WINSOCKAPI的頭文件,需要包含在項(xiàng)目中。2.ws2_32.lib:(WSOCK32.LIB)
WINSOCKAPI鏈接庫文件。在使用中,一定要把它作為項(xiàng)目的非缺省的連接庫包含到項(xiàng)目文件中去。
設(shè)置方法:工程->設(shè)置->鏈接->對象/庫模塊3.WINSOCK.DLL: WINSOCK的動態(tài)連接庫,位于WINDOWS的安裝目錄下。服務(wù)器端操作socket(套接字)
1)在初始化階段調(diào)用WSAStartup()
此函數(shù)在應(yīng)用程序中初始化WindowsSocketsDLL,只有此函數(shù)調(diào)用成功后,應(yīng)用程序才可以再調(diào)用其他WindowsSocketsDLL中的API函數(shù)。 在程式中調(diào)用該函數(shù)的形式如下:intWSAStartup(WORDwVersionRequested,LPWSADATAlpWSAData);intWSAStartup(WORDwVersionRequested,LPWSADATAlpWSAData);wVersionRequested: 用于指定準(zhǔn)備加載的Winsock庫的版本。 高位字節(jié)指定所需要的Winsock庫的副版本,而低位字節(jié)則是主版本。 可用MAKEWORD(x,y)(其中,x是高位字節(jié),y是低位字節(jié))方便地獲得wVersionRequested的正確值。lpWSAData: 是指向WSADATA結(jié)構(gòu)的指針,用來存儲系統(tǒng)傳回的關(guān)于加載的庫版本的資料,是一個結(jié)構(gòu)體。2)建立Socket
初始化WinSock的動態(tài)連接庫后,需要在服務(wù)器端建立一個監(jiān)聽的Socket,為此可以調(diào)用Socket()函數(shù)用來建立這個監(jiān)聽的Socket,并定義此Socket所使用的通信協(xié)議。定義方式:SOCKETPASCALFARsocket(intaf,inttype,intprotocol)參數(shù):
af: addressfamily,目前只提供AF_INET(PF_INET);
type: Socket的類型(SOCK_STREAM(流格式)、SOCK_DGRAM(數(shù)據(jù)報格式));
protocol: 特殊的通訊協(xié)定(不指定則設(shè)為0); 此函數(shù)調(diào)用成功返回Socket對象,失敗則返回INVALID_SOCKET(調(diào)用WSAGetLastError()可得知原因,所有WinSocket的函數(shù)都可以使用這個函數(shù)來獲取失敗的原因)。3)綁定端口
為監(jiān)聽的Socket指定一個地址及端口(Port),這樣客戶端才知道待會要連接哪一個地址的哪個端口,為此需調(diào)用bind()函數(shù):intPASCALFARbind(SOCKETs,conststructsockaddrFAR*name,intnamelen);參數(shù): s:Socket對象名; name:Socket的地址值,這個地址必須是執(zhí)行這個程式所在機(jī)器的IP地址; namelen:name的長度; 該函數(shù)調(diào)用成功返回0,否則返回SOCKET_ERROR。 如果使用者不在意地址或端口的值,那么可以設(shè)定地址為INADDR_ANY及Port為0,WindowsSockets會自動將其設(shè)定適當(dāng)之地址及Port(1024到5000之間的值)。此后可以調(diào)用getsockname()函數(shù)來獲知其被設(shè)定的值。4)監(jiān)聽
當(dāng)服務(wù)器端的Socket對象綁定完成之后,服務(wù)器端必須建立一個監(jiān)聽的隊(duì)列來接收客戶端的連接請求。listen()函數(shù)使服務(wù)器端的Socket進(jìn)入監(jiān)聽狀態(tài),并設(shè)定可以建立的最大連接數(shù)(目前最大值限制為5,最小值為1)。該函數(shù)調(diào)用成功返回0,否則返回SOCKET_ERROR。
intPASCALFARlisten(SOCKETs,intbacklog);參數(shù):s:
需要建立監(jiān)聽的Socket;backlog:
最大連接個數(shù);服務(wù)器端的Socket調(diào)用完listen()后,如果此時客戶端調(diào)用connect()函數(shù)提出連接申請的話,Server端必須再調(diào)用accept()函數(shù),這樣服務(wù)器端和客戶端才算正式完成通信程序的連接動作。4)監(jiān)聽 為了知道什么時候客戶端提出連接要求,從而服務(wù)器端的Socket在恰當(dāng)?shù)臅r候調(diào)用accept()函數(shù)完成連接的建立,我們就要使用WSAAsyncSelect()函數(shù),讓系統(tǒng)主動來通知我們有客戶端提出連接請求了。該函數(shù)調(diào)用成功返回0,否則返回SOCKET_ERROR。intPASCALFARWSAAsyncSelect(SOCKETs,HWNDhWnd,unsignedintwMsg,longlEvent);參數(shù):s:
Socket對象;hWnd:
接收消息的窗口句柄;wMsg:
傳給窗口的消息;lEvent: 被注冊的網(wǎng)絡(luò)事件,也即是應(yīng)用程序向窗口發(fā)送消息的網(wǎng)路事件,該值為下列值FD_READ、FD_WRITE、FD_OOB、FD_ACCEPT、FD_CONNECT、FD_CLOSE的組;FD_READ: 希望在套接字S收到數(shù)據(jù)時收到消息;FD_WRITE: 希望在套接字S上可以發(fā)送數(shù)據(jù)時收到消息;FD_ACCEPT: 希望在套接字S上收到連接請求時收到消息;FD_CONNECT: 希望在套接字S上連接成功時收到消息;FD_CLOSE: 希望在套接字S上連接關(guān)閉時收到消息;FD_OOB: 希望在套接字S上收到帶外數(shù)據(jù)時收到消息。 具體應(yīng)用時,wMsg應(yīng)是在應(yīng)用程序中定義的消息名稱,而消息結(jié)構(gòu)中的lParam則為以上各種網(wǎng)絡(luò)事件名稱。 即:
可以在窗口處理自定義消息函數(shù)中使用以下結(jié)構(gòu)來響應(yīng),Socket的不同事件:switch(lParam){caseFD_READ:… break; caseFD_WRITE、… break;… }
5)服務(wù)器端接受客戶端的連接請求
當(dāng)Client提出連接請求時,Server端hwnd視窗會收到WinsockStack送來我們自定義的一個消息,這時,我們可以分析lParam,然后調(diào)用相關(guān)的函數(shù)來處理此事件。為了使服務(wù)器端接受客戶端的連接請求,就要使用accept()函數(shù),該函數(shù)新建一Socket與客戶端的Socket相通,原先監(jiān)聽之Socket繼續(xù)進(jìn)入監(jiān)聽狀態(tài),等待他人的連接要求。該函數(shù)調(diào)用成功返回一個新產(chǎn)生的Socket對象,否則返回INVALID_SOCKET。SOCKETPASCALFARaccept(SCOKETs,structsockaddrFAR*addr,intFAR*addrlen);s:
Socket的識別碼;addr: 存放來連接的客戶端的地址;addrlen:
addr的長度6)結(jié)束socket連接
結(jié)束服務(wù)器和客戶端的通信連接是很簡單的,這一過程可以由服務(wù)器或客戶機(jī)的任一端啟動,只要調(diào)用closesocket()就可以了,而要關(guān)閉Server端監(jiān)聽狀態(tài)的socket,同樣也是利用此函數(shù)。另外,與程序啟動時調(diào)用WSAStartup()憨數(shù)相對應(yīng),程式結(jié)束前,需要調(diào)用WSACleanup()來通知WinsockStack釋放Socket所占用的資源。這兩個函數(shù)都是調(diào)用成功返回0,否則返回SOCKET_ERROR。intPASCALFARclosesocket(SOCKETs); s:Socket的識別碼;intPASCALFARWSACleanup(void); 參數(shù):無二、客戶端Socket的操作
1)建立客戶端的Socket
客戶端應(yīng)用程序首先也是調(diào)用WSAStartup()函數(shù)來與Winsock的動態(tài)連接庫建立關(guān)系,然后同樣調(diào)用socket()來建立一個TCP或UDPsocket(相同協(xié)定的sockets才能相通,TCP對TCP,UDP對UDP)。 與服務(wù)器端的socket同的是:客戶端的socket可以調(diào)用bind()函數(shù),由自己來指定IP地址及port號碼;但是也可以不調(diào)用bind(),而由Winsock來自動設(shè)定IP地址及port號碼。
2)提出連接申請
客戶端的Socket使用connect()函數(shù)來提出與服務(wù)器端的Socket建立連接的申請,函數(shù)調(diào)用成功返回0,否則返回SOCKET_ERROR。intPASCALFARconnect(SOCKETs,conststructsockaddrFAR*name,intnamelen);s: Socket的識別碼;name: Socket想要連接的對方地址;namelen: name的長度三、數(shù)據(jù)的傳送
一般情況下TCPSocket的數(shù)據(jù)發(fā)送和接收是調(diào)用send()及recv()這兩個函數(shù)來達(dá)成,而UDPSocket則是用sendto()及recvfrom()這兩個函數(shù),這兩個函數(shù)調(diào)用成功發(fā)揮發(fā)送或接收的資料的長度,否則返回SOCKET_ERROR。數(shù)據(jù)的發(fā)送:intPASCALFARsend(SOCKETs,constcharFAR*buf,intlen,intflags);s: Socket的識別碼buf: 存放要傳送的資料的暫存區(qū)lenbuf: 長度flags: 此函數(shù)被調(diào)用的方式
數(shù)據(jù)的接收:intPASCALFARrecv(SOCKETs,charFAR*buf,intlen,intflags);參數(shù):s: Socket的識別碼buf: 存放接收到的資料的暫存區(qū)lenbuf: 長度flags: 此函數(shù)被調(diào)用的方式對StreamSocket言,我們可以接收到目前inputbuffer內(nèi)有效的資料,但其數(shù)量不超過len的大小。服務(wù)器端編程的步驟:1、加載套接字庫,創(chuàng)建套接字(WSAStartup()/socket())
2、將套接字綁定到一個本地地址和端口上(bind())。3、將套接字設(shè)置為監(jiān)聽模式等待連接請求(listen());4、當(dāng)請求到來后,接受連接請求,返回一個新的對應(yīng)于此次連接的套接字(accept())。5、用返回的套接字和客戶端進(jìn)行通信(send()/recv());6、返回,等待另一客戶請求。7、關(guān)閉套接字,關(guān)閉加載的套接字庫(closesocket()/WSACleanup())。一、基于TCP(面向連接)的socket編程服務(wù)器端編程如下:#include<stdio.h>#include<Winsock2.h>voidmain(){ WORDwVersionRequested; WSADATAwsaData; interr; wVersionRequested=MAKEWORD(1,1); err=WSAStartup(wVersionRequested,&wsaData); if(err!=0) {return; } if(LOBYTE(wsaData.wVersion)!=1|| HIBYTE(wsaData.wVersion)!=1) { WSACleanup(); return;}1.加載套接字庫SOCKET
sockSrv=socket(AF_INET,SOCK_STREAM,0);SOCKADDR_IN
addrSrv;addrSrv.sin_family=AF_INET;addrSrv.sin_port=htons(6000);addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));listen(sockSrv,5);SOCKADDR_IN
addrClient;intlen=sizeof(SOCKADDR);while(1){ SOCKETsockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len); charsendBuf[50]; sprintf(sendBuf,"歡迎%s來到美麗的閩南科技學(xué)院!!!",inet_ntoa(addrClient.sin_addr));
send(sockConn,sendBuf,strlen(sendBuf)+1,0); charrecvBuf[50];
recv(sockConn,recvBuf,50,0); printf("%s\n",recvBuf);
closesocket(sockConn);}}htonl():
將32位的主機(jī)字節(jié)順序轉(zhuǎn)化為32位的網(wǎng)絡(luò)字節(jié)順序
htons():
將16位的主機(jī)字節(jié)順序轉(zhuǎn)化為16位的網(wǎng)絡(luò)字節(jié)順序(ip地址是32位的端口號是16位的)2.創(chuàng)建套接字htons()--"HosttoNetworkShort"htonl()--"HosttoNetworkLong"ntohs()--"NetworktoHostShort"ntohl()--"NetworktoHostLong“3.綁定套接字4.監(jiān)聽等待請求5.數(shù)據(jù)通信(發(fā)送)6.數(shù)據(jù)通信(接收)7.關(guān)閉套接字客戶端編程的步驟:1、加載套接字庫,創(chuàng)建套接字(WSAStartup()/socket());2、向服務(wù)器發(fā)出連接請求(connect());3、和服務(wù)器端進(jìn)行通信(send()/recv());4、關(guān)閉套接字,卸載加載的套接字庫(closesocket()/WSACleanup()),釋放系統(tǒng)資源。客戶端編程如下:#include<stdio.h>#include<Winsock2.h>voidmain(){ WORDwVersionRequested; WSADATAwsaData; interr; wVersionRequested=MAKEWORD(1,1); err=WSAStartup(wVersionRequested,&wsaData); if(err!=0) { return; } if(LOBYTE(wsaData.wVersion)!=1|| HIBYTE(wsaData.wVersion)!=1) { WSACleanup(); return; }SOCKETsockClient=socket(AF_INET,SOCK_STREAM,0);SOCKADDR_INaddrSrv;addrSrv.sin_family=AF_INET;addrSrv.sin_port=htons(6000);addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));charrecvBuf[50];recv(sockClient,recvBuf,50,0);printf("%s\n",recvBuf);send(sockClient,"嗨,你好,我是某某某",strlen("嗨,你好,我是某某某")+1,0);closesocket(sockClient);WSACleanup();}6.數(shù)據(jù)通信(接收)3.轉(zhuǎn)換為u_long操作步驟:1.新建工程TCP;2.新建C++源程序:TCPserv,加載ws2_32.lib;3.切換到FileView,新建工程TCPclient,新建C++源程序:TCPclient,加載ws2_32.lib;4.分別運(yùn)行Serv和client;可能用到的方式或快捷鍵:1.組建,編譯微型條2.Ctrl+Tab(切換程序)思考: 編寫基于UDP(面向無連接)的socket程序二、基于UDP(面向無連接)的socket編程服務(wù)器端(接收端)程序:1、創(chuàng)建套接字(socket)。 2、將套接字綁定到一個本地地址和端口上(bind)。3、等待接收數(shù)據(jù)(recvfrom)4、關(guān)閉套接字,釋放系統(tǒng)資源。客戶端(發(fā)送端)程序:1、創(chuàng)建套接字(socket)。
2、向服務(wù)器發(fā)送數(shù)據(jù)(sendto)。3、關(guān)閉套接字,釋放系統(tǒng)資源。服務(wù)器端編程如下:#include<stdio.h>#include<Winsock2.h>voidmain(){ WORDwVersionRequested; WSADATAwsaData; interr; wVersionRequested=MAKEWORD(1,1); err=WSAStartup(wVersionRequested,&wsaData); if(err!=0) {return; } if(LOBYTE(wsaData.wVersion)!=1|| HIBYTE(wsaData.wVersion)!=1) { WSACleanup(); return;}1.加載套接字庫SOCKET
sockSrv=socket(AF_INET,SOCK_DGRAM,0);SOCKADDR_IN
addrSrv;addrSrv.sin_family=AF_INET;addrSrv.sin_port=htons(6000);addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//基于UDP面向無連接,因此無需listen()和accept()SOCKADDR_IN
addrClient;intlen=sizeof(SOCKADDR); charrecvBuf[50];
recvfrom(sockSrv,recvBuf,50,(SOCKADDR*)&addrSrv,&len); printf("%s\n",recvBuf);
closesocket(sockConn);}}客戶端的編程如下:#include<stdio.h>#include<Winsock2.h>voidmain(){ WORDwVersionRequested; WSADATAwsaData; interr; wVersionRequested=MAKEWORD(1,1); err=WSAStartup(wVersionRequested,&wsaData); if(err!=0) { return; } if(LOBYTE(wsaData.wVersion)!=1|| HIBYTE(wsaData.wVersion)!=1) { WSACleanup(); return; }SOCKETsockClient=socket(AF_INET,SOCK_DGRAM,0);SOCKADDR_INaddrSrv;addrSrv.sin_family=AF_INET;addrSrv.sin_port=htons(6000);addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");sendto(sockClient,“嗨,你好,我是某某某”,strlen(“嗨,你好,我是某某某”)+1,0,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));closesocket(sockClient);WSACleanup();}三、基于UDP的聊天程序優(yōu)點(diǎn): 速度快,占用資源少,丟失可根據(jù)上下文理解或選擇重發(fā)消息。服務(wù)器端編程如下:#include<stdio.h>#include<Winsock2.h>voidmain(){ WORDwVersionRequested; WSADATAwsaData; interr; wVersionRequested=MAKEWORD(1,1); err=WSAStartup(wVersionRequested,&wsaData); if(err!=0) {return; } if(LOBYTE(wsaData.wVersion)!=1|| HIBYTE(wsaData.wVersion)!=1) { WSACleanup(); return;}SOCKET
sockSrv=socket(AF_INET,SOCK_DGRAM,0);SOCKADDR_IN
addrSrv;addrSrv.sin_family=AF_INET;addrSrv.sin_port=htons(6000);addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));//基于UDP面向無連接,因此無需listen()和accept()charrecvBuf[100];charsendBuf[100];chartempBuf[100];SOCKADDR_IN
addrClient;intlen=sizeof(SOCKADDR);while(1){ recvfrom(sockSrv,recvBuf,100,0,(SOCKADDR*)&addrClient,&len);If(recvBuf[0]==‘q’){sendto(sockSrv,"q",strlen("q")+1,0,(SOCKADDR*)&addrClient,len); printf("Chatend!!!\n"); break;} sprintf(tempBuf,"%s說:%s",inet_ntoa(addrClient.sin_addr),recvBuf); printf("%s\n",tempBuf); printf("Pleaseinputdata:\n");
gets(sendBuf);
sendto(sockSrv,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrClient,len);}
closesocket(sockSrv);WSACleanup();}客戶端編程如下:#include<stdio.h>#include<Winsock2.h>voidmain(){ WORDwVersionRequested; WSADATAwsaData; interr; wVersionRequested=MAKEWORD(1,1); err=WSAStartup(wVersionRequested,&wsaData); if(err!=0) { return; } if(LOBYTE(wsaData.wVersion)!=1|| HIBYTE(wsaData.wVersion)!=1) { WSACleanup(); return; }SOCKETsockClient=socket(AF_INET,SOCK_DGRAM,0);SOCKADDR_INaddrSrv;addrSrv.sin_family=AF_INET;addrSrv.sin_port=htons(6000);addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");charrecvBuf[300];charsendBuf[300];chartempBuf[300];intlen=sizeof(SOCKADDR);while(1){ printf("請輸入數(shù)據(jù):\n");
gets(sendBuf);
sendto(sockClient,sendBuf,strlen(sendBuf)+1,0,(SOCKADDR*)&addrSrv,len);
recvfrom(sockClient,rec
溫馨提示
- 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)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年度環(huán)保材料研發(fā)代理合同
- 大數(shù)據(jù)交易服務(wù)平臺的技術(shù)架構(gòu)與實(shí)施
- 2025年度家庭共同購置房產(chǎn)協(xié)議書模板
- 2025年度城市綜合體項(xiàng)目合作協(xié)議-@-1
- 再審申請書幾份
- 2025年中國倉柵式汽車行業(yè)市場深度研究及發(fā)展趨勢預(yù)測報告
- 2025-2030年中國脆木瓜行業(yè)深度研究分析報告
- 2025年度企業(yè)信用保證借款合同標(biāo)準(zhǔn)版
- 大一新生轉(zhuǎn)專業(yè)申請書
- 文藝社申請書
- 2025年初級社會工作者綜合能力全國考試題庫(含答案)
- 復(fù)工復(fù)產(chǎn)安全培訓(xùn)考試題
- vc約起來史上最全180個知名投資人聯(lián)系方式
- 中國酒文化英文介紹
- 部編版五年級語文下冊課文四字詞總結(jié)
- 社會穩(wěn)定風(fēng)險評估報告風(fēng)險評估參考
- 制冷操作證培訓(xùn)教材-制冷與空調(diào)設(shè)備運(yùn)行操作作業(yè)培課件
- 勞動感悟800字作文30篇
- 上下樓梯安全我知道安全教育課件
- 市級臨床重點(diǎn)??粕陥髸?/a>
- 《醫(yī)院重點(diǎn)??平ㄔO(shè)專項(xiàng)資金管理辦法》
評論
0/150
提交評論