Socket编程基本流程实践
通訊基本流程圖如下所示:
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 必須是應(yīng)用程序首先調(diào)用的 Winsock 函數(shù)。它允許應(yīng)用程序指定所需的 15 Windows Sockets API 的版本,獲取特定 Winsock 實(shí)現(xiàn)的詳細(xì)信息。僅當(dāng)這個(gè)函數(shù)成功執(zhí)行之 16 后,應(yīng)用程序才能調(diào)用其他 Winsock API 17 每一個(gè)對(duì)WSAStartup的調(diào)用必須對(duì)應(yīng)一個(gè)對(duì)WSACleanup的調(diào)用, 這個(gè)函數(shù)釋放Winsock庫(kù)。 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; //設(shè)置地址家族 34 addr_in.sin_addr.S_un.S_addr = INADDR_ANY; 35 addr_in.sin_port = htons(8080); // 轉(zhuǎn)化端口號(hào)8080到網(wǎng)絡(luò)字節(jié)順序,并安排它到正確的成員 36 37 // 綁定這個(gè)套節(jié)字到一個(gè)本地地址 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 函數(shù)置套節(jié)字進(jìn)入監(jiān)聽狀態(tài) 46 os << "開始監(jiān)聽...." << inet_ntoa(addr_in.sin_addr) << endl; //inet_ntoa() 將32 位的二進(jìn)制數(shù)轉(zhuǎn)化為字符串 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 函數(shù)用于接受到來的連接。 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 函數(shù)在一個(gè)連接的套節(jié)字上發(fā)送緩沖區(qū)內(nèi)的數(shù)據(jù),返回發(fā)送數(shù)據(jù)的實(shí)際字節(jié)數(shù)。flag參數(shù)通常設(shè)置為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函數(shù)轉(zhuǎn)化一個(gè)"aa.bb.cc.dd"類型的IP地址字符串到長(zhǎng)整型,它是以網(wǎng)絡(luò)字節(jié)順序記錄的IP地址, 25 // sin_addr.S_un.S_addr指定了地址聯(lián)合中的此長(zhǎng)整型 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 // 客戶端程序在創(chuàng)建套節(jié)字之后,要使用 connect 函數(shù)請(qǐng)求與服務(wù)器連接 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 函數(shù)從對(duì)方接收數(shù)據(jù),并存儲(chǔ)它到指定的緩沖區(qū)。flag參數(shù)通常設(shè)置為0 40 ::recv(s, buf, 256, 0); 41 stringstream os; 42 os << "從服務(wù)器接收到數(shù)據(jù):" << buf; 43 cout << os.str() << endl; 44 45 system("pause"); 46 return 0; 47 }?
TCP 協(xié)議由于可靠、穩(wěn)定的特征而被用在大部分場(chǎng)合,但它對(duì)系統(tǒng)資源要求比較高。UDP
協(xié)議是一個(gè)簡(jiǎn)單的面向數(shù)據(jù)報(bào)的傳輸層協(xié)議,又叫用戶數(shù)據(jù)報(bào)協(xié)議。它提供了無連接的、不可
靠的數(shù)據(jù)傳輸服務(wù)。無連接是指他不像 TCP 協(xié)議那樣在通信前先與對(duì)方建立連接以確定對(duì)方
的狀態(tài)。不可靠是指它直接安裝指定 IP 地址和端口號(hào)將數(shù)據(jù)包發(fā)出去,如果對(duì)方不在線的話
數(shù)據(jù)就可能丟失。UDP 協(xié)議編程流程如下:
1.服務(wù)器端
(1)創(chuàng)建套節(jié)字(socket) 。
(2)綁定 IP 地址和端口(bind) 。
(3)收發(fā)數(shù)據(jù)(sendto/recvfrom) 。
(4)關(guān)閉連接 (closesocket) 。
2.客戶端
(1)創(chuàng)建套節(jié)字(socket) 。
(2)收發(fā)數(shù)據(jù)(sendto/recvfrom) 。
(3)關(guān)閉連接(closesocket) 。
UDP 協(xié)議用于發(fā)送和接收數(shù)據(jù)的函數(shù)是 sendto 和 recvfrom。它們的原形如下。
int sendto (
SOCKET s, // 用來發(fā)送數(shù)據(jù)的套節(jié)字
const char FAR * buf, // 指向發(fā)送數(shù)據(jù)的緩沖區(qū)
int len, // 要發(fā)送數(shù)據(jù)的長(zhǎng)度
int flags, // 一般指定為0
const struct sockaddr * to, // 指向一個(gè)包含目標(biāo)地址和端口號(hào)的sockaddr_in結(jié)構(gòu)
int tolen // 為 sockaddr_in 結(jié)構(gòu)的大小
);
同樣 UDP 協(xié)議接收數(shù)據(jù)也需要知道通信對(duì)端的地址信息。
int recvfrom (SOCKET s, char FAR* buf, int len, int flags, struct sockaddr FAR* from, int FAR* fromlen);
這個(gè)函數(shù)不同于 recv 函數(shù)的是多出來的最后兩個(gè)參數(shù),from 參數(shù)是指向 sockaddr_in 結(jié)
構(gòu)的指針,函數(shù)在這里返回?cái)?shù)據(jù)發(fā)送方的地址,fromlen 參數(shù)用于返回前面的 sockaddr_in 結(jié)構(gòu)
的長(zhǎng)度。
與 TCP 協(xié)議相比,UDP 協(xié)議簡(jiǎn)單多了,編程細(xì)節(jié)就不詳細(xì)介紹了。
TCPClient封裝類(tcpClient.hpp):
?
?
?
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的Socket编程基本流程实践的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Filter的详解与配置应用
- 下一篇: OAF TABLE中添加序号列