windows socket 網路編程基本知識(中 下 )

來源:互聯網
上載者:User

下面介紹網路7層協議在WINDOWS的實現:

7層協議 WIN系統
________________________________________
7 應用程式層 7 應用程式
________________________________________________
6 展示層 6 WINSOCK API(DLL)
___________________________________________
5 會話層 5 SPI(DLL)
__________________________________________________
4 傳輸層 4 TDI(VXD,SYS)
___________________________________________________
3 網路層 3 NDIS(VXD,SYS)
__________________________________________________
2 資料連結層 2 網路卡驅動程式(VXD,SYS)
___________________________________________
1 物理層 1 網卡
_________________________________________________
相信這個映射圖可以讓大家比較清楚瞭解他們的對應關係

TCP協議圖示

應用程式協議 HTTP FTP TELNET
傳輸協議 TCP UDP
網際協議 IP
物理層協議 網卡

IP協議保證資料的傳輸,TCP協議保證資料轉送的品質.
TCP/IP協議基於四層結構:應用程式層,傳輸層,網路層,介面層,資料在傳輸時每通過一層就要在資料上加個頭,其中的資料供接受端同層使用,在
接收端,每經過一層就把頭去掉,來保證傳輸資料格式的一致.

TCP頭部結構:
16位源連接埠號碼 16位目的連接埠號碼
_______________________________________________________________________________
32位序號
___________________________________________________________________________
32位確認號
_____________________________________________________________________________________
4位首部長度+6位保留字 6位標誌 16位視窗大小
_______________________________________________________________________________________
16位效驗和 16位緊急資料位移量
_____________________________________________________________________________________
資料區段
_______________________________________________________________________________

IP頭部結構:
4位IP版本號碼 4位首部長度 8位服務類型 16位總長度
___________________________________________________________________________________________
16位標示 3位標誌和位移
__________________________________________________________________________
8位存留時間 8位協議 16位IP首部效驗和
_________________________________________________________________________________________
32位源IP地址
___________________________________________________________________________________________
32位目的IP地址
________________________________________________________________________________________
TCP頭和資料

____________________________________________________________________________
第四節 關於伺服器和用戶端編程

在網路編程中,最常用和最基礎的就是WINSOCK. 現在我們討論WINDOWS下的SOCKET編程.

大凡在WIN32平台上的WINSOCK編程都要經過下列步驟:
定義變數->獲得WINDOCK版本->載入WINSOCK庫->初始化->建立通訊端->設定通訊端選項->關閉通訊端>卸載WINSOCK庫->釋放資源

下面介紹WINSOCK C/S的建立過程:

伺服器 用戶端
________________________________________________
1 初始化WSA 1 初始化WSA
____________________________________________________
2 建立一個SOCKET 2 建立一個SOCKET
_____________________________________________________
3 綁定SOCKET 3 串連到伺服器
_____________________________________________________
4 在指定的連接埠監聽 4 發送和接受資料
_____________________________________________________
5 接受一個串連 5 中斷連線
______________________________________________________-
6 發送和接受資料
___________________________________________________
7 中斷連線
__________________________________________________

大家注意,在VC中進行WINSOCK編程時,需要引入如下兩個庫檔案:WINSOCK.H(這個是WINSOCK API的標頭檔,WIN2K以上支援WINSOCK2,所以
可以用WINSOCK2.H);Ws2_32.lib(WINSOCK API串連庫檔案).
使用方式如下:
#include
#pragma comment(lib,"ws2_32.lib")

大家注意,在VC中進行WINSOCK編程時,需要引入如下兩個庫檔案:WINSOCK.H(這個是WINSOCK API的標頭檔,WIN2K以上支援WINSOCK2,所以
可以用WINSOCK2.H);Ws2_32.lib(WINSOCK API串連庫檔案).
使用方式如下:
#include
#pragma comment(lib,"ws2_32.lib")

下面我們通過具體的代碼示範伺服器和用戶端的工作流程:

首先,建立一個WSADATA結構,通常用wsaData
WSADATA wsaData;

然後,調用WSAStartup函數,這個函數是串連應用程式與winsock.dll的第一個調用.其中,第一個參數是WINSOCK 版本號碼,第二個參數是指向
WSADATA的指標.該函數返回一個INT型值,通過檢查這個值來確定初始化是否成功.調用格式如下:WSAStartup(MAKEWORD(2,2),&wsaData),其中
MAKEWORD(2,2)表示使用WINSOCK2版本.wsaData用來儲存系統傳回的關於WINSOCK的資料.

if(iResuit=WSAStartup(MAKEWORD(2,2),&wsaData)!=0)
{
printf("WSAStartup failed:%d",GetLastError()); //傳回值不等與0,說明初始化失敗
ExitProcess(); //退出程式
}

應用程式在完成對請求的SOCKET庫使用後,要調用WSACleanup函數來接觸SOCKET庫的綁定,並且釋放資源.

注意WSAStartup初始化後,必須建立一個SOCKET結構來儲存SOCKET控制代碼.

下面我們建立一個SOCKET.

首先我們建立一個m_socket的SOCKET控制代碼,接著調用socket()函數,函數傳回值儲存在m_socket中.我們使用AF_INFE,SOCK_STREAM,IPPROTO_TCP
三個參數.第一個表示地址族,AF_INFE表示TCP/IP族,第二個表示服務類型,在WINSOCK2中,SOCKET支援以下三種類型;

SOCK_STREAM 流式通訊端
SOCK_DGRAM 資料通訊端
SOCK_RAW 原始通訊端

第三個參數表示協議:

IPPROTO_UDP UDP協議 用於無串連資料通訊端
IPPROTO_TCP TCP協議 用於流式通訊端
IPPROTO_ICMP ICMP協議用於原始通訊端

m_socket=socket(AF_INFE,SOCK_STREAM,IPPROTO_TCP); //建立TCP協議

以下代碼用於檢查傳回值是否有錯誤:

if(m_scoket==INVALID_SOCKET)
{
prinrf("Error at socket():%d\n",GetLastError());
WSACleanup(); //釋放資源
return;
}
說明,如果socket()調用失敗,他將返回INVALID_SOCKET.

為了伺服器能接受一個串連,他必須綁定一個網路地址,下面的代碼展示如何綁定一個已經初始化的IP和連接埠的Socket.用戶端程式用這個
IP地址和連接埠來串連伺服器.

sockaddr_in service;
service.sin_family=AF_INET; //INTERNET地址族
service.sin_addr.s_addr=inet_addr("127.0.0.1"); //將要綁定的本地IP地址
service.sin_port=htons(27015); //27015將要綁定的連接埠

下面我們調用BIND函數,把SOCKET和SOCKADDR以參數的形式傳入,並檢查錯誤.

if(bind(m_socket,(SOCKADDR*)&SERVICE,sizeof(service))==SOCKET_ERROR)
{
printf("bind() failed.\n");
closesocket(m_socket);
return;
}

當綁定完成後,伺服器必須建立一個監聽隊列,以接受用戶端的請求.listen()使伺服器進入監聽狀態,該函數調用成功返回0,否則返回
SOCKET_ERROR.代碼如下:

if(listen(m_socket,1)==SOCKET-ERROR)
{
printf("error listening on socket.\n");
}

伺服器端調用完LISTEN()後,如果此時用戶端調用CONNECT()函數,伺服器端必須在調用ACCEPT().這樣伺服器和用戶端才算正式完成通訊程式的
串連動作.

一旦伺服器開始監聽,我們就要指定一個控制代碼來表示利用ACCEPT()函數接受的串連,這個控制代碼是用來發送和接受資料的表示.建立一個SOCKET控制代碼
Socket AcceptSocket 然後利用無限迴圈來檢測是否有串連傳入.一但有串連請求,ACCEPT()函數就會被調用,並且返回這次串連的控制代碼.

printf("waitong for a client to connect...\n");
while(1)
{
AcceptSocket=SOCKET_ERROR;
while(AcceptSocket==SOCKET_ERROR)
{
AcceptSocket=accept(m_socket,NULL,NULL);
}
}

下面看用戶端端代碼:

sockaddr_in clientService;
clientService.sin_family=AF_INET; //INTERNET地址族
clientService.sin_addr.s_addr=inet_addr("127.0.0.1"); //將要綁定的本地IP地址
clientService.sin_port=htons(27015); //27015將要綁定的連接埠

下面調用CONNECT()函數:

if ( connect( m_socket, (SOCKADDR*) &clientService, sizeof(clientService) ) == SOCKET_ERROR)
{
printf( "Failed to connect.\n" );
WSACleanup();
return;
} //如果調用失敗清理退出
//調用成功繼續讀寫資料

_____________________________________________________________________________________
到這裡,伺服器和用戶端的基本流程介紹完畢,下面我們介紹資料交換.

send():
int send
{
SOCKET s, //指定發送端通訊端
const char FAR?*buf, //指明一個存放應用程式要發送的資料的緩衝區
int len, //實際要發送的資料位元組數
int flags //一般設定為0
};
C/S都用SEND函數向TCP串連的另一端發送資料.

recv():
int recv
{
SOCKET s, //指定發送端通訊端
char FAR?*buf, //指明一個緩衝區 存放RECC受到的資料
int len, //指明BUF的長度
int flags //一般設定為0

};
C/S都使用RECV函數從TCP串連的另一端接受資料

_______________________________________________________________________________________________

下面將完整的程式碼提供如下,大家可直接編譯運行

首先看用戶端的代碼:

#include
#include
#pragma comment(lib, "ws2_32.lib")
void main() {

// 初始化 Winsock.
WSADATA wsaData;
int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );
if ( iResult != NO_ERROR )
printf("Error at WSAStartup()\n");

// 建立socket socket.
SOCKET client;
client = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );

if ( client == INVALID_SOCKET ) {
printf( "Error at socket(): %ld\n", WSAGetLastError() );
WSACleanup();
return;
}

// 串連到伺服器.
sockaddr_in clientService;

clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr( "127.0.0.1" );
clientService.sin_port = htons( 27015 );

if ( connect( client, (SOCKADDR*) &clientService, sizeof(clientService) ) == SOCKET_ERROR) {
printf( "Failed to connect.\n" );
WSACleanup();
return;
}

// 發送並接收資料.
int bytesSent;
int bytesRecv = SOCKET_ERROR;
char sendbuf[32] = "Client: Sending data.";
char recvbuf[32] = "";

bytesSent = send( client, sendbuf, strlen(sendbuf), 0 );
printf( "Bytes Sent: %ld\n", bytesSent );

while( bytesRecv == SOCKET_ERROR ) {
bytesRecv = recv( client, recvbuf, 32, 0 );
if ( bytesRecv == 0 || bytesRecv == WSAECONNRESET ) {
printf( "Connection Closed.\n");
break;
}
if (bytesRecv < 0)
return;
printf( "Bytes Recv: %ld\n", bytesRecv );
}

return;
}

下面是伺服器端代碼:

#include
#include
#pragma comment(lib, "ws2_32.lib")
void main() {

// 初始化
WSADATA wsaData;
int iResult = WSAStartup( MAKEWORD(2,2), &wsaData );
if ( iResult != NO_ERROR )
printf("Error at WSAStartup()\n");

// 建立socket
SOCKET server;
server = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );

if ( server == INVALID_SOCKET ) {
printf( "Error at socket(): %ld\n", WSAGetLastError() );
WSACleanup();
return;
}

// 綁定socket
sockaddr_in service;

service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr( "127.0.0.1" );
service.sin_port = htons( 27015 );

if ( bind( server, (SOCKADDR*) &service, sizeof(service) ) == SOCKET_ERROR ) {
printf( "bind() failed.\n" );
closesocket(server);
return;
}

// 監聽 socket
if ( listen( server, 1 ) == SOCKET_ERROR )
printf( "Error listening on socket.\n");

// 接受串連
SOCKET AcceptSocket;

printf( "Waiting for a client to connect...\n" );
while (1) {
AcceptSocket = SOCKET_ERROR;
while ( AcceptSocket == SOCKET_ERROR ) {
AcceptSocket = accept( server, NULL, NULL );
}
printf( "Client Connected.\n");
server = AcceptSocket;
break;
}

// 發送接受資料
int bytesSent;
int bytesRecv = SOCKET_ERROR;
char sendbuf[32] = "Server: Sending Data.";
char recvbuf[32] = "";

bytesRecv = recv( server, recvbuf, 32, 0 );
printf( "Bytes Recv: %ld\n", bytesRecv );

bytesSent = send( server, sendbuf, strlen(sendbuf), 0 );
printf( "Bytes Sent: %ld\n", bytesSent );

return;
}
本程式僅僅描述了同步的情況!

第五節 多線程編程介紹

對於多線程的基本概念,我不在贅述,是個只要學習過一門程式設計語言就應該多進程和線程有個基本的瞭解.這裡重點介紹一下如何?多線程.

通常一個程式的主線程有作業系統建立,如果想讓其建立額外的線程,可以調用CreateThread()函數來完成.函數原形如下:

HANDLE CreateThread()
{
LPSECURITY_ATTRIBUTES LPThreadAttributes, //指向SECURITY_ATTRIBUTES的指標
SIZE_T dwStackSize, //表示線程為自己所用堆棧分配的地址空間的大小 系統預設值為0
LPTHREAD_START-TOUTINE lpStartAddress, //表示新線程開始執行時代碼所在函數的地址 即線程函數名
LPVOID lpParameter, //是傳入線程函數的參數
DWORD dwCreationFlags, //指定控制線程建立的附加標誌 取0線程立即執行 取CREATE_SUSPENDED線程掛起
LPDWORD lpThreadld //是個DWORD類型的地址,返回賦給該新線程的ID
}

線程函數lpParameter必須有以下原形:

DWORD WINAPI XXXThreadFun(LPVOID lpParameter)
{
return(0);
}

________________________________________________________________________________________
下面我們來建立一個線程:

#include
#include
DWORD WINAPI ThreadFunc( LPVOID lpParam ) //線程函數,跟普通的函數沒什麼兩樣
{
printf( "Parameter = %d.", *(DWORD*)lpParam );
return 0;
}

VOID main( VOID )
{
DWORD dwThreadId, dwThrdParam = 1;
HANDLE hThread;
hThread = CreateThread( NULL,0,ThreadFunc,&dwThrdParam, 0,&dwThreadId);
if (hThread == NULL)
{
printf( "CreateThread failed (%d)\n", GetLastError() );
}
else
{
_getch();
CloseHandle( hThread );
}
}

關於線程同步的問題,這裡就不再講解,請大家自己查閱資料,不查閱以後可能會有困難啊.培養一下各位的自己動手能力.

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.