1. 程式人生 > 實用技巧 >C++tcp伺服器和跨平臺客戶端

C++tcp伺服器和跨平臺客戶端

TcpServer

#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <windows.h>
#include <winsock2.h>
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
#pragma comment(lib,"ws2_32.lib")

std::mutex g_Mutex;
bool g_IsQuit = false;

void QuitServer()
{
	while (!g_IsQuit)
	{
		short key = GetAsyncKeyState('A');
		if(key&0x0001)
		{
			g_Mutex.lock();
			g_IsQuit = true;
			g_Mutex.unlock();
		}
		std::this_thread::sleep_for(std::chrono::milliseconds(1));
	}
}

int main()
{
	//1.初始化網路環境
	WORD wVersion = MAKEWORD(2, 2);
	WSADATA wsaData;
	int err = WSAStartup(wVersion, &wsaData);

	if (err != 0)
	{
		printf("網路初始化錯誤,錯誤程式碼%d\n", err);
		system("pause");
		return 0;
	}

	//2.建立監聽套接字
	SOCKET listenSock = socket(
		AF_INET,
		SOCK_STREAM,
		IPPROTO_TCP
	);
	if (INVALID_SOCKET == listenSock)
	{
		printf("監聽套接字建立失敗\n");
		WSACleanup();
		system("pause");
		return 0;
	}

	//3.設定伺服器的埠資訊
	sockaddr_in sin;
	sin.sin_port = htons(6666);
	sin.sin_family = AF_INET;
	sin.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//任意ip

	//4.繫結地址資訊到監聽套接字
	if (SOCKET_ERROR == bind(listenSock, (sockaddr*)&sin, sizeof(sin)))
	{
		printf("繫結監聽套接字建立失敗\n");
		closesocket(listenSock);
		WSACleanup();
		system("pause");
		return 0;
	}
	//5.開始監聽
	if (SOCKET_ERROR == listen(listenSock, 100))
	{
		printf("監聽出現錯誤\n");
		closesocket(listenSock);
		WSACleanup();
		system("pause");
		return 0;
	}

	//6.收發資訊
	sockaddr_in clientaddinfo;
	int addlen = sizeof(clientaddinfo);
	SOCKET clientsock;

	clientsock = accept(listenSock, (sockaddr*)&clientaddinfo, &addlen);


	if (INVALID_SOCKET == clientsock)
	{
		printf("Accept錯誤\n");
		closesocket(listenSock);
		WSACleanup();
		system("pause");
		
		return 0;
	}

	//使用多執行緒轉發和接收資訊
	while (!g_IsQuit)
	{
		printf("接收到一個客戶:%d,IP:%s,Port:&d\n",
			clientsock,
			inet_ntoa(clientaddinfo.sin_addr),
			clientaddinfo.sin_port);
		char buf[MAX_PATH] = {};
		recv(clientsock, buf, MAX_PATH - 1, 0);
		printf("接收到的資料:%s\n", buf);

		send(clientsock, "this is my backdata!", strlen("this is my backdata!") + 1,0);
	}

	//7.關閉套接字和網路環境
	closesocket(clientsock);
	closesocket(listenSock);
	WSACleanup();
	system("pause");
	return 0;
}

Linux跨平臺網路客戶端

#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <windows.h>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")
#else
#include <unistd.h>
#include <arpa/inet.h>
#include <string.h>

#define MAX_PATH	260
typedef int SOCKET;
#define INVALID_SOCKET (SOCKET)(~0)
#define SOCKET_ERROR			(-1)
#define WSACleanup() //

#endif
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
using namespace std;

bool g_IsQuit = false;
bool g_IsSend = false;
char g_SendBuffer[MAX_PATH];
mutex g_Mutex1;
mutex g_Mutex2;

void InputData()
{
	while (!g_IsQuit )
	{
		printf("input msg:\n");
		scanf("%s",g_SendBuffer);
		if (strcmp(g_SendBuffer,"exit")==0)
		{
			g_Mutex1.lock();
			g_IsQuit = true;
			g_Mutex1.unlock();
			break;
		}else if (!g_IsSend)
		{
			g_Mutex2.lock();
			g_IsSend = true;
			g_Mutex2.unlock();
		}
		this_thread::sleep_for(chrono::milliseconds(1));
	}
}



int main()
{
	#ifdef _WIN32
	WORD wVersion = MAKEWORD(2, 2);
	WSADATA wsaData;
	int err = WSAStartup(wVersion, &wsaData);
	if (err != 0)
	{
		printf("初始化錯誤%d\n", err);
		system("pause");
		return 0;
	}
	#endif

	SOCKET clientSock = socket(
		AF_INET,
		SOCK_STREAM,
		IPPROTO_TCP
		);

	if (INVALID_SOCKET == clientSock)
	{
		printf("create clientsocket failed\n");
		WSACleanup();
		system("pause");
		return 0;
	}

	sockaddr_in sin;
	sin.sin_port = htons(12345);
	sin.sin_family = AF_INET;
	#ifdef _WIN32
	sin.sin_addr.S_un.S_addr = inet_addr("192.168.1.2");
	#else 
	sin.sin_addr.s_addr = inet_addr("192.168.1.2");
	#endif

	if (SOCKET_ERROR == connect(clientSock, (sockaddr*)&sin, sizeof(sin)))
	{
		printf("錯誤\n");
		#ifdef _WIN32
		closesocket(clientSock);
		#else
		close(clientSock);
		#endif
		WSACleanup();
		system("pause");
		return 0;
	}

	thread inputthread(InputData);
	inputthread.detach();


	char recvdata[MAX_PATH] = {};
	while (!g_IsQuit)
	{
		if (g_IsSend)
		{
			printf("start send data\n");
			send(clientSock,g_SendBuffer, strlen(g_SendBuffer) + 1, 0);
			printf("start recv data\n");
			recv(clientSock, recvdata, MAX_PATH, 0);
			printf("reved msg:%s\n", recvdata);
			g_Mutex2.lock();
			g_IsSend = false;
			g_Mutex2.unlock();
			printf("lock end\n");
		}
		this_thread::sleep_for(chrono::milliseconds(1));
	}

	#ifdef _WIN32
	closesocket(clientSock);
	#else
	close(clientSock);
	#endif
	WSACleanup();
	return 0;
}

總結

  • 跨平臺注意編碼
  • 通過使用內建的巨集,可以進行作業系統判斷 #ifdef _WIN32 #endif
  • arpa/inet.h:IP地址轉換