天天看點

C++ socket 簡單通訊

一、socket通訊開發步驟

1、建立socket

1.1、指定服務端相關資訊系

指定socket版本号 如: winsock_ver = MAKEWORD(2, 2);

addr_len = sizeof(SOCKADDR_IN);

//指定ip位址族

addr_svr.sin_family = AF_INET;

//轉換網絡編碼,指定偵聽本地端口

addr_svr.sin_port = ::htons(SERVER_PORT);

//設定接收任意位址的連接配接

addr_svr.sin_addr.S_un.S_addr = ADDR_ANY;

//設定某一指定記憶體内容為0;

memset(buf_ip, 0, IP_BUF_SIZE)将buf_ip全部指定為0, IP_BUF_SIZE:IP位址長度

1.2、啟動socket

//WSAStartup,即WSA(Windows Sockets Asynchronous,Windows異步套接字)的啟動指令。0表示成功
	ret_val = ::WSAStartup(winsock_ver, &wsa_data);
	if (ret_val != 0)
	{
		cerr << "WSA failed to start up!Error code: " << ::WSAGetLastError() << "\n";
		system("pause");
		exit(1);
	}
	cout << "WSA started up successfully...\n";
           
1.3、建立服務端socket
           
//建立socket  socket如果調用成功就傳回新建立的套接字的描述符
	sock_svr = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sock_svr == INVALID_SOCKET)
	{
		cerr << "Failed to create server socket!Error code: " << ::WSAGetLastError() << "\n";
		::WSACleanup();
		system("pause");
		exit(1);
	}
	cout << "Server socket created successfully...\n";
           

2、綁定

//bind 綁定要通信的程式,成為伺服器,如果函數執行成功,傳回值為0,否則為SOCKET_ERROR。

//sock_svr就是第二步成功買回的,不,是申請到的接口

//(SOCKADDR*)&addr_svr 是指程序具體位址,就像具體電話号碼一樣,他的類型是一個結構體

//addr_len就是位置的長度

ret_val = ::bind(sock_svr, (SOCKADDR*)&addr_svr, addr_len);
	if (ret_val != 0)
	{
		cerr << "Failed to bind server socket!Error code: " << ::WSAGetLastError() << "\n";
		::WSACleanup();
		system("pause");
		exit(1);
	}
	cout << "Server socket bound successfully...\n";
           

3、監聽端口

ret_val = ::listen(sock_svr, SOMAXCONN);
	if (ret_val == SOCKET_ERROR)
	{
		cerr << "Server socket failed to listen!Error code: " << ::WSAGetLastError() << "\n";
		::WSACleanup();
		system("pause");
		exit(1);
	}
	cout << "Server socket started to listen...\n";
	//
	cout << "Server started successfully..." << endl;
           

4、等待用戶端,

while (true)
	{
		//接受用戶端的資訊
		sock_clt = ::accept(sock_svr, (SOCKADDR*)&addr_clt, &addr_len);
		cout << "ssss " << sock_clt << endl;
		if (sock_clt == INVALID_SOCKET)
		{
			cout << "ssss " << sock_clt <<endl;
			cerr << "Failed to accept client!Error code: " << ::WSAGetLastError() << "\n";
			::WSACleanup();
			system("pause");
			exit(1);
		}
           

5、接收用戶端發送的資訊、發送資訊給用戶端

char buf_msg[MSG_BUF_SIZE];  //  接收資訊
	int ret_val = 0;   //傳回讀入位元組數
	int snd_result = 0;  //傳回發送資訊位元組數

	do
	{
		memset(buf_msg, 0, MSG_BUF_SIZE);
		//接收資料  若無錯誤,傳回讀入位元組數
		ret_val = ::recv(sock_clt, buf_msg, MSG_BUF_SIZE, 0);
		if (ret_val > 0)
		{
			//如果接送的資訊為“exit”,則關閉連接配接
			if (strcmp(buf_msg, "exit") == 0)
			{
				cout << "Client requests to close the connection..." << endl;
				break;
			}
			//列印接收資訊
			cout << "Message received: " << buf_msg << endl;

			//發送消息給用戶端
			char send_to_client[SEND_BUF_SIZE];  //發送給用戶端的資訊
			cout << "enter the message that you want to send client: " << flush;
			cin.getline(send_to_client, SEND_BUF_SIZE);//控制台輸入需要發送資訊
			cout << "enter the message " << send_to_client << endl;
			snd_result = ::send(sock_clt, send_to_client, static_cast<int>(strlen(send_to_client)), 0);

			//snd_result = ::send(sock_clt, buf_msg, MSG_BUF_SIZE, 0);
			//如果發送失敗,即snd_result=-1
			if (snd_result == SOCKET_ERROR)
			{
				cerr << "Failed to send message to client!Error code: " << ::GetLastError() << "\n";
				::closesocket(sock_clt);
				system("pause");
				return 1;
			}
		}//接受資訊失敗ret_val==0
		else if (ret_val == 0)
		{
			cout << "connection closed..." << endl;
		}
		else
		{
			cerr << "Failed to receive message from client!Error code: " << ::GetLastError() << "\n";
			::closesocket(sock_clt);
			system("pause");
			return 1;
		}
	} while (ret_val > 0);
	//
	ret_val = ::shutdown(sock_clt, SD_SEND);
	if (ret_val == SOCKET_ERROR)
	{
		cerr << "Failed to shutdown the client socket!Error code: " << ::GetLastError() << "\n";
		::closesocket(sock_clt);
		system("pause");
		return 1;
	}
           

6、建立用戶端socket

6.1、同樣先建立socket

WSADATA wsa_data; //WSADATA變量,包含windows socket執行的資訊
	int i_result = 0; //接收傳回值
	SOCKET sock_client = INVALID_SOCKET;
	addrinfo *result = nullptr, hints;
	//初始化winsock動态庫(ws2_32.dll),MAKEWORD(2, 2)用于請求使用winsock2.2版本
	i_result = WSAStartup(MAKEWORD(2, 2), &wsa_data);
	if (i_result != 0) {
		cerr << "WSAStartup() function failed: " << i_result << "\n";
		system("pause");
		return 1;
	}
	SecureZeroMemory(&hints, sizeof(hints));//其作用是用0來填充一塊記憶體區域
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	//getaddrinfo函數能夠處理名字到位址以及服務到端口這兩種轉換,
	//傳回的是一個sockaddr結構的連結清單而不是一個位址清單。
	//這些sockaddr結構随後可由套接口函數直接使用
	i_result = getaddrinfo("127.0.0.1", DEFAULT_PORT, &hints, &result);
	if (i_result != 0) {
		cerr << "getaddrinfo() function failed with error: " << WSAGetLastError() << "\n";
		WSACleanup();
		system("pause");
		return 1;
	}
	//建立套接字
	sock_client = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
	if (sock_client == INVALID_SOCKET) {
		cerr << "socket() function failed with error: " << WSAGetLastError() << "\n";
		WSACleanup();
		system("pause");
		return 1;
	}
           

6.2、連接配接伺服器

//連接配接伺服器
	i_result = connect(sock_client, result->ai_addr, result->ai_addrlen);
	if (i_result == SOCKET_ERROR) {
		cerr << "connect() function failed with error: " << WSAGetLastError() << "\n";
		WSACleanup();
		system("pause");
		return 1;
	}
	cout << "connect server successfully..." << endl;
	//釋放由getaddrinfo傳回的存儲空間,包括addrinfo結構、ai_addr結構和ai_canonname字元串
	freeaddrinfo(result);
           

6.3、發送資訊給服務端

//發送資訊給服務端
	char send_buf[SEND_BUF_SIZE];
	int recv_result = 0; //接收伺服器傳回的資料的傳回值
	//SecureZeroMemory(send_buf, SEND_BUF_SIZE);
	do {
		cout << "enter the message that you want to send: " << flush;
		cin.getline(send_buf, SEND_BUF_SIZE);
		i_result = send(sock_client, send_buf, static_cast<int>(strlen(send_buf)), 0);
		if (i_result == SOCKET_ERROR) {
			cerr << "send() function failed with error: " << WSAGetLastError() << "\n";
			closesocket(sock_client);
			WSACleanup();
			system("pause");
			return 1;
		}
		cout << "Bytes sent: " << i_result << endl;
		
		//接收伺服器傳回的資料
		char rec_from_server[SEND_BUF_SIZE];
		recv_result = recv(sock_client, rec_from_server, SEND_BUF_SIZE, 0);
		//recv_result = recv(sock_client, send_buf, SEND_BUF_SIZE, 0);
		if (recv_result > 0) {
			cout << "feedback from server: " << rec_from_server << endl;
			//cout << "feedback from server: " << send_buf << endl;
		}
		else if (recv_result == 0) {
			cout << "connection closed..." << endl;
		}
		else {
			cerr << "recv() function failed with error: " << WSAGetLastError() << "\n";
			closesocket(sock_client);
			WSACleanup();
			system("pause");
			return 1;
		}
	} while (recv_result > 0);
           

7、關閉socket

i_result = shutdown(sock_client, SD_SEND);
	if (i_result == SOCKET_ERROR) {
		cerr << "shutdown() function failed with error: " << WSAGetLastError() << "\n";
		closesocket(sock_client);
		WSACleanup();
		system("pause");
		return 1;
	}
	closesocket(sock_client);
	WSACleanup();
	cout << "socket closed..." << endl;
           

服務端代碼:

1、socketserver.h

#pragma once
#ifndef SOCKETSERVER_H
#define SOCKETSERVER_H

#include <Winsock2.h>
#include <windows.h>

#pragma comment (lib, "ws2_32.lib")

#define IP_BUF_SIZE 129

class Server
{
public:
	Server();
	~Server();
	Server(const Server &) = delete;
	Server & operator=(const Server &) = delete;
	void WaitForClient();
private:
	WORD winsock_ver;  //socket版本
	WSADATA wsa_data;  //存儲被WSAStartup函數調用後傳回的Windows Sockets資料
	SOCKET sock_svr;   //服務端socket
	SOCKET sock_clt;   //用戶端socket
	HANDLE h_thread;   //建立的線程
	SOCKADDR_IN addr_svr;  //存放了服務端位址族、端口、ip位址,用來處理通信位址
	SOCKADDR_IN addr_clt;  //存放了用戶端位址族、端口、ip位址,用來處理通信位址
	int ret_val;
	int addr_len;
	char buf_ip[IP_BUF_SIZE];
};

#endif


           

2、socketserver.cpp

#include "pch.h"
#include "socketserver.h"
#include <iostream>
#include <WS2tcpip.h>


using namespace std;

#define SERVER_PORT 5000     //端口
#define MSG_BUF_SIZE 1024    //接送資訊最大長度

const int SEND_BUF_SIZE = 1024;  //發送資訊最大長度

Server::Server()
{
	cout << "Initializing server...\n";
	//
	winsock_ver = MAKEWORD(2, 2);    //指定socket版本2.2
	addr_len = sizeof(SOCKADDR_IN);
	addr_svr.sin_family = AF_INET; //ip位址族
	addr_svr.sin_port = ::htons(SERVER_PORT);  //轉換網絡編碼,偵聽本地5000端口
	addr_svr.sin_addr.S_un.S_addr = ADDR_ANY;  //接收任意位址的連接配接

	//memset某一塊記憶體中的内容全部設定為指定的值, 
	//這個函數通常為新申請的記憶體做初始化工作。
	memset(buf_ip, 0, IP_BUF_SIZE);//将buf_ip全部指定為0

	//WSAStartup,即WSA(Windows Sockets Asynchronous,Windows異步套接字)的啟動指令。0表示成功
	ret_val = ::WSAStartup(winsock_ver, &wsa_data);
	if (ret_val != 0)
	{
		cerr << "WSA failed to start up!Error code: " << ::WSAGetLastError() << "\n";
		system("pause");
		exit(1);
	}
	cout << "WSA started up successfully...\n";
	//建立socket  socket如果調用成功就傳回新建立的套接字的描述符
	sock_svr = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (sock_svr == INVALID_SOCKET)
	{
		cerr << "Failed to create server socket!Error code: " << ::WSAGetLastError() << "\n";
		::WSACleanup();
		system("pause");
		exit(1);
	}
	cout << "Server socket created successfully...\n";

	//bind  綁定要通信的程式,成為伺服器,如果函數執行成功,傳回值為0,否則為SOCKET_ERROR。
	//sock_svr就是第二步成功買回的,不,是申請到的接口
	//(SOCKADDR*)&addr_svr 是指程序具體位址,就像具體電話号碼一樣,他的類型是一個結構體
	//addr_len就是位置的長度
	ret_val = ::bind(sock_svr, (SOCKADDR*)&addr_svr, addr_len);
	if (ret_val != 0)
	{
		cerr << "Failed to bind server socket!Error code: " << ::WSAGetLastError() << "\n";
		::WSACleanup();
		system("pause");
		exit(1);
	}
	cout << "Server socket bound successfully...\n";
	//監聽端口
	ret_val = ::listen(sock_svr, SOMAXCONN);
	if (ret_val == SOCKET_ERROR)
	{
		cerr << "Server socket failed to listen!Error code: " << ::WSAGetLastError() << "\n";
		::WSACleanup();
		system("pause");
		exit(1);
	}
	cout << "Server socket started to listen...\n";
	//
	cout << "Server started successfully..." << endl;
	cout << "ret_val..." << ret_val << endl;
}

Server::~Server()
{
	::closesocket(sock_svr);
	::closesocket(sock_clt);
	::WSACleanup();
	cout << "Socket closed..." << endl;
}

DWORD WINAPI CreateClientThread(LPVOID lpParameter);

//等待用戶端
void Server::WaitForClient()
{
	while (true)
	{
		//接受用戶端的資訊
		sock_clt = ::accept(sock_svr, (SOCKADDR*)&addr_clt, &addr_len);
		cout << "ssss " << sock_clt << endl;
		if (sock_clt == INVALID_SOCKET)
		{
			cout << "ssss " << sock_clt <<endl;
			cerr << "Failed to accept client!Error code: " << ::WSAGetLastError() << "\n";
			::WSACleanup();
			system("pause");
			exit(1);
		}
		//::InetNtop(addr_clt.sin_family, &addr_clt, buf_ip, IP_BUF_SIZE);
		//cout << "A new client connected...IP address: " << buf_ip << ", port number: " << ::ntohs(addr_clt.sin_port) << endl;


		//:CreateThread:新的線程的函數,該函數在主線程的基礎上建立一個新線程。線程終止運作後,
		//線程對象仍然在系統中,必須通過CloseHandle函數來關閉該線程對象
		h_thread = ::CreateThread(nullptr, 0, CreateClientThread, (LPVOID)sock_clt, 0, nullptr);
		if (h_thread == NULL)
		{
			cerr << "Failed to create a new thread!Error code: " << ::WSAGetLastError() << "\n";
			::WSACleanup();  //終止Winsock 2 DLL (Ws2_32.dll) 的使用
			system("pause");
			exit(1);
		}
		//關閉線程關閉一個核心對象。其中包括檔案、檔案映射、程序、線程、安全和同步對象等。
		//在CreateThread成功之後會傳回一個hThread的handle,且核心對象的計數加1,CloseHandle之後,
		//引用計數減1,當變為0時,系統删除核心對象。
		::CloseHandle(h_thread);
	}
}
//建立用戶端線程
DWORD WINAPI CreateClientThread(LPVOID lpParameter)
{
	SOCKET sock_clt = (SOCKET)lpParameter;  //用戶端
	char buf_msg[MSG_BUF_SIZE];  //  接收資訊
	int ret_val = 0;   //傳回讀入位元組數
	int snd_result = 0;  //傳回發送資訊位元組數

	do
	{
		memset(buf_msg, 0, MSG_BUF_SIZE);
		//接收資料  若無錯誤,傳回讀入位元組數
		ret_val = ::recv(sock_clt, buf_msg, MSG_BUF_SIZE, 0);
		if (ret_val > 0)
		{
			//如果接送的資訊為“exit”,則關閉連接配接
			if (strcmp(buf_msg, "exit") == 0)
			{
				cout << "Client requests to close the connection..." << endl;
				break;
			}
			//列印接收資訊
			cout << "Message received: " << buf_msg << endl;

			//發送消息給用戶端
			char send_to_client[SEND_BUF_SIZE];  //發送給用戶端的資訊
			cout << "enter the message that you want to send client: " << flush;
			cin.getline(send_to_client, SEND_BUF_SIZE);//控制台輸入需要發送資訊
			cout << "enter the message " << send_to_client << endl;
			snd_result = ::send(sock_clt, send_to_client, static_cast<int>(strlen(send_to_client)), 0);

			//snd_result = ::send(sock_clt, buf_msg, MSG_BUF_SIZE, 0);
			//如果發送失敗,即snd_result=-1
			if (snd_result == SOCKET_ERROR)
			{
				cerr << "Failed to send message to client!Error code: " << ::GetLastError() << "\n";
				::closesocket(sock_clt);
				system("pause");
				return 1;
			}
		}//接受資訊失敗ret_val==0
		else if (ret_val == 0)
		{
			cout << "connection closed..." << endl;
		}
		else
		{
			cerr << "Failed to receive message from client!Error code: " << ::GetLastError() << "\n";
			::closesocket(sock_clt);
			system("pause");
			return 1;
		}
	} while (ret_val > 0);
	//
	ret_val = ::shutdown(sock_clt, SD_SEND);
	if (ret_val == SOCKET_ERROR)
	{
		cerr << "Failed to shutdown the client socket!Error code: " << ::GetLastError() << "\n";
		::closesocket(sock_clt);
		system("pause");
		return 1;
	}
	return 0;
}
           

3、啟動服務端代碼

#include "pch.h"
#include "socketserver.h"
int main()
{
	Server svr;
	svr.WaitForClient();
	system("pause");
	return 0;
}
           

4、用戶端代碼

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

#pragma comment(lib, "ws2_32.lib")

using std::cin;
using std::cerr;
using std::cout;
using std::endl;
using std::flush;

const char DEFAULT_PORT[] = "5000";
const int SEND_BUF_SIZE = 1024;

//用戶端
int main() {
	WSADATA wsa_data; //WSADATA變量,包含windows socket執行的資訊
	int i_result = 0; //接收傳回值
	SOCKET sock_client = INVALID_SOCKET;
	addrinfo *result = nullptr, hints;
	//初始化winsock動态庫(ws2_32.dll),MAKEWORD(2, 2)用于請求使用winsock2.2版本
	i_result = WSAStartup(MAKEWORD(2, 2), &wsa_data);
	if (i_result != 0) {
		cerr << "WSAStartup() function failed: " << i_result << "\n";
		system("pause");
		return 1;
	}
	SecureZeroMemory(&hints, sizeof(hints));//其作用是用0來填充一塊記憶體區域
	hints.ai_family = AF_UNSPEC;
	hints.ai_socktype = SOCK_STREAM;
	hints.ai_protocol = IPPROTO_TCP;
	//getaddrinfo函數能夠處理名字到位址以及服務到端口這兩種轉換,
	//傳回的是一個sockaddr結構的連結清單而不是一個位址清單。
	//這些sockaddr結構随後可由套接口函數直接使用
	i_result = getaddrinfo("127.0.0.1", DEFAULT_PORT, &hints, &result);
	if (i_result != 0) {
		cerr << "getaddrinfo() function failed with error: " << WSAGetLastError() << "\n";
		WSACleanup();
		system("pause");
		return 1;
	}
	//建立套接字
	sock_client = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
	if (sock_client == INVALID_SOCKET) {
		cerr << "socket() function failed with error: " << WSAGetLastError() << "\n";
		WSACleanup();
		system("pause");
		return 1;
	}
	//連接配接伺服器
	i_result = connect(sock_client, result->ai_addr, result->ai_addrlen);
	if (i_result == SOCKET_ERROR) {
		cerr << "connect() function failed with error: " << WSAGetLastError() << "\n";
		WSACleanup();
		system("pause");
		return 1;
	}
	cout << "connect server successfully..." << endl;
	//釋放由getaddrinfo傳回的存儲空間,包括addrinfo結構、ai_addr結構和ai_canonname字元串
	freeaddrinfo(result);

	//發送資訊給服務端
	char send_buf[SEND_BUF_SIZE];
	int recv_result = 0; //接收伺服器傳回的資料的傳回值
	//SecureZeroMemory(send_buf, SEND_BUF_SIZE);
	do {
		cout << "enter the message that you want to send: " << flush;
		cin.getline(send_buf, SEND_BUF_SIZE);
		i_result = send(sock_client, send_buf, static_cast<int>(strlen(send_buf)), 0);
		if (i_result == SOCKET_ERROR) {
			cerr << "send() function failed with error: " << WSAGetLastError() << "\n";
			closesocket(sock_client);
			WSACleanup();
			system("pause");
			return 1;
		}
		cout << "Bytes sent: " << i_result << endl;
		
		//接收伺服器傳回的資料
		char rec_from_server[SEND_BUF_SIZE];
		recv_result = recv(sock_client, rec_from_server, SEND_BUF_SIZE, 0);
		//recv_result = recv(sock_client, send_buf, SEND_BUF_SIZE, 0);
		if (recv_result > 0) {
			cout << "feedback from server: " << rec_from_server << endl;
			//cout << "feedback from server: " << send_buf << endl;
		}
		else if (recv_result == 0) {
			cout << "connection closed..." << endl;
		}
		else {
			cerr << "recv() function failed with error: " << WSAGetLastError() << "\n";
			closesocket(sock_client);
			WSACleanup();
			system("pause");
			return 1;
		}
	} while (recv_result > 0);
	//
	i_result = shutdown(sock_client, SD_SEND);
	if (i_result == SOCKET_ERROR) {
		cerr << "shutdown() function failed with error: " << WSAGetLastError() << "\n";
		closesocket(sock_client);
		WSACleanup();
		system("pause");
		return 1;
	}
	closesocket(sock_client);
	WSACleanup();
	cout << "socket closed..." << endl;
	system("pause");
	return 0;
}
           

繼續閱讀