1. 程式人生 > >基於TCP的socket套接字的網路程式設計(客戶端/服務端模式)

基於TCP的socket套接字的網路程式設計(客戶端/服務端模式)

於資料完整性要求較高的場合,就應採用TCP協議。

IP網路層提供IP定址和路由。因為在網路上資料可以經由多條線路到達目的地,網路層負責找出最佳的傳輸線路。

IP地址與資料包:

基於TCP的socket套接字的網路程式設計(客戶端/服務端模式)

 

IP層就是把資料分組從一個主機跨越千山萬水搬運到另外一主機, 並且這搬運服務一點都不可靠, 丟包、重複、失序可以說是家常便飯。如果失敗是否需要重傳?如果需要,那就使用TCP協議實現可靠的、面向連線的傳輸連線,如果不需要,那就使用UDP協議使用不可靠的、不面向連線的傳輸連線。

所以不同的網路應用程式可以用TCP實現,也可以用UDP實現,只是可靠性和實時性不一樣。

在TCP或UDP協議上程式設計是比較複雜的。例如TCP協議, 我們不能要求每個程式設計師都去實現建立連線的3次握手(確認客戶端、服務端的發信、收信能力),分組交換、失敗重傳(中間節點的路由可以是隨機的,允許失序、重複、丟失,可靠的傳輸完全由兩端點來實現,失敗後重傳即可,而順序可以由資料包的序號來確定), 這些應該是屬於作業系統核心的部分, 沒必要重複開發, 但是對於應用程式來講, 作業系統可以抽象出一個socket概念, 讓上層應用去程式設計。

所以,Socket是應用層與TCP/IP協議族通訊的中間軟體抽象層,它是一組介面。在設計模式中,Socket其實就是一個門面模式,它把複雜的TCP/IP協議族隱藏在Socket介面後面,對使用者來說,一組簡單的介面就是全部,讓Socket去組織資料,以符合指定的協議。

基於TCP的socket套接字的網路程式設計(客戶端/服務端模式)

 

Socket是連線應用程式與網路驅動程式的橋樑,Socket在應用程式中建立,通過bind與驅動程式建立關係。此後,應用程式送給Socket的資料,由Socket交給驅動程式向網路上傳送出去。計算機從網路上收到與該Socket繫結的IP+Port相關的資料後,由驅動程式交給Socket,應用程式便可從該Socket中提取接收到的資料。網路應用程式就是這樣通過socket進行資料的傳送與接收的。

基於TCP(面向連線)的socket程式設計的伺服器端程式流程如下:

1 建立套接字(socket())。

2 將套接字繫結到一個本地地址和埠上(bind())。

3 將套接字設為監聽模式,準備接收客戶請求(listen())

4 等待客戶請求到來;當清求到來後,接受連線請求,返回一個新的對應於此次連線的套接字(accept())。

5 用返回的套接字和客戶端進行通訊(send/recv())返回,等待另一客戶請求。

6 關閉套接字。

基於TCP(面向連線)的socket程式設計的客戶端程式流程如下:

1 建立套接字(socket())。

2 向伺服器發出連線請求(connect())。

3 和伺服器端進行通訊(send/recv())。

4 關閉套接字。

基於TCP的socket套接字的網路程式設計(客戶端/服務端模式)

 

程式碼:

//tcp server
#include <Winsock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
void main(){
WORD wVersionRequested; // 指定準備載入的Winsock庫版本
WSADATA wsaData; // Winsock庫版本資訊的結構體
wVersionRequested = MAKEWORD( 1, 1);
int err = WSAStartup( wVersionRequested, &wsaData ); // 載入套接字型檔
if ( err != 0 ) { return;}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( );
// 釋放為該應用程式分配的資源,終止對WinSock動態庫的使用
return;
}
SOCKET sockSrv=socket(AF_INET,SOCK_STREAM,0);
// 建立套接字AF_INET表示TCP/IP協議
// SOCK_STREAM表示TCP連線,SOCK_DGRAM表示UDP連線
// 第三個引數為零表示自動選擇協議
SOCKADDR_IN addrSrv; // 定義一個地址結構體的變數
addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSrv.sin_family=AF_INET;
addrSrv.sin_port=htons(6000);
//htons把一個u_short型別從主機位元組序轉換為網路位元組序
bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR)); //將套接字繫結到本地的某個地址和埠上
listen(sockSrv,5); //將指定的套接字設定為監聽模式
SOCKADDR_IN addrClient;
int len=sizeof(SOCKADDR);
while(1) {
SOCKET sockConn=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
//接受客戶端傳送的連線請求
char sendBuf[100];
sprintf(sendBuf,"Welcome %s to here",inet_ntoa(addrClient.sin_addr));
send(sockConn,sendBuf,strlen(sendBuf)+1,0);
//通過一個已建立連線的套接字傳送資料
char recvBuf[100];
recv(sockConn,recvBuf,100,0); /
/從一個已建立連線的套接字接收資料
printf("%s ",recvBuf);
closesocket(sockConn);
}
}
//新增ws2_32.lib:工程→設定→連線,新增該庫(前面要有空格)或
//#pragma comment(lib,"ws2_32.lib")

//Tcp client

#include <Winsock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
void main(){
WORD wVersionRequested; // 指定準備載入的Winsock庫版本
WSADATA wsaData; // Winsock庫版本資訊的結構體
wVersionRequested = MAKEWORD( 1, 1);
int err = WSAStartup( wVersionRequested, &wsaData ); // 載入套接字型檔
if ( err != 0 ) { return;}
if ( LOBYTE( wsaData.wVersion ) != 1 ||
HIBYTE( wsaData.wVersion ) != 1 ) {
WSACleanup( );
// 釋放為該應用程式分配的資源,終止對WinSock動態庫的使用
return;
}

SOCKET sockClient=socket(AF_INET,SOCK_STREAM,0);
// 建立套接字。AF_INET表示TCP/IP協議

// SOCK_STREAM表示TCP連線,SOCK_DGRAM表示UDP連線

//第三個引數為零表示自動選擇協議
SOCKADDR_IN addrSrv; //定義一個地址結構體的變數
addrSrv.sin_addr.S_un.S_addr=inet_addr("127.0.0.1");
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6000);

connect(sockClient,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
//向伺服器發出連線請求

char recvBuf[1000];
recv(sockClient,recvBuf,100,0); //接收資料
printf("%s ",recvBuf);

send(sockClient,"Hello!",strlen("Hello!")+1,0); //傳送資料

closesocket(sockClient); //關閉套接字
WSACleanup();
system("pause");
}
//新增ws2_32.lib:工程→設定→連線,新增該庫(前面要有空格)或
//#pragma comment(lib,"ws2_32.lib")

-End-