通訊基本流程圖如下所示:
Server端代碼(ServerDemo.cpp):
1 #include <WinSock2.h>
2 #include <Windows.h>
3 #include <iostream>
4 #include <
string>
5 #include <sstream>
6
7 using namespace std;
8
9 #pragma comment(lib, "WS2_32.lib")
10
11 int main()
12 {
13 /*
14 WSAstartup 必須是應用程序首先調用的 Winsock 函數。它允許應用程序指定所需的
15 Windows Sockets API 的版本,獲取特定 Winsock 實現的詳細信息。僅當這個函數成功執行之
16 后,應用程序才能調用其他 Winsock API
17 每一個對WSAStartup的調用必須對應一個對WSACleanup的調用, 這個函數釋放Winsock庫。
18 */
19 WORD wVersion = MAKEWORD(
2,
2);
20 WSADATA WSAData;
21 ::WSAStartup(wVersion, &
WSAData);
22
23 stringstream os;
24 cout <<
"初始化套接字...." <<
endl;
25 SOCKET s;
26 s = ::socket(AF_INET, SOCK_STREAM,
0);
27 if(s ==
INVALID_SOCKET)
28 {
29 cout <<
"socket fail!" <<
endl;
30 goto __end;
31 }
32 sockaddr_in addr_in;
33 addr_in.sin_family = AF_INET;
//設置地址家族
34 addr_in.sin_addr.S_un.S_addr =
INADDR_ANY;
35 addr_in.sin_port = htons(
8080);
// 轉化端口號8080到網絡字節順序,并安排它到正確的成員
36
37 // 綁定這個套節字到一個本地地址
38 cout <<
"綁定端口8080...." <<
endl;
39 if(::bind(s, (LPSOCKADDR)&addr_in,
sizeof(addr_in)) ==
SOCKET_ERROR)
40 {
41 cout <<
"bind error!" <<
endl;
42 goto __end;
43 }
44
45 // listen 函數置套節字進入監聽狀態
46 os <<
"開始監聽...." << inet_ntoa(addr_in.sin_addr) << endl;
//inet_ntoa() 將32 位的二進制數轉化為字符串
47 cout << os.str() <<
endl;
48 if(::listen(s,
2) ==
SOCKET_ERROR)
49 {
50 cout <<
"listen error!" <<
endl;
51 goto __end;
52 }
53
54 SOCKET s_client;
55 sockaddr_in addr_client;
56 char szServerMsg[
256] =
"Hello client, this is server!";
57 int nMsgLen =
sizeof(szServerMsg);
58 while(
true)
59 {
60 // accept 函數用于接受到來的連接。
61 if ((s_client = ::accept(s, (LPSOCKADDR)&addr_client, &nMsgLen)) ==
SOCKET_ERROR)
62 {
63 cout <<
"accept error!" <<
endl;
64 goto __end;
65 }
66 os.str(
"");
67 os.clear();
68 os <<
"接收到來自" << inet_ntoa(addr_client.sin_addr) <<
"的連接!";
69 cout << os.str() <<
endl;
70 // send 函數在一個連接的套節字上發送緩沖區內的數據,返回發送數據的實際字節數。flag參數通常設置為0
71 ::send(s_client, szServerMsg,
256,
0);
72 ::closesocket(s_client);
73 }
74 ::closesocket(s);
75
76 __end:
77 ::WSACleanup();
78 system(
"pause");
79 return 0;
80 }
Client端代碼(ClientDemo.cpp)
1 #include <WinSock2.h>
2 #include <Windows.h>
3 #include <iostream>
4 #include <
string>
5 #include <sstream>
6
7 #pragma comment(lib, "WS2_32.lib")
8
9 using namespace std;
10
11 int main()
12 {
13 WORD wVersion = MAKEWORD(
2,
2);
14 WSADATA WSAData;
15 ::WSAStartup(wVersion, &
WSAData);
16 SOCKET s = ::socket(AF_INET, SOCK_STREAM,
0);
17 if (s ==
INVALID_SOCKET)
18 {
19 cout <<
"socket fail!" << ::WSAGetLastError() <<
endl;
20 ::WSACleanup();
21 return 0;
22 }
23 sockaddr_in serverAddr;
24 // inet_addr函數轉化一個"aa.bb.cc.dd"類型的IP地址字符串到長整型,它是以網絡字節順序記錄的IP地址,
25 // sin_addr.S_un.S_addr指定了地址聯合中的此長整型
26 serverAddr.sin_addr.S_un.S_addr = inet_addr(
"127.0.0.1");
27 serverAddr.sin_family =
AF_INET;
28 serverAddr.sin_port = htons(
8080);
29
30 // 客戶端程序在創建套節字之后,要使用 connect 函數請求與服務器連接
31 if (::connect(s, (LPSOCKADDR)&serverAddr,
sizeof(sockaddr_in)))
32 {
33 cout <<
"connect server fail!" <<
endl;
34 ::WSACleanup();
35 return 0;
36 }
37
38 char buf[
256];
39 // recv 函數從對方接收數據,并存儲它到指定的緩沖區。flag參數通常設置為0
40 ::recv(s, buf,
256,
0);
41 stringstream os;
42 os <<
"從服務器接收到數據:" <<
buf;
43 cout << os.str() <<
endl;
44
45 system(
"pause");
46 return 0;
47 }
?
TCP 協議由于可靠、穩定的特征而被用在大部分場合,但它對系統資源要求比較高。UDP
協議是一個簡單的面向數據報的傳輸層協議,又叫用戶數據報協議。它提供了無連接的、不可
靠的數據傳輸服務。無連接是指他不像 TCP 協議那樣在通信前先與對方建立連接以確定對方
的狀態。不可靠是指它直接安裝指定 IP 地址和端口號將數據包發出去,如果對方不在線的話
數據就可能丟失。UDP 協議編程流程如下:
1.服務器端
(1)創建套節字(socket) 。
(2)綁定 IP 地址和端口(bind) 。
(3)收發數據(sendto/recvfrom) 。
(4)關閉連接 (closesocket) 。
2.客戶端
(1)創建套節字(socket) 。
(2)收發數據(sendto/recvfrom) 。
(3)關閉連接(closesocket) 。
UDP 協議用于發送和接收數據的函數是 sendto 和 recvfrom。它們的原形如下。
int sendto (
SOCKET s, // 用來發送數據的套節字
const char FAR * buf, // 指向發送數據的緩沖區
int len, // 要發送數據的長度
int flags, // 一般指定為0
const struct sockaddr * to, // 指向一個包含目標地址和端口號的sockaddr_in結構
int tolen // 為 sockaddr_in 結構的大小
);
同樣 UDP 協議接收數據也需要知道通信對端的地址信息。
int recvfrom (SOCKET s, char FAR* buf, int len, int flags, struct sockaddr FAR* from, int FAR* fromlen);
這個函數不同于 recv 函數的是多出來的最后兩個參數,from 參數是指向 sockaddr_in 結
構的指針,函數在這里返回數據發送方的地址,fromlen 參數用于返回前面的 sockaddr_in 結構
的長度。
與 TCP 協議相比,UDP 協議簡單多了,編程細節就不詳細介紹了。
TCPClient封裝類(tcpClient.hpp):
1 #ifndef __TCP_CLIENT_H__
2 #define __TCP_CLIENT_H__
3
4 #include <winsock2.h>
5 #include <stdio.h>
6 #include <iostream>
7
8 class CTcpClient
9 {
10 public:
11 std::
string m_strErrInfo;
//錯誤信息
12
13 CTcpClient()
14 {
15 WSAData wsaData;
16 if(WSAStartup(MAKEWORD(
2,
2),&wsaData) !=
0)
17 {
18 m_strErrInfo =
"WSAStartup失敗";
19 printf(m_strErrInfo.c_str());
20 }
21 if(LOBYTE(wsaData.wVersion) !=
2 || HIBYTE(wsaData.wVersion) !=
2)
22 {
23 m_strErrInfo =
"WSAStartup SOCKET版本不對";
24 printf(m_strErrInfo.c_str());
25 }
26 }
27
28 ~
CTcpClient()
29 {
30 WSACleanup();
31 }
32
33 int SendData(
const char *pAddr,
const char *
pPort
34 ,
int iSendTimeOut,
int iRecvTimeOut
35 ,
const char *pSendData,
int nSendLen
36 ,
char *pRecvData,
int nRecevLen)
37 {
38 int iTimeOut;
39 struct sockaddr_in addrServer;
40 m_strErrInfo=
"";
41 int nRet =
0;
42
43 //創建SOCKET
44 SOCKET sockClient = socket(AF_INET,SOCK_STREAM,
0);
45 do
46 {
47 if(sockClient ==
INVALID_SOCKET)
48 {
49 m_strErrInfo =
"socket創建失失敗";
50 nRet = -
1;
51 break;
52 }
53 //連接到服務器
54 memset(&addrServer,
0,
sizeof(sockaddr_in));
55 addrServer.sin_family =
AF_INET;
56 addrServer.sin_addr.s_addr =
inet_addr(pAddr);
57 addrServer.sin_port =
htons(atoi(pPort));
58
59 if(connect(sockClient,(
const struct sockaddr *)&addrServer,
sizeof(sockaddr)) !=
0)
60 {
61 nRet = -
2;
62 m_strErrInfo =
"連接到服務器失敗.";
63 break;
64 }
65 //設置發送超時
66 iTimeOut =
iSendTimeOut;
67
68 if(::setsockopt(sockClient,SOL_SOCKET,SO_SNDTIMEO,(
char *)&iTimeOut,
sizeof(iTimeOut))==
SOCKET_ERROR)
69 {
70 m_strErrInfo =
"setsockopt失敗";
71 nRet = -
3;
72 break;
73 }
74 //設置接收超時
75 iTimeOut =
iRecvTimeOut;
76
77 if(::setsockopt(sockClient,SOL_SOCKET,SO_RCVTIMEO,(
char *)&iTimeOut,
sizeof(iTimeOut))==
SOCKET_ERROR)
78 {
79 m_strErrInfo =
"setsockopt失敗";
80 nRet = -
4;
81 break;
82 }
83 //發送請求
84 if(send(sockClient, pSendData, nSendLen *
sizeof(
char),
0) <=
0)
85 {
86 m_strErrInfo =
"發送失敗.";
87 nRet = -
5;
88 break;
89 }
90
91 //接收服務端應答
92 memset(pRecvData,
0, nRecevLen *
sizeof(
char));
93 int rc =
SOCKET_ERROR;
94 int cnt = nRecevLen *
sizeof(
char);
95
96 while(cnt >
0)
97 {
98 rc = recv(sockClient, pRecvData, nRecevLen *
sizeof(
char),
0);
99
100 if(rc ==
SOCKET_ERROR)
101 {
102 m_strErrInfo =
"接收失敗";
103 nRet = -
6;
104 break;
105 }
106 if(rc ==
0)
107 {
108 if(nRet <=
0)
109 {
110 nRet = -
7;
111 m_strErrInfo =
"后臺無應答";
112 }
113 //nRet = ( ? -7 : nRet);
114 break;
115 }
116 nRet +=
rc;
117 pRecvData +=
rc;
118 cnt -=
rc;
119 }
120
121 }
while (
0);
122
123 closesocket(sockClient);
124 return nRet;
125 }
126
127 int SendData(
const char *pAddr,
const char *
pPort
128 ,
int iSendTimeOut,
int iRecvTimeOut
129 ,
const char *pSendData, std::
string &strRecv,
int iMulRecv =
0)
130 {
131 int iRet;
132 int iTimeOut;
133 struct sockaddr_in addrServer;
134 char szRecvDataBuf[
1024*
64+
1];
135
136 m_strErrInfo=
"";
137
138 //創建SOCKET
139 SOCKET sockClient = socket(AF_INET,SOCK_STREAM,
0);
140 if(sockClient ==
INVALID_SOCKET)
141 {
142 m_strErrInfo =
"socket創建失敗";
143 return -
1;
144 }
145
146 //連接到服務器
147 memset(&addrServer,
0,
sizeof(sockaddr_in));
148 addrServer.sin_family =
AF_INET;
149 addrServer.sin_addr.s_addr =
inet_addr(pAddr);
150 addrServer.sin_port =
htons(atoi(pPort));
151 if(connect(sockClient,(
const struct sockaddr *)&addrServer,
sizeof(sockaddr)) !=
0)
152 {
153 m_strErrInfo =
"連接服務器失敗";
154 goto _end;
155 }
156
157 //設置發送超時
158 iTimeOut =
iSendTimeOut;
159 if(::setsockopt(sockClient,SOL_SOCKET,SO_SNDTIMEO,(
char *)&iTimeOut,
sizeof(iTimeOut))==
SOCKET_ERROR)
160 {
161 m_strErrInfo =
"setsockopt失敗";
162 goto _end;
163 }
164 //設置接收超時
165 iTimeOut =
iRecvTimeOut;
166 if(::setsockopt(sockClient,SOL_SOCKET,SO_RCVTIMEO,(
char *)&iTimeOut,
sizeof(iTimeOut))==
SOCKET_ERROR)
167 {
168 m_strErrInfo =
"setsockopt失敗";
169 goto _end;
170 }
171
172 //發送請求
173 if(send(sockClient, pSendData, strlen(pSendData),
0) <=
0)
174 {
175 m_strErrInfo =
"發送失敗";
176 goto _end;
177 }
178
179 //接收服務端應答
180 strRecv =
"";
181 do
182 {
183 memset(szRecvDataBuf,
0,
sizeof(szRecvDataBuf));
184 iRet = recv(sockClient, szRecvDataBuf,
sizeof(szRecvDataBuf)-
1,
0);
185 strRecv +=
szRecvDataBuf;
186 }
while (iRet >
0 &&
iMulRecv);
187 if(
0 ==
strRecv.length())
188 {
189 m_strErrInfo =
"接收失敗";
190 }
191
192 //關閉SOCKET
193 closesocket(sockClient);
194 return 0;
195
196 _end:
197 closesocket(sockClient);
198 return -
1;
199 }
200
201 std::
string GetIPAddrByDNS(
const char *
pDNS)
202 {
203 //通過域名得到IP地址
204 std::
string strAddr;
205 WSADATA wsadata;
206 WSAStartup(MAKEWORD(
2,
2),&
wsadata);
207 hostent *phost=
gethostbyname(pDNS);
208 if(phost)
209 {
210 in_addr addr;
211 for(
int i=
0;;i++
)
212 {
213 char *p=phost->
h_addr_list[i];
214 if(p==NULL)
break;
215 memcpy(&addr.S_un.S_addr,p,phost->
h_length);
216 char* ip=
inet_ntoa(addr);
217 strAddr =
ip;
218
219 if (strAddr.length())
220 break;
221 }
222 }
223 return strAddr;
224 }
225 };
226 #endif ?
?
?
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀
總結
以上是生活随笔為你收集整理的Socket编程基本流程实践的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。