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

📋 个人简介

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

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

一、开发配置介绍

1.开发软件:VS2017

2.开发语言:C++

3.操作系统:Windows10

3.使用技术:Socket、MFC

二、演示效果

(一)Windows服务端:

(二)Windows客户端:

基于MFC的客户端效果

控制台客户端效果

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

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

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

三、C++程序实现

(一)Socket传输字符串数据

I.服务端:

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

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

#define _WINSOCK_DEPRECATED_NO_WARNINGS			//防止报错
#include <iostream>
#include <WinSock2.h>
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

2.UI设计,添加Button控件

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

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

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

        ③编写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类

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

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

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

#pragma once
#include <iostream>
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 <iostream>
#include <string>
#include <WinSock2.h>
#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.客户端目录结构如下:

 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数据序列化

(未完待续...)

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

  • 6
    点赞
  • 77
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 好的,下面是关于 C 语言下 Socket 组播服务客户代 码的示例: 服务代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #define PORT 8080 #define MAXLINE 1024 int main() { int sockfd; char buffer[MAXLINE]; char* message = "Hello, Multicast!"; struct sockaddr_in servaddr; // 创建套接字 sockfd = socket(AF_INET, SOCK_DGRAM, 0); memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(PORT); // 绑定套接字 if(bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } // 循环发送数据 while(1) { if(sendto(sockfd, (const char*)message, strlen(message), MSG_CONFIRM, (const struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { perror("sendto failed"); exit(EXIT_FAILURE); } sleep(1); } close(sockfd); return 0; } ``` 客户代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #define PORT 8080 #define MAXLINE 1024 int main() { int sockfd; char buffer[MAXLINE]; struct sockaddr_in servaddr; // 创建套接字 sockfd = socket(AF_INET, SOCK_DGRAM, 0); memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = inet_addr("239.0.0.1"); servaddr.sin_port = htons(PORT); // 加入组播组 struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = inet_addr("239.0.0.1"); mreq.imr_interface.s_addr = htonl(INADDR_ANY); if(setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { perror("setsockopt failed"); exit(EXIT_FAILURE); } // 循环接收数据 while(1) { memset(buffer, 0, MAXLINE); if(recvfrom(sockfd, (char*)buffer, MAXLINE, MSG_WAITALL, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { perror("recvfrom failed"); exit(EXIT_FAILURE); } printf("Message received: %s\n", buffer); } close(sockfd); return 0; } ``` 这段示例代码展示了如何创建基于 UDP 协议的 Socket 组播服务客户,其中服务会不断向组播地址发送消息,而客户会监听这个组播地址并接收到服务发来的消息。 ### 回答2: 下面是一个使用Socket进行组播的服务客户的代码示例: 服务代码: ```python import socket # 创建UDP socket,并绑定口 server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) server_socket.bind(('', 5000)) # 加入组播组 group_ip = '239.0.0.1' group_port = 5555 group = socket.inet_aton(group_ip) + socket.inet_aton('0.0.0.0') server_socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, group) while True: # 接收客户发送的数据 data, address = server_socket.recvfrom(1024) # 打印接收到的数据 print(f"接收到来自 {address} 的消息: {data.decode()}") # 将接收到的数据发送给组播组 server_socket.sendto(data, (group_ip, group_port)) # 关闭socket连接 server_socket.close() ``` 客户代码: ```python import socket # 创建UDP socket,并设置套接字为广播类型 client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) while True: # 获取用户输入消息 message = input("请输入要发送的消息(退出请输入q): ") if message == 'q': break # 将消息发送给组播组 client_socket.sendto(message.encode(), ('<broadcast>', 5000)) # 接收服务发送的数据 data, address = client_socket.recvfrom(1024) # 打印接收到的数据 print(f"接收到来自 {address} 的回复: {data.decode()}") # 关闭socket连接 client_socket.close() ``` 以上代码中,服务通过创建一个UDP socket,并绑定指定口,然后加入指定的组播组。客户通过创建一个UDP socket并设置为广播类型,然后发送消息给组播组。服务接收到客户发送的消息后,将消息发送给组播组,客户收到组播消息后打印出来。通过这种方式,服务客户可以进行组播通信。 ### 回答3: 组播(Multicast)是一种基于UDP的通信协议,它允许一个服务器同时向多个客户发送数据。下面是Socket组播服务客户的示例代码: 服务代码: ```python import socket def multicast_server(): multicast_group = '224.0.0.1' server_address = ('', 10000) # 创建组播套接字 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 设置套接字为可复用 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定地址和口 sock.bind(server_address) # 将套接字加入组播组 group = socket.inet_aton(multicast_group) mreq = struct.pack('4sL', group, socket.INADDR_ANY) sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) while True: data, address = sock.recvfrom(1024) print(f'Received message: {data.decode()} from {address}') # 关闭套接字 sock.close() if __name__ == '__main__': multicast_server() ``` 客户代码: ```python import socket def multicast_client(): multicast_group = '224.0.0.1' server_address = ('', 10000) # 创建组播套接字 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 设置套接字为可复用 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定地址和口 sock.bind(server_address) # 将套接字加入组播组 group = socket.inet_aton(multicast_group) mreq = struct.pack('4sL', group, socket.INADDR_ANY) sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) while True: message = input('Enter message to send: ') # 发送消息到组播组 sock.sendto(message.encode(), (multicast_group, 10000)) # 关闭套接字 sock.close() if __name__ == '__main__': multicast_client() ``` 以上是一个基本的组播示例,服务通过创建组播套接字绑定地址和口,并加入组播组。客户同样创建组播套接字并加入组播组,然后从用户输入获取消息并发送到组播组。通过组播可以实现向多个客户同时发送相同的消息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

abcdefzzzz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值