C++多线程实现TCP服务器端同时和多个客户端通信

 更新时间:2021年05月17日 09:11:50   作者:新西兰做的饭  
通讯建立后首先由服务器端发送消息,客户端接收消息;接着客户端发送消息,服务器端接收消息,实现交互发送消息。本文主要介绍了C++多线程实现TCP服务器端同时和多个客户端通信,感兴趣的可以了解一下

通讯建立后首先由服务器端发送消息,客户端接收消息;接着客户端发送消息,服务器端接收消息,实现交互发送消息。

服务器同时可以和多个客户端建立连接,进行交互;

在某次交互中,服务器端或某客户端有一方发送"end"即终止服务器与其的通信;服务器还可以继续接收其他客户端的请求,与其他客户端通信。

服务器端

#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iostream>
using namespace std;
#pragma comment(lib, "ws2_32.lib")
#define PORT 65432
DWORD WINAPI ThreadFun(LPVOID lpThreadParameter);
int main() 
{
	//初始化winsock2.DLL
	WSADATA wsaData;
	WORD wVersionRequested = MAKEWORD(2, 2);
	if (WSAStartup(wVersionRequested, &wsaData) != 0)
	{
		cout << "加载winsock.dll失败!" << endl;
		return 0;
	}
	//创建套接字
	SOCKET  sock_server;
	if ((sock_server = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR)
	{
		cout << "创建套接字失败!错误代码:" << WSAGetLastError() << endl;
		WSACleanup();
		return 0;
	}
	//绑定端口和Ip
	sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(PORT);
	inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);//绑定本机的环回地址
	if (SOCKET_ERROR == bind(sock_server, (SOCKADDR*)&addr, sizeof(sockaddr_in)))
	{
		cout << "地址绑定失败!错误代码:" << WSAGetLastError() << endl;
		closesocket(sock_server);
		WSACleanup();
		return 0;
	}
	//将套接字设为监听状态
	listen(sock_server, 0);
	
	//主线程循环接收客户端的连接
	while (1) 
	{
		sockaddr_in addrClient;
		int len = sizeof(sockaddr_in);
		//接收成功返回与client通讯的socket
		SOCKET con = accept(sock_server, (SOCKADDR*)&addrClient, &len);
		if (con != INVALID_SOCKET) 
		{
			//创建线程 并且传入与client通讯的套接字
			HANDLE hThread = CreateThread(NULL, 0, ThreadFun, (LPVOID)con, 0, NULL);
			CloseHandle(hThread); //关闭对线程的引用
		}
	}
	closesocket(sock_server);
	WSACleanup();
	return 0;
}
//线程通讯部分
DWORD WINAPI ThreadFun(LPVOID lpThreadParameter) 
{
	//与客户端通讯 先发送再接收数据
	SOCKET sock = (SOCKET)lpThreadParameter;
	cout << "成功和" << sock << "建立连接!" << endl;
	while (1)
	{
		char msgbuffer[1000];//字符缓冲区
		printf("服务器向%d发送数据:\n", sock);
		cin.getline(msgbuffer, sizeof(msgbuffer));
		int size = send(sock, msgbuffer, sizeof(msgbuffer), 0);//给客户端发送一段信息
		if (strcmp(msgbuffer, "end\0") == 0)
		{
			cout << "关闭和" << sock << "的连接!" << endl;
			return 0;
		}
		if (size == SOCKET_ERROR || size == 0)
		{
			cout << "发送信息失败!错误代码:" << WSAGetLastError() << endl;
			return 0;
		}
		else cout << "信息发送成功!" << endl;
		
		//接收客户端数据
		msgbuffer[999] = { 0 };
		int ret = recv(sock, msgbuffer, sizeof(msgbuffer), 0);
		if(ret == SOCKET_ERROR || ret == 0)
		{
			cout << sock << "断开了连接!" << endl;
			break;
		}
		else cout << sock << "  说: " << msgbuffer << endl;
	}
	return 0;
}

客户端

#include <winsock2.h>
#include <WS2tcpip.h>
#include <iostream>
using  namespace std;
#pragma comment(lib, "ws2_32.lib")
#define PORT 65432
int  main() 
{
	//初始化winsock2.DLL
	WSADATA wsaData;
	WORD wVersionRequested = MAKEWORD(2, 2);
	if (WSAStartup(wVersionRequested, &wsaData) != 0)
	{
		cout << "加载winsock.dll失败!" << endl;
		return 0;
	}
	//创建套接字
	SOCKET  sock_client; 
	if ((sock_client = socket(AF_INET, SOCK_STREAM, 0)) == SOCKET_ERROR)
	{
		cout << "创建套接字失败!错误代码:" << WSAGetLastError() << endl;
		WSACleanup();
		return 0;
	}
	//连接服务器
	sockaddr_in   addr;
	addr.sin_family = AF_INET;
	addr.sin_port = htons(PORT);
	inet_pton(AF_INET, "127.0.0.1", &addr.sin_addr);//绑定本机的环回地址
	int len = sizeof(sockaddr_in);
	if (connect(sock_client, (SOCKADDR*)&addr, len) == SOCKET_ERROR) {
		cout << "连接失败!错误代码:" << WSAGetLastError() << endl;
		return 0;
	}
	//实现交互部分,客户端先接收后发送数据
	while (1)
	{
		//接收服务端的消息
		char msgbuffer[1000] = { 0 };
		int size = recv(sock_client, msgbuffer, sizeof(msgbuffer), 0);
		if (strcmp(msgbuffer, "end\0") == 0)
		{
			cout << "服务器端已经关闭连接!" << endl;
			break;
		}
		if (size < 0)
		{
			cout << "接收信息失败!错误代码:" << WSAGetLastError() << endl;
			break;
		}
		else if (size == 0)
		{
			cout << "对方已经关闭连接" << endl;
			break;
		}
		else cout << "The message from Server:" << msgbuffer << endl;

		//从键盘输入一行文字发送给服务器
		msgbuffer[999] =  0 ;
		cout << "从键盘输入发给服务器的信息:" << endl;
		cin.getline(msgbuffer, sizeof(msgbuffer));
		if (strcmp(msgbuffer, "end\0") == 0)
		{
			cout << "关闭连接!" << endl;
			break;
		}
		int ret = send(sock_client, msgbuffer, sizeof(msgbuffer), 0);
		if (ret == SOCKET_ERROR || ret == 0)
		{
			cout << "发送信息失败!错误代码:" << WSAGetLastError() << endl;
			break;
		}
		else cout << "信息发送成功!" << endl;
	}
	closesocket(sock_client);
	WSACleanup();
	return 0;
}

我们用建立连接时服务器端接收的客户端套接字来唯一标识该客户端。
服务器端可以随时接收客户端的连接并与其进行交互。

运行实例

在这里插入图片描述

实例展示了服务器端和两个客户端通信的运行过程,包括正常交互、交互过程中另一服务器请求建立连接、服务器主动断开连接和客户端主动断开连接等过程。

到此这篇关于C++多线程实现TCP服务器端同时和多个客户端通信 的文章就介绍到这了,更多相关C++ 服务器端和多个客户端通信 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • OpenCV实现二值图像的边缘光滑处理

    OpenCV实现二值图像的边缘光滑处理

    这篇文章主要为大家详细介绍了OpenCV实现二值图像的边缘光滑处理,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-07-07
  • C语言实现三子棋(井字棋)算法

    C语言实现三子棋(井字棋)算法

    这篇文章主要为大家详细介绍了C语言实现三子棋(井字棋)算法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-07-07
  • STL容器之vector源码详细解读

    STL容器之vector源码详细解读

    这篇文章主要介绍了STL容器之vector源码详细解读,vector的数据安排和array和类似,它们的主要差别在于空间的运用和灵活性,array是静态空间,一旦配置了就不能改变,需要的朋友可以参考下
    2024-01-01
  • 链接库动态链接库详细介绍

    链接库动态链接库详细介绍

    静态链接库.lib和动态链接库.dll。其中动态链接库在被使用的时候,通常还提供一个.lib,称为引入库,它主要提供被Dll导出的函数和符号名称,使得链接的时候能够找到dll中对应的函数映射
    2012-11-11
  • C语言报错Use of Uninitialized Variable的原因及解决方案

    C语言报错Use of Uninitialized Variable的原因及解决方案

    Use of Uninitialized Variable是C语言中常见且危险的错误之一,它通常在程序试图使用一个未初始化的变量时发生,本文将详细介绍Use of Uninitialized Variable的产生原因,提供多种解决方案,并通过实例代码演示如何有效避免和解决此类错误,需要的朋友可以参考下
    2024-06-06
  • C语言实现动态版通讯录的代码分享

    C语言实现动态版通讯录的代码分享

    这篇文章主要为大家详细介绍了如何利用C语言实现一个简单的动态版通讯录,主要运用了结构体,一维数组,函数,分支与循环语句等等知识,需要的可以参考一下
    2023-01-01
  • QT+ffmpeg实现视频解析的示例详解

    QT+ffmpeg实现视频解析的示例详解

    这篇文章主要为大家详细介绍了如何利用QT+ffmpeg实现视频解析功能,文中的示例代码讲解详细,对我们学习Qt有一定帮助,需要的可以参考一下
    2022-09-09
  • C语言实现杨辉三角实例

    C语言实现杨辉三角实例

    这篇文章主要介绍了C语言实现杨辉三角的方法,主要通过数组简单实现,具有一定的参考借鉴价值,需要的朋友可以参考下
    2014-09-09
  • cocos2d-x学习笔记之CCLayer、CCLayerColor、CCLayerGradient、CCLayerMultiplex场景层介绍

    cocos2d-x学习笔记之CCLayer、CCLayerColor、CCLayerGradient、CCLayerMu

    这篇文章主要介绍了cocos2d-x学习笔记之CCLayer、CCLayerColor、CCLayerGradient、CCLayerMultiplex场景层介绍,需要的朋友可以参考下
    2014-09-09
  • C++ 栈和队列的实现超详细解析

    C++ 栈和队列的实现超详细解析

    栈和队列,严格意义上来说,也属于线性表,因为它们也都用于存储逻辑关系为 "一对一" 的数据,但由于它们比较特殊,因此将其单独作为一章,做重点讲解
    2022-03-03

最新评论