C++封裝的基於WinSock2的TCP服務端、客戶端
阿新 • • 發佈:2019-01-26
無聊研究Winsock套接字程式設計,用原生的C語言介面寫出來的程式碼看著難受,於是自己簡單用C++封裝一下,把思路過程理清,方便自己後續翻看和新手學習。
只寫好了TCP通訊服務端,有空把客戶端流程也封裝一下。
先上主函式:
JTCPserver.cpp內容:// main.cpp : 異想家sandeepin poi! #include "stdafx.h" #include <iostream> extern int JTCPserver(); extern int JTCPclient(); int main() { JTCPserver(); // 兩者選一 // JTCPclient(); return 0; }
JTCPclient.cpp內容:// JTCPserver.cpp : 蔣方正封裝的TCP服務端 #include "stdafx.h" #include <iostream> #include <string> #include <WinSock2.h> // WinSocket #include <WS2tcpip.h> // IP地址轉換用到inet_pton #pragma comment(lib,"ws2_32.lib") using namespace std; // 【1】初始化WinSock bool initWinSock(); // 【2】建立socket bool createSocket(SOCKET &listenScok); // 【3】socket繫結本機地址資訊 bool bindIPandPort(SOCKET &listenScok, const string ip, const unsigned short port); // 【4】偵聽socket,接收客戶端請求 bool listenSocket(SOCKET &listenScok); // 【5】等待客戶端連線-阻塞 bool waitClientConnect(SOCKET &listenScok, SOCKET &clientSock); // 【6】接收資料-阻塞 bool receiveData(SOCKET &clientSock, string &data); // 【7】停止套接字的接收、傳送 bool shutdownSocket(SOCKET &clientSock); // 【8】傳送資訊 bool sendData(SOCKET &clientSock, const string &data); int JTCPserver() { SOCKET listenScok; // 服務端Socket SOCKET clientSock; // 客戶端Socket string data; // 收到的資料 // 【1】初始化WinSock initWinSock(); // 【2】建立socket createSocket(listenScok); // 【3】socket繫結本機地址資訊 bindIPandPort(listenScok, "127.0.0.1", 1994); // 【4】偵聽socket,接收客戶端請求 listenSocket(listenScok); // 坐等客戶端連線 bool isClientSockConnect = false; // 是否有客戶端連進來 bool isReceiveData = false; // 是否接收資料成功 while (true) { if (!isClientSockConnect) { // 【5】等待客戶端連線 isClientSockConnect = waitClientConnect(listenScok, clientSock); } else { if(!isReceiveData) { // 【6】接收資料-阻塞 isReceiveData = receiveData(clientSock, data); // 如果接收資料失敗則斷開 if(!isReceiveData) { // 【7】停止套接字的接收、傳送 shutdownSocket(clientSock); cout << "等待客戶端再連線..." << endl; isClientSockConnect = false; // 可以重連了 } } if(isReceiveData && data != "jfzpoi" && data != "@end#") { isReceiveData = false; } if(isReceiveData && data == "jfzpoi") { // 【8】傳送資訊(收的資料為jfzpoi) sendData(clientSock, "sandeepin!\r\n"); isReceiveData = false; } if (isReceiveData && data == "@end#") { // 【9】關閉相關 int err; // err = shutdown(listenScok, 2); // if (err == SOCKET_ERROR) // { // cout << "關閉失敗!" << endl; // } // 關閉套接字,釋放資源 err = closesocket(listenScok); if (err == SOCKET_ERROR) { cout << "關閉socket失敗!" << endl; } // 停止使用WinSock庫,釋放對應資源 if (WSACleanup() != 0) { cout << "WSA清空失敗!" << endl; } cout << "關完了,坐等關機!poi" << endl; return 0; } } } } // 【1】初始化WinSock bool initWinSock() { WORD verision = MAKEWORD(2, 2); WSADATA lpData; int intEr = WSAStartup(verision, &lpData); // 指定winsock版本並初始化 if (intEr != 0) { cout << "WinSock初始化失敗!" << endl; return false; } cout << "WinSock初始化成功!" << endl; return true; } // 【2】建立socket bool createSocket(SOCKET &listenScok) { // 建立偵聽socket listenScok = socket(AF_INET, SOCK_STREAM, 0); if (listenScok == INVALID_SOCKET) { cout << "socket建立失敗!" << endl; return false; } cout << "socket建立成功!" << endl; return true; } // 【3】socket繫結本機地址資訊 bool bindIPandPort(SOCKET &listenScok, const string ip, const unsigned short port) { // 製作sockaddr_in結構體 // 在bind函式,connect函式裡提到了套接字程式設計網路地址資訊結構體const struct sockaddr和const struct sockaddr_i sockaddr_in hostAddr; hostAddr.sin_family = AF_INET; hostAddr.sin_port = htons(port);//轉換成網路位元組序 //hostAddr.sin_addr.S_un.S_addr = inet_addr(SERVERIP);//轉換成網路位元組序 //cout << "net IP:" << hostAddr.sin_addr.S_un.S_addr << endl; /* inet_addr()版本太低,被棄用使用inet_pton(協議族,字串IP地址,void目標in_addr*) 標頭檔案:WS2tcpip.h */ in_addr addr; inet_pton(AF_INET, ip.c_str(), (void*)&addr); hostAddr.sin_addr = addr; cout << "ip(網路位元組序):" << addr.S_un.S_addr << endl; cout << "ip(常規形式):" << ip.c_str() << endl; // 偵聽套接字listenSock繫結本機地址資訊 int err = bind(listenScok, (struct sockaddr*)&hostAddr, sizeof(sockaddr)); if (err != 0) { cout << "本地IP繫結失敗!" << endl; return false; } return true; } // 【4】偵聽socket,接收客戶端請求 bool listenSocket(SOCKET &listenScok) { // 設定套接字為偵聽狀態,準備接收客戶機程序傳送來的連線請求 int err = listen(listenScok, 3); if (err != 0) { cout << "socket監聽失敗!" << endl; return false; } cout << "監聽客戶端連線中……" << endl; return true; } // 【5】等待客戶端連線-阻塞 bool waitClientConnect(SOCKET &listenScok, SOCKET &clientSock) { sockaddr_in clientAddr; int len = sizeof(struct sockaddr); // 必須指定長度,否則會導致accept返回10014錯誤 // accept會迴圈等待客戶端連線 clientSock = accept(listenScok, (struct sockaddr*)&clientAddr, &len); cout << "客戶端Socket編號:" << clientSock << endl; if (clientSock == INVALID_SOCKET) { cout << "客戶端連線失敗!" << endl; cout << WSAGetLastError() << endl; return false; } return true; } // 【6】接收資料-阻塞 bool receiveData(SOCKET &clientSock, string &data) { static int cnt = 1; // 接收資料編號-靜態 // 通過已建立連線的套接字,接收資料 設定緩衝1024位元組 char buf[1024] = "\0"; // flags操作方式(0正常資料,MSG_PEED系統緩衝區的資料複製到所提供的接收緩衝區內,系統緩衝區資料未刪除,MSG_OOB處理帶外資料,通常用引數0即可) int buflen = recv(clientSock, buf, 1024, 0); if (buflen == SOCKET_ERROR) { cout << "接收失敗!" << endl; return false; } // 一切正常則顯示接收資料 data = string(buf); cout << "收到第" << cnt++ << "次,內容為:\n" << buf << endl; return true; } // 【7】停止套接字的接收、傳送 bool shutdownSocket(SOCKET &clientSock) { //(收完就關)停止套接字的接收、傳送功能(0禁止接收,1禁止傳送,2禁制接收發送) int err = shutdown(clientSock, 2); if (err == SOCKET_ERROR) { cout << "關閉Socket失敗!" << endl; return false; } return true; } // 【8】傳送資訊 bool sendData(SOCKET &clientSock, const string &data) { int err = send(clientSock, data.c_str(), data.size(), 0); if (err == SOCKET_ERROR) { cout << "傳送失敗!" << endl; return false; } cout << "傳送資料為:\n" << data << endl; return true; }
// JTCPclient.cpp : 蔣方正封裝的TCP客戶端程式碼 #include "stdafx.h" #include <iostream> #include <WinSock2.h> #include <WS2tcpip.h> #pragma comment(lib,"ws2_32.lib") using namespace std; // 【1】初始化WinSock bool initWinSockC(); // 【2】建立socket bool createSocketC(SOCKET &listenScok); // 【3】連線到伺服器 bool connectSocketC(SOCKET &conSock, const string ip, const unsigned short port); // 【4】傳送資料 bool sendDataC(SOCKET &clientSock, const string &data); // 【5】接收資料 bool receiveDataC(SOCKET &clientSock, string &data); int JTCPclient() { SOCKET clientSock; // 客戶端Socket string data; // 收到的資料 bool isCreateSocket = false; // 是否建立了Socket bool isConnectSocket = false; // 是否連上了伺服器 bool isSendDataOK = false; // 是否傳送成功資料 bool isReceiveDataOK = false; // 是否接收成功資料 // 【1】初始化WinSock if(initWinSockC()) { while (true) { if(!isCreateSocket) { // 【2】建立socket createSocketC(clientSock); isCreateSocket = true; } else { if(!isConnectSocket) { // 【3】連線到伺服器 connectSocketC(clientSock, "127.0.0.1", 1994); isConnectSocket = true; } else { if(!isSendDataOK) { // 【4】傳送資料 isSendDataOK = sendDataC(clientSock, "jfz hello\r\n"); } else { if(!isReceiveDataOK) { // 【5】接收資料 isReceiveDataOK = receiveDataC(clientSock, data); } else { if(data == "@end#") { WSACleanup(); return 0; } isReceiveDataOK = false; isSendDataOK = false; } } } } } } return 0; } // 【1】初始化WinSock bool initWinSockC() { WORD verision = MAKEWORD(2, 2); WSADATA lpData; int intEr = WSAStartup(verision, &lpData); // 指定winsock版本並初始化 if (intEr != 0) { std::cout << "WinSock初始化失敗!" << endl; return false; } std::cout << "WinSock初始化成功!" << endl; return true; } // 【2】建立socket bool createSocketC(SOCKET &listenScok) { // 建立偵聽socket listenScok = socket(AF_INET, SOCK_STREAM, 0); if (listenScok == INVALID_SOCKET) { cout << "socket建立失敗!" << endl; return false; } cout << "socket建立成功!" << endl; return true; } // 【3】連線到伺服器 bool connectSocketC(SOCKET &conSock, const string ip, const unsigned short port) { // 建立地址結構體 sockaddr_in hostAddr; hostAddr.sin_family = AF_INET; hostAddr.sin_port = htons(port);//轉換成網路位元組序 //hostAddr.sin_addr.S_un.S_addr = inet_addr(SERVERIP);//轉換成網路位元組序 //cout << "net IP:" << hostAddr.sin_addr.S_un.S_addr << endl; /* inet_addr()版本太低,被棄用使用inet_pton(協議族,字串IP地址,void目標in_addr*) 標頭檔案:WS2tcpip.h */ in_addr addr; inet_pton(AF_INET, ip.c_str(), (void*)&addr); hostAddr.sin_addr = addr; cout << "ip(網路位元組序):" << addr.S_un.S_addr << endl; cout << "ip(常規形式):" << ip.c_str() << endl; // 向伺服器提出連線請求 int err = connect(conSock, (sockaddr*)&hostAddr, sizeof(sockaddr)); if (err == INVALID_SOCKET) { cout << "連線伺服器失敗!" << endl; return false; } return true; } // 【4】傳送資料 bool sendDataC(SOCKET &clientSock, const string &data) { int err = send(clientSock, data.c_str(), data.size(), 0); if (err == SOCKET_ERROR) { cout << "傳送失敗!" << endl; return false; } cout << "傳送資料為:\n" << data.c_str() << endl; return true; } // 【5】接收資料 bool receiveDataC(SOCKET &clientSock, string &data) { static int cnt = 1; // 接收資料編號-靜態 // 通過已建立連線的套接字,接收資料 設定緩衝1024位元組 char buf[1024] = "\0"; // flags操作方式(0正常資料,MSG_PEED系統緩衝區的資料複製到所提供的接收緩衝區內,系統緩衝區資料未刪除,MSG_OOB處理帶外資料,通常用引數0即可) int buflen = recv(clientSock, buf, 1024, 0); if (buflen == SOCKET_ERROR) { cout << "接收失敗!" << endl; return false; } // 一切正常則顯示接收資料 data = string(buf); cout << "收到第" << cnt++ << "次,內容為:\n" << buf << endl; return true; }