在很多工程中都需要用到資訊的發送和接收,是以在網上收集了些資料,實作了簡單的發送文字、檔案、圖檔,以及BASE64編碼。(TCP/IP,IP協定這裡就不細說了,着重與功能的實作)
套接字基礎
套接字是一種網絡API,可以使用它開發網絡程式。套接字接口提供一種程序間的通信方法,使得在相同或不同的主機上的程序能以相同的規範進行雙向資訊傳送。程序通過調用套接字接口來實作互相間的通信,而套接字接口又利用下層的網絡通信協定功能和系統調用實作實際的通信工作。
程序之間要進行通信,首先要調用網絡程式設計接口,由套接字負者将程序接收和發送的請求資訊通過下層的網絡通信協定服務接口(TCP/IP)向上或向下傳遞,是以套接字接口是應用層到傳輸層的接口。
IPv4套接字位址結構
IPv4套接字位址結構通常也稱為“網絡套接字位址結構”,它的名字為sockaddr_in,其結構定義如下:
- sin_family: Internet位址族,在IPv4中是AF_INET
- sin_port:是端口号,以網絡位元組序存儲
- sin_addr:是一個結構,該結構中的成員存儲的才是IP位址
- sin_zero:暫時還未用,但總将它置零
TCP套接字程式設計
使用TCP套接字程式設計可以實作基于TCP/IP協定的面向連接配接的通信,分為伺服器和用戶端,主要實作過程如下圖所示:
用戶端
1.加載初始化套接字
WSADATA wsaData;//初始化套接字
char buff[2048];
char buffs2[100];
memset(buff, 0, sizeof(buff));
memset(buffs2, 0, sizeof(buffs2));
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)//第一個參數表示winsock的版本,本例使用的是winsock2.2版本
{
printf("初始化Winsock失敗");
return 0;
}
SOCKADDR_IN addrSrv;
addrSrv.sin_family = AF_INET;//代表TCP/IP協定族
// htons函數:将一個16位無符号短整型資料由主機排列方式轉化為網絡排列方式,htonl函數的作用恰好相反。
addrSrv.sin_port = htons(8888);//端口号
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//IP位址,sin_addr存儲IP位址,使用in_addr這個資料結構
//建立套接字
/*
af:一個位址家族,通常為AF_INET
type:套接字類型,SOCK_STREAM表示建立面向流連接配接的套接字。為SOCK_DGRAM,表示建立面向無連接配接的資料包套接字。為SOCK_RAW,表示建立原始套接字
protocol:套接字所用協定,不指定可以設定為0
傳回值就是一個socket
SOCKET socket(int af,int type,int protocol);
*/
SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);//協定、套接字類型、
if (SOCKET_ERROR == sockClient){
printf("Socket() error:%d", WSAGetLastError());
return 0;
}
2.向伺服器發出連接配接請求
/*
connect函數:用戶端socket發送連接配接請求的函數,
第一個參數是用戶端的socket,
第二個參數是一個結構體指針,裡面包括連接配接主機的位址和ip,
第三個參數為緩沖區的長度。
*/
connect(sockClient, (struct sockaddr*)&addrSrv, sizeof(addrSrv));
3.向伺服器發送資料
/*
第一個參數為socket,
第二個參數為接收資料緩沖區,
第三個參數為緩沖區的長度,
第四個參數為函數的調用方式
*/
send(sockClient, buffs, sizeof(buffs), 0);
4.接收資料
/*
recv函數:接收資料,
第一個參數為socket,
第二個參數為接收資料緩沖區,
第三個參數為緩沖區的長度,
第四個參數為函數的調用方式。
*/
recv(sockClient, buff, sizeof(buff), 0);
printf("%s\n", buff);
5.關閉套接字
closesocket(sockClient);
伺服器端
1.建立用于監聽的套接字
SOCKET sockSrv = socket(AF_INET, SOCK_STREAM, 0);
SOCKADDR_IN addrSrv;
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(port); //1024以上的端口号
/**
* INADDR_ANY就是指定位址為0.0.0.0的位址,這個位址事實上表示不确定位址,或“所有位址”、“任意位址”。 一般來說,在各個系統中均定義成為0值。
*/
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
2.将套接字綁定到指定的端口和位址
/*
第一個參數為socket,第二個參數是一個結構指針,它包含了端口和IP位址資訊,
第三個參數表示緩沖區長度。需要說明的是,第二個參數在API中表示為:const struct sockaddr FAR*
*/
bind(sockSrv, (LPSOCKADDR)&addrSrv, sizeof(SOCKADDR_IN));
3.将socket設定為監聽模式(服務端的socket特有)
/*
必須将服務端的socket設定為監聽模式才能和服務端履歷連接配接。
裡面有兩個參數,第一個參數為socket,第二個參數為等待連接配接最大隊列的長度。
*/
listen(sockSrv, 10);
4.accept()函數,等待使用者請求到來
/*
accept函數:服務端socket接收用戶端的連接配接請求,連接配接成功,
則傳回一個socket,該socket可以在服務端發送和接收資料。
第一個參數為socket,
第二個參數為包含用戶端端口IP資訊的sockaddr_in結構指針,
第三個參數為接收參數addr的長度。
*/
accept(sockSrv, (SOCKADDR *)&addrClient, &len);
5.與用戶端資訊互動recv(),sent()
recv(sockConn, recvBuf, sizeof(recvBuf), 0);
send(sockConn, sendbuf, sizeof(sendbuf), 0);
6.關閉套接字
closesocket(sockSrv);