C++ 網絡編程 socket(結構化數據傳輸)

在這裏插入圖片描述
在這裏插入圖片描述

結構化數據傳輸

server

// ctrl + k + c
//  Ctrl + K+ U 
#define WIN32_LEAN_AND_MEAN

#include <iostream>
#include <Windows.h>
#include <WinSock2.h>

using namespace std;
//加入靜態連接庫
#pragma comment(lib,"ws2_32.lib")
//輸出目錄 $(SolutionDir)..bin/$(Platform)/$(Configuration)\
//中間目錄 $(SolutionDir)../temp/$(Platform)/$(Configuration)/$(ProjectName)\

struct DataPackage {
	int age;
	char name[32];
	//在客戶端聲明時也要保持該格式,字節對齊
};

int main()
{
	//啓動windows socket 2.x環境
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	WSAStartup(ver, &dat);
	//---------------------------
	//TCP 服務端
	//啓動windows scoket編程環境
	//1.建立一個socket

	SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	//ipv4 數據流 TCP


	//2.綁定接受客戶端連接的端口號bind
	sockaddr_in _sin = {};//
	_sin.sin_family = AF_INET;//網絡類型 ipv4
	_sin.sin_port = htons(4567);// host to net unsigned short 端口號 主機數據轉換到網絡數據,無符號數據類型

	_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");// 服務器的ip地址
													   //當不知道自己的本機ip地址時可以寫爲 INADDR_ANY
	if (bind(_sock, (sockaddr*)&_sin, sizeof(_sin)) == SOCKET_ERROR)
	{
		cout << "綁定用於接收客戶端連接的網絡端口失敗" << endl;
	}
	else {
		cout << "綁定網絡端口成功..." << endl;
	}//網絡端口是否被佔用

	 //3.監聽網絡端口listen
	if (listen(_sock, 5) == SOCKET_ERROR)//等待連接的隊列的長度
	{
		cout << "監聽網絡端口失敗..." << endl;
	}
	else {
		cout << "監聽網絡端口成功..." << endl;
	}
	//4.等待接受客戶端連接accept
	sockaddr_in clientAddr = {};//客戶端的地址和端口
	int nAddrLen = sizeof(sockaddr_in);
	SOCKET _cSock = INVALID_SOCKET;
	
	_cSock = accept(_sock, (sockaddr *)&clientAddr, &nAddrLen);
	if (_cSock == INVALID_SOCKET)
	{
		cout << "錯誤,接收到無效客戶端SOCKET..." << endl;
	}
	else
	{
		cout << "接收數據客戶端有效..." << endl;
	}
	cout << "新客戶端加入:socket = " << _cSock << ", IP = " << inet_ntoa(clientAddr.sin_addr) /*轉換爲 字符串 刻度的字符串數字*/ << endl;
	char _recvBuf[128] = {};//接收緩存區
	while (true)
	{
		//5.接收客戶端的請求數據
		int nLen = recv(_cSock, _recvBuf, 128, 0);
		if (nLen <= 0)
		{
			cout << "客戶端已退出,任務結束" << endl;
			break;
		}
		cout << "收到命令:" << _recvBuf << endl;
		//6.處理請求
		if (strcmp(_recvBuf, "getInfo") == 0) {
			//7.向客戶端發送一條數據send
			DataPackage dp = {80,"張國榮"};
			send(_cSock, (const char*)&dp, sizeof(DataPackage), 0);//strlen(msgBuf) + 1 將結尾符一併發送過去
		}
		//if (strcmp(_recvBuf, "getName") == 0) {
		//	char msgBuf[] = "Xiao Qiang!";
		//	//7.向客戶端發送一條數據send
		//	send(_cSock, msgBuf, strlen(msgBuf) + 1, 0);//strlen(msgBuf) + 1 將結尾符一併發送過去
		//}
		//else if (strcmp(_recvBuf, "getAge") == 0) {
		//	char msgBuf[] = "18";
		//	send(_cSock, msgBuf, strlen(msgBuf) + 1, 0);//strlen(msgBuf) + 1 將結尾符一併發送過去
		//}

		else {
			char msgBuf[] = "hello ,I'm server!";
			send(_cSock, msgBuf, strlen(msgBuf) + 1, 0);//strlen(msgBuf) + 1 將結尾符一併發送過去
		}
		
	}
	//8.關閉socket closesocket
	closesocket(_sock);

	//----------------------------
	//清除windows socket環境
	WSACleanup();
	getchar();
	return 0;
}

client

#define WIN32_LEAN_AND_MEAN

#include <iostream>
#include <Windows.h>
#include <WinSock2.h>

using namespace std;
//加入靜態連接庫
#pragma comment(lib,"ws2_32.lib")
//輸出目錄 $(SolutionDir)..bin/$(Platform)/$(Configuration)\
//中間目錄 $(SolutionDir)../temp/$(Platform)/$(Configuration)/$(ProjectName)\

struct DataPackage {
	int age;
	char name[32];
	//在客戶端聲明時也要保持該格式,字節對齊
};

int main()
{
	//啓動windows socket 2.x環境
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	WSAStartup(ver, &dat);
	//-----------------------------
	//TCP客戶端
	//1.建立一個socket
	SOCKET _sock = socket(AF_INET, SOCK_STREAM, 0);
	if (_sock == INVALID_SOCKET)
	{
		cout << "連接服務器失敗..." << endl;
	}
	else {
		cout << "連接服務器成功.." << endl;
	}
	//2.連接服務器connect
	sockaddr_in _sin = {};
	_sin.sin_family = AF_INET;
	_sin.sin_port = htons(4567);
	_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//聲明服務端地址
	int ret = connect(_sock, (sockaddr*)(&_sin), sizeof(sockaddr_in));
	if (ret == SOCKET_ERROR)
	{
		cout << "連接服務器失敗" << endl;
	}
	else {
		cout << "連接服務器成功" << endl;
	}
	while (true)
	{
		//3.輸入請求命令
		char cmdBuf[128] = {};
		//cin >> cmdBuf;
		scanf("%s", cmdBuf);
		//4.處理請求
		if (strcmp(cmdBuf, "exit") == 0) {
			cout << "已退出客戶端,任務結束!" << endl;
			break;
		}
		else {
			//5.把命令發送給服務端
			send(_sock, cmdBuf, strlen(cmdBuf) + 1, 0);
		}
		//6.接受服務器信息recv
		char recBuf[256] = {};//創建數據緩衝區
		int nlen = recv(_sock, recBuf, 256, 0);
		if (nlen > 0) {
			DataPackage *dp = (DataPackage *)recBuf;
			cout << "接受的數據爲:age: " << dp->age << ", name: "<< dp->name << endl;
		}
		else {
			cout << "接受數據失敗" << endl;
		}
	}
	
	//7.關閉socket closesocket
	closesocket(_sock);
	//----------------------------
	//清除windows socket環境
	WSACleanup();
	getchar();
	return 0;
}

在這裏插入圖片描述

以上命名格式輸入錯誤的時,強制類型轉換帶來的不安全;

2. 網絡數據報文

在這裏插入圖片描述

* 報文有兩個部分,包頭和包體,是網絡消息的基本單元;
* 包頭:描述本次消息包的大小,描述數據的作用;
*包體:數據;

2.1 server

// ctrl + k + c
//  Ctrl + K+ U 
#define WIN32_LEAN_AND_MEAN

#include <iostream>
#include <Windows.h>
#include <WinSock2.h>

using namespace std;
//加入靜態連接庫
#pragma comment(lib,"ws2_32.lib")
//輸出目錄 $(SolutionDir)..bin/$(Platform)/$(Configuration)\
//中間目錄 $(SolutionDir)../temp/$(Platform)/$(Configuration)/$(ProjectName)\

enum CMD{
	CMD_LOGIN,
	CMD_LOGOUT,
	LOG_ERROR
};
//消息頭
struct  DataHeader{
	short dataLength;//數據長度
	short cmd;//
};
//DataPackage
struct Login{
	char userName[32];
	char PassWord[32];
};
struct LoginResult {
	int result;
};

struct LogOut
{
	char userName[32];
};
struct LogOutResult {
	int result;
};

int main()
{
	//啓動windows socket 2.x環境
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	WSAStartup(ver, &dat);
	//---------------------------
	//TCP 服務端
	//啓動windows scoket編程環境
	//1.建立一個socket

	SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	//ipv4 數據流 TCP


	//2.綁定接受客戶端連接的端口號bind
	sockaddr_in _sin = {};//
	_sin.sin_family = AF_INET;//網絡類型 ipv4
	_sin.sin_port = htons(4567);// host to net unsigned short 端口號 主機數據轉換到網絡數據,無符號數據類型

	_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");// 服務器的ip地址
													   //當不知道自己的本機ip地址時可以寫爲 INADDR_ANY
	if (bind(_sock, (sockaddr*)&_sin, sizeof(_sin)) == SOCKET_ERROR)
	{
		cout << "綁定用於接收客戶端連接的網絡端口失敗" << endl;
	}
	else {
		cout << "綁定網絡端口成功..." << endl;
	}//網絡端口是否被佔用

	 //3.監聽網絡端口listen
	if (listen(_sock, 5) == SOCKET_ERROR)//等待連接的隊列的長度
	{
		cout << "監聽網絡端口失敗..." << endl;
	}
	else {
		cout << "監聽網絡端口成功..." << endl;
	}
	//4.等待接受客戶端連接accept
	sockaddr_in clientAddr = {};//客戶端的地址和端口
	int nAddrLen = sizeof(sockaddr_in);
	SOCKET _cSock = INVALID_SOCKET;
	
	_cSock = accept(_sock, (sockaddr *)&clientAddr, &nAddrLen);
	if (_cSock == INVALID_SOCKET)
	{
		cout << "錯誤,接收到無效客戶端SOCKET..." << endl;
	}
	else
	{
		cout << "接收數據客戶端有效..." << endl;
	}
	cout << "新客戶端加入: socket = " << _cSock << ", IP = " << inet_ntoa(clientAddr.sin_addr) /*轉換爲 字符串 刻度的字符串數字*/ << endl;
	
	while (true)
	{
		DataHeader header = {};
		//5.接收客戶端的請求數據
		int nLen = recv(_cSock,(char*)&header, sizeof(DataHeader), 0);
		if (nLen <= 0)
		{
			cout << "客戶端已退出,任務結束" << endl;
			break;
		}
		cout << "收到命令:數據命令:" << header.cmd<< ",數據長度:"<< header.dataLength << endl;
		//6.處理請求

		switch (header.cmd) {
			case CMD_LOGIN: 
			{
				Login login = {};
				recv(_cSock, (char*)&login, sizeof(Login), 0);
				LoginResult ret = {1};
				//忽略判斷用戶名和密碼是否正確
				send(_cSock, (char*)&header, sizeof(DataHeader), 0);
				send(_cSock, (char*)&ret, sizeof(LoginResult), 0);
			}
			break;
			case CMD_LOGOUT: 
			{
				LogOut logOut = {};
				recv(_cSock, (char*)&logOut, sizeof(LogOut), 0);
				LogOutResult ret = { 1 };
				//忽略判斷用戶名和密碼是否正確
				send(_cSock, (char*)&header, sizeof(DataHeader), 0);
				send(_cSock, (char*)&ret, sizeof(LogOutResult), 0);
			}
			 break;
			default:
			{
				header.cmd = LOG_ERROR;
				header.dataLength = 0;
				send(_cSock, (char *)&header, sizeof(header), 0);//strlen(msgBuf) + 1 將結尾符一併發送過去
				break;
			}
		}	
	}
	//8.關閉socket closesocket
	closesocket(_sock);

	//----------------------------
	//清除windows socket環境
	WSACleanup();
	getchar();
	return 0;
}

2.2 client

#define WIN32_LEAN_AND_MEAN

#include <iostream>
#include <Windows.h>
#include <WinSock2.h>

using namespace std;
//加入靜態連接庫
#pragma comment(lib,"ws2_32.lib")
//輸出目錄 $(SolutionDir)..bin/$(Platform)/$(Configuration)\
//中間目錄 $(SolutionDir)../temp/$(Platform)/$(Configuration)/$(ProjectName)\

enum CMD {
	CMD_LOGIN,
	CMD_LOGOUT,
	LOG_ERROR
};
//消息頭
struct  DataHeader {
	short dataLength;//數據長度
	short cmd;//
};
//DataPackage
struct Login {
	char userName[32];
	char PassWord[32];
};
struct LoginResult {
	int result;
};

struct LogOut
{
	char userName[32];
};
struct LogOutResult {
	int result;
};


int main()
{
	//啓動windows socket 2.x環境
	WORD ver = MAKEWORD(2, 2);
	WSADATA dat;
	WSAStartup(ver, &dat);
	//-----------------------------
	//TCP客戶端
	//1.建立一個socket
	SOCKET _sock = socket(AF_INET, SOCK_STREAM, 0);
	if (_sock == INVALID_SOCKET)
	{
		cout << "連接服務器失敗..." << endl;
	}
	else {
		cout << "連接服務器成功.." << endl;
	}
	//2.連接服務器connect
	sockaddr_in _sin = {};
	_sin.sin_family = AF_INET;
	_sin.sin_port = htons(4567);
	_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//聲明服務端地址
	int ret = connect(_sock, (sockaddr*)(&_sin), sizeof(sockaddr_in));
	if (ret == SOCKET_ERROR)
	{
		cout << "連接服務器失敗" << endl;
	}
	else {
		cout << "連接服務器成功" << endl;
	}
	while (true)
	{
		//3.輸入請求命令
		char cmdBuf[128] = {};
		//cin >> cmdBuf;
		scanf("%s", cmdBuf);
		//4.處理請求
		//5.把命令發送給服務端
		if (strcmp(cmdBuf, "exit") == 0) {
			cout << "已退出客戶端,任務結束!" << endl;
			break;
		}
		else if (strcmp(cmdBuf, "login") == 0) {
			Login login = { "jmm","lovely" };
			DataHeader dh = {sizeof(login),CMD_LOGIN };
			//5.把命令發送給服務端
			send(_sock, (char*)&dh, sizeof(dh), 0);
			send(_sock, (char *)&login, sizeof(login), 0);
			// 接受服務器返回的數據
			DataHeader retHeader = {};
			LoginResult loginRet = {};
			recv(_sock, (char*)&retHeader, sizeof(retHeader), 0);
			recv(_sock, (char*)&loginRet, sizeof(loginRet), 0);
			cout << "LoginResult: " << loginRet.result << endl;
		}
		else if (strcmp(cmdBuf, "logout") == 0) {
			LogOut logout = {};
			DataHeader dh = {sizeof(logout),CMD_LOGOUT };
			//5.把命令發送給服務端
			send(_sock, (char *)&dh, sizeof(dh), 0);
			send(_sock, (char *)&logout, sizeof(logout), 0);
			// 接受服務器返回的數據
			DataHeader retHeader = {};
			LoginResult logoutRet = {};
			recv(_sock, (char*)&retHeader, sizeof(retHeader), 0);
			recv(_sock, (char*)&logoutRet, sizeof(logoutRet), 0);
			cout << "LogOutResult: " << logoutRet.result << endl;
		}
		else {
			cout << "不支持該命令,請重新輸入!" << endl;
		}
	}
	
	//7.關閉socket closesocket
	closesocket(_sock);
	//----------------------------
	//清除windows socket環境
	WSACleanup();
	getchar();
	return 0;
}

2.3 運行結果

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章