C++实现Windows下服务端与客户端Socket通信(一)

个人简介

  • 作者简介:大家好,我是abcdefzzzz,2021年7月正式加入新生代农民工大军。
  • 个人主页:abcdefzzzz
  • 支持我:码字不易 欢迎关注点赞收藏⭐️留言
  • 格言:日有所学,不见其增,必有所长!

源码链接:基于MFC可视化界面的Socket双向通信+protobuf传输源码-C++文档类资源-CSDN下载

一、开发配置介绍

1.开发软件:VS2017

2.开发语言:C++

3.操作系统:Windows10

3.使用技术:Socket、MFC

二、演示效果

(一)Windows服务端:

C++实现Windows下服务端与客户端Socket通信(一)_第1张图片

(二)Windows客户端:

C++实现Windows下服务端与客户端Socket通信(一)_第2张图片C++实现Windows下服务端与客户端Socket通信(一)_第3张图片

基于MFC的客户端效果

C++实现Windows下服务端与客户端Socket通信(一)_第4张图片

控制台客户端效果

①客户端与服务端建立连接

②客户端向服务端发送注册、登录的数据信息。

③服务端验证客户端的信息,向客户端发送是否验证成功的消息。

三、C++程序实现

(一)Socket传输字符串数据

I.服务端:

1.创建控制台应用程序:ServerSocket_CSDN

C++实现Windows下服务端与客户端Socket通信(一)_第5张图片

2.服务端程序代码&注释

#define _WINSOCK_DEPRECATED_NO_WARNINGS			//防止报错
#include 
#include 
using namespace std;

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

int main()
{
	/*初始化启动信息*/
	WORD sockVersion = MAKEWORD(2, 2);//调用2.2版本的socket
	WSADATA wsaData;				  //WSA(Windows Sockets Asynchronous)异步套接字

	//将指定版本的socket与该应用程序绑定
	if (WSAStartup(sockVersion, &wsaData) != 0)	//返回为0则表示初始化成功
		return 0;

	/*创建服务器Socket*/
	//AF_INET,使用IPV4通信;SOCK_STREAM,使用流式套接字;IPPROTO_TCP,采用TCP协议

	SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (serverSocket == INVALID_SOCKET)			//如果创建失败,则输出错误
	{
		cout << "socket error:" << WSAGetLastError() << endl;
		WSACleanup();			//中止Windows Sockets DLL的使用;与上面WSAStartup()配套使用
		return 0;
	}
	//创建服务器Socket套接字成功后

	/*sockaddr_in是一个结构体,需要指定 协议族 + IP地址 + 端口号*/
	sockaddr_in serverAddr;
	serverAddr.sin_family = AF_INET;				//指定协议族,AF_INET<->IPV4;AF_INET6<->IPV6
	serverAddr.sin_addr.S_un.S_addr = INADDR_ANY;	//指定IP地址
	serverAddr.sin_port = htons(8888);				//指定端口号

	//Socket绑定地址,第二个参数注意需要使用强制转换
	if (bind(serverSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
	{
		cout << "Bind Error!" << endl;
		closesocket(serverSocket);
		WSACleanup();
		return 0;
	}

	//建立监听客户端请求的信号,设置最多有5个客户端
	if (listen(serverSocket, 5) == SOCKET_ERROR)//如果建立失败
	{
		cout << "Listen Error !" << endl;
		closesocket(serverSocket);				//关闭Socket套接字
		WSACleanup();
		return 0;
	}
	cout << "正在监听..." << endl;

	//开始不断处理各个客户端请求
	while (true)
	{
		SOCKET clientSocket = INVALID_SOCKET;	//初始化一个客户端socket
		sockaddr_in clientAddr;					//客户端的地址结构
		int iAddrLength = sizeof(clientAddr);	//求出该结构的长度
		cout << "等待登录..." << endl;
		//接收客户端的连接请求。第一个参数为服务端Socket,第二个参数用来接收从客户端传来的地址,第三个参数为地址结构的长度
		clientSocket = accept(serverSocket, (SOCKADDR*)&clientAddr, &iAddrLength);//accept为阻塞函数
		
		if (clientSocket == INVALID_SOCKET)		//如果接收失败
		{
			cout << "Accept Error !" << WSAGetLastError() << endl;
			closesocket(serverSocket);
			WSACleanup();
			return 0;
		}
		//inet_ntoa将网络地址转换为字符输出
		cout << "客户端地址:" << inet_ntoa(clientAddr.sin_addr) << endl;

		//开始不断接收该客户端数据
		char buffFromClient[1024];			//用于接收客户端传来的数据
		while(true)
		{
			memset(buffFromClient, 0, sizeof(buffFromClient));
			//recv也为阻塞函数,只有客户端发送数据过来后,程序才会往下继续走
			int iLenOfRecvData = recv(clientSocket, buffFromClient, sizeof(buffFromClient), 0);
			
			if (iLenOfRecvData > 0)		//如果接收的数据不为空
			{
				cout << buffFromClient << endl;
			}
			else
			{
				cout << "服务器断开,无接收..." << endl;
				break;
			}
			char sendToClientBuff[1024] = "服务端收到啦!";
			send(clientSocket, sendToClientBuff, sizeof(sendToClientBuff), 0);
		}
		closesocket(clientSocket);//关闭与该客户端的套接字
	}
	closesocket(serverSocket);//关闭服务器套接字
	WSACleanup();
}

II.客户端:

1.新建MFC对话框程序:ClientSocket_CSDN

C++实现Windows下服务端与客户端Socket通信(一)_第6张图片

C++实现Windows下服务端与客户端Socket通信(一)_第7张图片

2.UI设计,添加Button控件

C++实现Windows下服务端与客户端Socket通信(一)_第8张图片

C++实现Windows下服务端与客户端Socket通信(一)_第9张图片

C++实现Windows下服务端与客户端Socket通信(一)_第10张图片

3.实现点击Button控件向服务端发送消息的函数

        ①在.h文件中添加需要的头文件、库,定义一个全局变量:m_clientSocket。

C++实现Windows下服务端与客户端Socket通信(一)_第11张图片

C++实现Windows下服务端与客户端Socket通信(一)_第12张图片

        ②修改项目属性,将SDL检查设置为:否,用于防止报错。

C++实现Windows下服务端与客户端Socket通信(一)_第13张图片

        ③编写OnBnClickedButton1()函数

void CClientSocketCSDNDlg::OnBnClickedButton1()
{
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsaData;//WSA(Windows Sockets Asynchronous)异步套接字
	if (WSAStartup(sockVersion, &wsaData) != 0)
		return;

	m_clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (m_clientSocket == INVALID_SOCKET)
	{
		MessageBox("Invalid Socket!", "错误", MB_ICONERROR);
		return;
	}
	//建立一个客户端地址
	sockaddr_in serAddr;
	serAddr.sin_family = AF_INET;
	serAddr.sin_port = htons(8888);
	serAddr.sin_addr.S_un.S_addr = inet_addr("127.1.0.1");
	//客户端向服务端请求连接
	if (connect(m_clientSocket, (sockaddr*)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
	{
		MessageBox("Connect Error!!", "错误", MB_ICONERROR);
		closesocket(m_clientSocket);
		return;
	}
	//与服务端连接成功,开发发送消息
	char sendToServerBuff[1024] = "服务端,我来啦~";
	send(m_clientSocket, *(&sendToServerBuff), sizeof(sendToServerBuff), 0);

	//接收服务端返回的消息
	char buffFromServer[1024];
	int recvDataLen = recv(m_clientSocket, buffFromServer, sizeof(buffFromServer), 0);
	if (recvDataLen > 0)
	{
		MessageBox(buffFromServer);
	}

	closesocket(m_clientSocket);
	WSACleanup();
}

(二)Socket传输结构体数据

见我另一篇博客:(详细源码)C++ socket 传输不同类型数据的四种方式_abcdefzzzz的博客-CSDN博客

(三)Socket传输类对象数据

I.服务端

1.添加StudentInfo类

C++实现Windows下服务端与客户端Socket通信(一)_第14张图片

2.服务端目录结构如下:

C++实现Windows下服务端与客户端Socket通信(一)_第15张图片

 3.类对象的.h程序代码

//StudentInfo.h文件如下,.cpp文件自行实现

#pragma once
#include 
using namespace std;
class StudentInfo
{
private:
	int m_iId;
	string m_strName;
	bool m_bSex;

public:
	StudentInfo();
	~StudentInfo();

	int GetId();
	string GetName();
	bool GetSex();

	void SetId(int iId);
	void SetName(string strName);
	void SetSex(bool bSex);
};

4.实现传输类对象的程序代码

#define _WINSOCK_DEPRECATED_NO_WARNINGS			//防止报错
#include 
#include 
#include 
#include "StudentInfo.h"
using namespace std;

#pragma comment(lib,"ws2_32.lib")
struct Student
{
	int iId;
	string strName;
	bool bSex;
};
int main()
{
	/*初始化启动信息*/
	WORD sockVersion = MAKEWORD(2, 2);//调用2.2版本的socket
	WSADATA wsaData;				  //WSA(Windows Sockets Asynchronous)异步套接字

	//将指定版本的socket与该应用程序绑定
	if (WSAStartup(sockVersion, &wsaData) != 0)	//返回为0则表示初始化成功
		return 0;

	/*创建服务器Socket*/
	//AF_INET,使用IPV4通信;SOCK_STREAM,使用流式套接字;IPPROTO_TCP,采用TCP协议

	SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (serverSocket == INVALID_SOCKET)			//如果创建失败,则输出错误
	{
		cout << "socket error:" << WSAGetLastError() << endl;
		WSACleanup();			//中止Windows Sockets DLL的使用;与上面WSAStartup()配套使用
		return 0;
	}
	//创建服务器Socket套接字成功后

	/*sockaddr_in是一个结构体,需要指定 协议族 + IP地址 + 端口号*/
	sockaddr_in serverAddr;
	serverAddr.sin_family = AF_INET;				//指定协议族,AF_INET<->IPV4;AF_INET6<->IPV6
	serverAddr.sin_addr.S_un.S_addr = INADDR_ANY;	//指定IP地址
	serverAddr.sin_port = htons(8888);				//指定端口号

	//Socket绑定地址,第二个参数注意需要使用强制转换
	if (bind(serverSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
	{
		cout << "Bind Error!" << endl;
		closesocket(serverSocket);
		WSACleanup();
		return 0;
	}

	//建立监听客户端请求的信号,设置最多有5个客户端
	if (listen(serverSocket, 5) == SOCKET_ERROR)//如果建立失败
	{
		cout << "Listen Error !" << endl;
		closesocket(serverSocket);				//关闭Socket套接字
		WSACleanup();
		return 0;
	}
	cout << "正在监听..." << endl;

	//开始不断处理各个客户端请求
	StudentInfo stuInfo;
	while (true)
	{
		SOCKET clientSocket = INVALID_SOCKET;	//初始化一个客户端socket
		sockaddr_in clientAddr;					//客户端的地址结构
		int iAddrLength = sizeof(clientAddr);	//求出该结构的长度
		cout << "等待登录..." << endl;
		//接收客户端的连接请求。第一个参数为服务端Socket,第二个参数用来接收从客户端传来的地址,第三个参数为地址结构的长度
		clientSocket = accept(serverSocket, (SOCKADDR*)&clientAddr, &iAddrLength);//accept为阻塞函数
		
		if (clientSocket == INVALID_SOCKET)		//如果接收失败
		{
			cout << "Accept Error !" << WSAGetLastError() << endl;
			closesocket(serverSocket);
			WSACleanup();
			return 0;
		}
		//inet_ntoa将网络地址转换为字符输出
		cout << "客户端地址:" << inet_ntoa(clientAddr.sin_addr) << endl;

		//开始不断接收该客户端数据
		char buffFromClient[1024];			//用于接收客户端传来的数据
		while(true)
		{
			memset(buffFromClient, 0, sizeof(buffFromClient));
			//recv也为阻塞函数,只有客户端发送数据过来后,程序才会往下继续走
			int iLenOfRecvData = -1;
			//传输类对象数据
			iLenOfRecvData = recv(clientSocket, (char*)&stuInfo, sizeof(StudentInfo), 0);
			if (iLenOfRecvData > 0)		//如果接收的数据不为空
			{
				cout << stuInfo.GetId() << endl;
				cout << stuInfo.GetName() << endl;
				cout << stuInfo.GetSex() << endl;
			}
			else
			{
				cout << "服务器断开,无接收..." << endl;
				break;
			}
			char sendToClientBuff[1024] = "服务端收到啦!";
			send(clientSocket, sendToClientBuff, sizeof(sendToClientBuff), 0);
		}
		closesocket(clientSocket);//关闭与该客户端的套接字
	}
	closesocket(serverSocket);//关闭服务器套接字
	WSACleanup();
}

II.客户端

1.添加StudentInfo类(同服务端)

2.客户端目录结构如下:

C++实现Windows下服务端与客户端Socket通信(一)_第16张图片

 3.类对象的程序代码同服务端。

 4.编写OnBnClickedButton1函数,实现传输类对象的程序代码


void CClientSocketCSDNDlg::OnBnClickedButton1()
{
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA wsaData;//WSA(Windows Sockets Asynchronous)异步套接字
	if (WSAStartup(sockVersion, &wsaData) != 0)
		return;

	m_clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	if (m_clientSocket == INVALID_SOCKET)
	{
		MessageBox("Invalid Socket!", "错误", MB_ICONERROR);
		return;
	}
	//建立一个客户端地址
	sockaddr_in serAddr;
	serAddr.sin_family = AF_INET;
	serAddr.sin_port = htons(8888);
	serAddr.sin_addr.S_un.S_addr = inet_addr("127.1.0.1");
	//客户端向服务端请求连接
	if (connect(m_clientSocket, (sockaddr*)&serAddr, sizeof(serAddr)) == SOCKET_ERROR)
	{
		MessageBox("Connect Error!!", "错误", MB_ICONERROR);
		closesocket(m_clientSocket);
		return;
	}
	//与服务端连接成功,开始发送消息
	//char sendToServerBuff[1024] = "服务端,我来啦~";
	char sendToServerBuff[1024];
	//初始化stuInfo类对象
	StudentInfo stuInfo;
	stuInfo.SetId(111);
	stuInfo.SetName("abcdefzzzzz");
	stuInfo.SetSex(true);
	send(m_clientSocket, (char*)&stuInfo, sizeof(StudentInfo), 0);
	//send(m_clientSocket, *(&sendToServerBuff), sizeof(sendToServerBuff), 0);
	

	//接收服务端返回的消息
	char buffFromServer[1024];
	int recvDataLen = recv(m_clientSocket, buffFromServer, sizeof(buffFromServer), 0);
	if (recvDataLen > 0)
	{
		MessageBox(buffFromServer);
	}

	closesocket(m_clientSocket);
	WSACleanup();
}

 以上于2022.4.6更新。

(四)Socket+Protobuf数据序列化

(未完待续...)

有任何问题和改进建议可私信或评论留言!

你可能感兴趣的:(网络编程,websocket,c++,mfc,visualstudio)