目錄
成都創(chuàng)新互聯(lián)公司是一家專注于成都網(wǎng)站設(shè)計(jì)、網(wǎng)站建設(shè)與策劃設(shè)計(jì),名山網(wǎng)站建設(shè)哪家好?成都創(chuàng)新互聯(lián)公司做網(wǎng)站,專注于網(wǎng)站建設(shè)10多年,網(wǎng)設(shè)計(jì)領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:名山等地區(qū)。名山做網(wǎng)站價(jià)格咨詢:18982081108
前言
一、Socket的客戶端與服務(wù)端的通訊原理
二、各接口介紹
1.WSAStartup:異步啟動(dòng)套接字命令
2.Socket創(chuàng)建套接字
3.bind:綁定套接字
4.listen:監(jiān)聽
5.accept:接受連接請(qǐng)求
6. connet:發(fā)送連接請(qǐng)求
7.send:發(fā)送數(shù)據(jù)
8.recv:接收數(shù)據(jù)函數(shù)?
9.closesocket,?WSACleanup:釋放socket
三、代碼塊的簡單實(shí)現(xiàn)(可直接使用)
? (1) 服務(wù)器
(2)客戶端
效果展示:
完結(jié)撒花
?首先、在敲代碼之前我們必須先要了解服務(wù)端與客戶端之前的通訊原理,否則我們敲代碼將會(huì)無從下手、了解原理后也會(huì)對(duì)我們的敲代碼的效率達(dá)到事半功倍的效果!
? 在c++里面有一個(gè)我們可以通過直接調(diào)用一些api來實(shí)現(xiàn)、前提是我們要聲明其頭文件并且連接到其提供的動(dòng)態(tài)鏈接庫!
在Windows系統(tǒng)中的剛好有這么一個(gè)、即Winsock。聲明頭文件為:
#include#pragma comment(lib,"ws2_32.lib")
一、Socket的客戶端與服務(wù)端的通訊原理如圖所示:圖左(客戶端創(chuàng)建過程) 圖右(服務(wù)器創(chuàng)建過程)
二、各接口介紹 1.WSAStartup:異步啟動(dòng)套接字命令1、函數(shù)原型:
int WSAAPI WSAStartup(
WORD wVersionRequested,
LPWSADATA lpWSAData
);
2、參數(shù):
@WORD wVersionRequested:調(diào)用者可以使用的Windows套接字規(guī)范的最高版本。 高位字節(jié)指定次要版本號(hào);
低位字節(jié)指定主要版本號(hào)。[這個(gè)版本我也不太清楚、但是大部分人用的是2.2、
所以我也用的2.2]
但是可借鑒這篇文章:
@LPWSADATA lpWSAData:指向WSADATA數(shù)據(jù)結(jié)構(gòu)的指針,該數(shù)據(jù)結(jié)構(gòu)將接收Windows套接字實(shí)現(xiàn)的詳細(xì)信息
3、返回值:
返回0執(zhí)行正確
否則失敗
4、代碼如下:
WSADATA wsdata;
if (WSAStartup(MAKEWORD(2, 2), &wsdata))
{
?? ?std::cout<< "init socket failed!"<< std::endl;
?? ?WSACleanup();
?? ?return FALSE;
}
2.Socket創(chuàng)建套接字1、函數(shù)原型:
SOCKET socket(int af,int type,int protocl);
參數(shù):
@int af:第一個(gè)參數(shù)(af)指定地址族,對(duì)于TCP/IP協(xié)議的套接字他有以下兩個(gè)參數(shù):
AF_INET,PF_INET IPV4協(xié)議
PF_INET6 IPV6協(xié)議
其他還有很多協(xié)議這里不做介紹
@int type:用于設(shè)置套接字通信的類型,有流式套接字(SOCKET_STREAM)和數(shù)據(jù)包套接字
(SOCK_DGRAM)
SOCK_STREAM TCP連接,提供有序化的、可靠的、雙向連接的字節(jié)流。支持帶外數(shù)據(jù)傳輸
SOCK_DGRAM UDP連接
@int protocl:用于制定某個(gè)協(xié)議的特定類型,即type類型中的某個(gè)類型,通常一種協(xié)議只有一
種類型,該參數(shù)可以直接被設(shè)置為0;如果協(xié)議有多種類型,則需要指定協(xié)議類
型
2、返回值
如果沒有錯(cuò)誤發(fā)生,socket()返回一個(gè)與建立的套接字相關(guān)的描述符。
否則它返回值INVALID_SOCKET,錯(cuò)誤碼可通過調(diào)用WSAGetLastError()函數(shù)得到
3、代碼范例:
//定義一個(gè)套接字 作為服務(wù)端
SOCKET s_server;
s_server = socket(PF_INET, SOCK_STREAM, 0);
if (s_server == INVALID_SOCKET)
{
std::cout<< "create socket fail"<< std::endl;
WSACleanup();
return FALSE;
}
3.bind:綁定套接字1、函數(shù)原型:
int bind(
SOCKET s,
const sockaddr *addr,
int namelen
);
@ SOCKET s:要綁定的套接字
@const sockaddr *addr:指向要分配給綁定套接字的本地地址的sockaddr結(jié)構(gòu)的指針
struct sockaddr {
ushort sa_family;
char sa_data[14];
};
struct sockaddr_in {
short sin_family; //指定協(xié)議家族 ipv4 or ipv6
u_short sin_port; //指定端口號(hào)
struct in_addr sin_addr; //指定地址 也可以是所有ip 參數(shù)為:INADDR_ANY
char sin_zero[8]; //沒有特殊意義不用管
};
struct in_addr {
union {
struct {
u_char s_b1;
u_char s_b2;
u_char s_b3;
u_char s_b4;
} S_un_b;
struct {
u_short s_w1;
u_short s_w2;
} S_un_w;
u_long S_addr; //ip地址
} S_un;
};
@int namelen:sockaddr結(jié)構(gòu)的指針的大小
2、返回值
如果沒有錯(cuò)誤發(fā)生,bind()返回0。
否則返回值SOCKET_ERROR,錯(cuò)誤碼可通過調(diào)用WSAGetLastError()函數(shù)得到
3、代碼范例:
//填充服務(wù)端信息
SOCKADDR_IN server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8224);
server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
//數(shù)據(jù)綁定服務(wù)器 s_server為服務(wù)端套接字
if (bind(s_server, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR)
{
std::cout<< "Binding Socket fail.......!"<< std::endl;
WSACleanup();
return FALSE;
}
1、函數(shù)原型:
int PASCAL FAR listen (
_In_ SOCKET s,
_In_ int backlog);
@SOCKET s:服務(wù)端的socket,也就是socket函數(shù)創(chuàng)建的
@int backlog:掛起的連接隊(duì)列的大長度,由用戶自主選擇。但是我們知道我們電腦處理線程的時(shí)候用一時(shí)間只能處理n個(gè)線程、這里也是一樣的原理,我們創(chuàng)建的服務(wù)器他可能不支持你定義的這個(gè)隊(duì)列長度的用戶
比如:一個(gè)洗手間 只能同時(shí)供4個(gè)人使用 而這個(gè)時(shí)候來了8個(gè)人 那么其他4個(gè)人則需要外面等待。
所以一般填寫這個(gè)參數(shù)為SOMAXCONN
作用是讓系統(tǒng)自動(dòng)選擇最合適的個(gè)數(shù)
不同的系統(tǒng)環(huán)境不一樣,所以這個(gè)合適的數(shù)也不一樣
2、返回值
成功返回0
失敗返回SOCKET_ERROR
具體錯(cuò)誤碼:WSAGetLastError()得到
3、代碼示例:
//監(jiān)聽
if (listen(s_server, 1) == SOCKET_ERROR)
{
std::cout<< "Listening Socket fail........!"<< std::endl;
WSACleanup();
return FALSE;
}
5.accept:接受連接請(qǐng)求1、函數(shù)原型:
SOCKET WSAAPI accept (
SOCKET s,
struct sockaddr FAR* addr,
int FAR* addrlen
);
@SOCKET s:該套接字在用作accept()函數(shù)的參數(shù)前必須先調(diào)用過listen()函數(shù),此時(shí)它正處于監(jiān)聽連接的狀態(tài)。
@struct sockaddr FAR* addr:一個(gè)可選的指向緩沖區(qū)的指針,用來接收連接實(shí)體的地址,在通訊層使用。addr的確切格式由套接字創(chuàng)建時(shí)建立的地址族決定
@int FAR* addrlen:一個(gè)可選的指向整數(shù)的指針,它調(diào)用時(shí)含有地址addr指向的空間的大小,返回時(shí)含有返回的地址的確切長度(字節(jié)數(shù))。
2、返回值
如果沒有錯(cuò)誤發(fā)生,accept()返回一個(gè)SOCKET類型的值,表示接收到的套接字的描述符。
否則返回值INVALID_SOCKET,錯(cuò)誤碼可通過調(diào)用WSAGetLastError()函數(shù)得到。
2、代碼范例:
//接受連接請(qǐng)求
sockaddr_in accept_addr; //用來記錄請(qǐng)求連接的套接字信息
int len = sizeof(SOCKADDR);
s_accept = accept(s_server, (SOCKADDR*)&accept_addr, &len );
if (s_accept == SOCKET_ERROR) {
std::cout<< "Error: accept failed !"<< std::endl;
WSACleanup();
break;
}
6. connet:發(fā)送連接請(qǐng)求1、函數(shù)原型:
int WSAAPI connect (
SOCKET s,
const struct sockaddr FAR* name,
int namelen //socket address結(jié)構(gòu)的字節(jié)數(shù)
);
@SOCKET s:發(fā)出連接請(qǐng)求的套接字的描述符
@const struct sockaddr FAR* name:對(duì)等方的套接字的地址、就是你創(chuàng)建客戶端的套接字時(shí)綁定ip和
端口號(hào)的套接字地址
@namelen: 套接字類型大小
2、返回值
如果沒有錯(cuò)誤發(fā)生,connect()返回0。
否則返回值SOCKET_ERROR,錯(cuò)誤碼可通過調(diào)用WSAGetLastError()函數(shù)得到。
3、代碼范例:
//填充服務(wù)端信息
//SOCKADDR_IN server_addr;
//server_addr.sin_family = AF_INET;
//server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
//server_addr.sin_port = htons(8224);
//創(chuàng)建套接字
//SOCKET client= socket(AF_INET, SOCK_STREAM, 0);
if (connect(client, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
std::cout<< "Error: connect server failed !"<< std::endl;
WSACleanup();
return -1;
}
7.send:發(fā)送數(shù)據(jù)1、函數(shù)原型:
int WSAAPI send(
SOCKET s,
const char FAR * buf,
int len,
int flags
);
@SOCKET s: 已連接的套接字描述符
@ const char FAR * buf:指向存有發(fā)送數(shù)據(jù)的緩沖區(qū)的指針
@int len:緩沖區(qū)buf中數(shù)據(jù)長度
@ int flags:一般為0,為阻塞發(fā)送 即發(fā)送不成功會(huì)一直阻塞,直到被某個(gè)信號(hào)終端終止,或者直到發(fā)
送成功為止。
指定MSG_NOSIGNAL,表示當(dāng)連接被關(guān)閉時(shí)不會(huì)產(chǎn)生SIGPIPE信號(hào)
指定MSG_DONTWAIT 表示非阻塞發(fā)
2、返回值
如果沒有錯(cuò)誤發(fā)生,send()返回總共發(fā)送的字節(jié)數(shù)(注意,這可能比len指示的長度?。?。
否則它返回SOCKET_ERROR,錯(cuò)誤碼可通過調(diào)用WSAGetLastError()函數(shù)得到。
2、代碼范例:
char temp[1024] = { 0 };
snprintf(temp, sizeof(temp), "%s", detectInfo);
int sendLen = send(socket, (char*)temp, sizeof(temp), 0);
if (sendLen< 0) {
std::cout<< "Error: send info to server failed !"<< std::endl;
return -1;
}
8.recv:接收數(shù)據(jù)函數(shù)?1、函數(shù)原型:
int PASCAL FAR recv (
SOCKET s,
writes_bytes_to_(len, return) __out_data_source(NETWORK) char FAR * buf,
int len,
int flags);
@SOCKET s:已連接的套接字描述符。
@char FAR * buf:指向接收輸入數(shù)據(jù)緩沖區(qū)的指針
@int len:buf參數(shù)所指緩沖區(qū)的長度
@int flags:一般為0
2、返回值
如果沒有錯(cuò)誤發(fā)生,recv()返回收到的字節(jié)數(shù)。
如果連接被關(guān)閉,返回0。
否則它返回SOCKET_ERROR,錯(cuò)誤碼可通過調(diào)用WSAGetLastError()函數(shù)得到。
3、代碼示例:
char recv_buf[8192] = { 0 };
int recv_len = recv(socket, recv_buf, sizeof(recv_buf), 0);
if (recv_len< 0) {
std::cout<< "Error: receive info from server failed !"<< std::endl;
return -1;
}
9.closesocket,?WSACleanup:釋放socket使用完后續(xù)如果不再使用 記住一定要釋放相關(guān)資源 否則你懂的。
//關(guān)閉套接字 參數(shù):需要關(guān)閉的套接字描述符
closesocket(s_server);
//釋放DLL資源
WSACleanup();
三、代碼塊的簡單實(shí)現(xiàn)(可直接使用)
? ? ? ? (1) 服務(wù)器// ServerTest.cpp : 此文件包含 "main" 函數(shù)。程序執(zhí)行將在此處開始并結(jié)束。
//
#include#include#pragma comment(lib,"ws2_32.lib")
int main()
{
std::cout<< "Hello World!\n";
WSADATA wsdata;
std::cout<< "start up now ...."<< std::endl;
if (WSAStartup(MAKEWORD(2, 2), &wsdata))
{
std::cout<< "init socket failed!"<< std::endl;
WSACleanup();
return FALSE;
}
std::cout<< "before create socket ...."<< std::endl;
SOCKET s_server, s_accept;
s_server = socket(PF_INET, SOCK_STREAM, 0);
if (s_server == INVALID_SOCKET)
{
std::cout<< "create socket fail"<< std::endl;
WSACleanup();
return FALSE;
}
std::cout<< "create socket success...."<< std::endl;
//填充服務(wù)端信息
SOCKADDR_IN server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8226);
//當(dāng)然這里也可以將 127.0.0.1改成你想要的ip或者 INADDR_ANY 為向所有ip發(fā)送信息
server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
//數(shù)據(jù)綁定服務(wù)器 s_server為服務(wù)端套接字
std::cout<< "before bind socket ...."<< std::endl;
if (bind(s_server, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR)
{
std::cout<< "Binding Socket fail.......!"<< std::endl;
WSACleanup();
return FALSE;
}
std::cout<< "bind socket success...."<< std::endl;
std::cout<< "before listen socket ...."<< std::endl;
if (listen(s_server, 1) == SOCKET_ERROR)
{
std::cout<< "Listening Socket fail........!"<< std::endl;
WSACleanup();
return FALSE;
}
std::cout<< "listen socket success...."<< std::endl;
sockaddr_in accept_addr; //用來記錄請(qǐng)求連接的套接字信息
int len = sizeof(SOCKADDR);
char recv_buf[8192] = { 0 };
char send_buf[1024] = { 0 };
while (true)
{
//接受連接請(qǐng)求
std::cout<< "wait accept...."<< std::endl;
s_accept = accept(s_server, (SOCKADDR*)&accept_addr, &len);
if (s_accept == SOCKET_ERROR) {
std::cout<< "Error: accept failed !"<< std::endl;
WSACleanup();
break;
}
std::cout<< "建立連接成功...."<< std::endl;
while (true)
{
std::cout<< "等待客戶端數(shù)據(jù)中...."<< std::endl;
int recv_len = recv(s_accept, recv_buf, sizeof(recv_buf), 0);
if (recv_len< 0) {
std::cout<< "Error: receive info from server failed !"<< std::endl;
return -1;
}
std::cout<< "已收到數(shù)據(jù),客戶端的數(shù)據(jù)是:"<< recv_buf<< std::endl;
std::cout<< "給客戶端返回?cái)?shù)據(jù)中..."<< std::endl;
snprintf(send_buf, sizeof(send_buf), "%s", "我給你返回信息了");
int sendLen = send(s_accept, (char*)send_buf, sizeof(send_buf), 0);
if (sendLen< 0) {
std::cout<< "Error: send info to server failed !"<< std::endl;
return -1;
}
std::cout<< "給客戶端返回?cái)?shù)據(jù)完畢"<< std::endl;
}
}
}
(2)客戶端// ClientTest.cpp : 此文件包含 "main" 函數(shù)。程序執(zhí)行將在此處開始并結(jié)束。
//
#include#include#pragma comment(lib,"ws2_32.lib")
int main()
{
std::cout<< "Hello World!\n";
WSADATA wsdata;
if (WSAStartup(MAKEWORD(2, 2), &wsdata))
{
std::cout<< "init socket failed!"<< std::endl;
WSACleanup();
return FALSE;
}
//檢測(cè)版本號(hào)
if (LOBYTE(wsdata.wVersion) != 2 || HIBYTE(wsdata.wHighVersion) != 2) {
std::cout<< "Error: wsadata.wVersion != 2 ."<< std::endl;
WSACleanup();
return -1;
}
SOCKET client;
client = socket(PF_INET, SOCK_STREAM, 0);
if (client == INVALID_SOCKET)
{
std::cout<< "create socket fail"<< std::endl;
WSACleanup();
return FALSE;
}
//填充服務(wù)端信息
SOCKADDR_IN server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
server_addr.sin_port = htons(8226);
//發(fā)送連接請(qǐng)求 請(qǐng)求連接服務(wù)器
if (connect(client, (SOCKADDR*)&server_addr, sizeof(SOCKADDR)) == SOCKET_ERROR) {
std::cout<< "Error: connect server failed !"<< std::endl;
WSACleanup();
return -1;
}
std::cout<< "成功連接到服務(wù)器"<< std::endl;
//發(fā)送數(shù)據(jù)
char temp[1024] = { 0 };
std::cout<< "開始發(fā)送數(shù)據(jù)....."<< std::endl;
snprintf(temp, sizeof(temp), "%s", "我是客戶端 我發(fā)送信息給你了服務(wù)端");
int sendLen = send(client, (char*)temp, sizeof(temp), 0);
if (sendLen< 0) {
std::cout<< "Error: send info to server failed !"<< std::endl;
return -1;
}
std::cout<< "發(fā)送數(shù)據(jù)成功、等待服務(wù)器響應(yīng)....."<< std::endl;
char recv_buf[8192] = { 0 };
int recv_len = recv(client, recv_buf, sizeof(recv_buf), 0);
if (recv_len< 0) {
std::cout<< "Error: receive info from server failed !"<< std::endl;
return -1;
}
std::cout<< "收到了服務(wù)器返回的信息 內(nèi)容是:"<< recv_buf<< std::endl;
system("pause");
}
效果展示:完結(jié)撒花當(dāng)然、以上都是一些比較簡單的實(shí)現(xiàn)、更加深層次我的也慢慢學(xué)習(xí)咯~比如像這個(gè)客戶端和服務(wù)端發(fā)送數(shù)據(jù)、我們完全可以再發(fā)送數(shù)據(jù)和接收數(shù)據(jù)之前對(duì)數(shù)據(jù)進(jìn)行一些處理、也就是說我們可以先加一個(gè)判斷、先判斷這個(gè)發(fā)過過來的數(shù)據(jù)是否符合我想接收的要求、如果不是我選擇不往下接續(xù)接收信息。
以上只是個(gè)人學(xué)習(xí)的一些基礎(chǔ)、有哪里不對(duì)的話 歡迎糾正哈
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級(jí)流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級(jí)服務(wù)器適合批量采購,新人活動(dòng)首月15元起,快前往官網(wǎng)查看詳情吧