(C++)TCP、UDP套接字多线程

一、前言

主要功能是实现并发的“客户端–服务器”通信。这里有两种方式,分别是TCP和UDP。这里是一个基本的通信代码,读者可以根据自己的需要稍作修改。

二、主要思路

1、TCP:

监听套接字中的accept函数返回一个连接套接字,就用这个套接字作为参数区分不同的客户端。

2、UDP:

对于服务器,第一个来访的客户端新开一个进程,设置端口为6001,之后的端口依次设置为6002、6003…然后告知每个客户端对应的端口重新绑定端口,之后每个客户端都与对应的端口、对应的进程通信。
即根据端口号区别通信的客户端。
改进:如果不想写死端口号,可以系统分配然后再获取端口号,这里没有实现。

三、源代码

1、TCP并发“服务器-客户端”

这个程序中TCP的交互中对应客户端每个消息都需要回应一句。

(1)TCP服务器端

// stdafx.h : include file for standard system include files,
//  or project specific include files that are used frequently, but
//      are changed infrequently
//

#if !defined(AFX_STDAFX_H__E01B4F25_9E02_42E0_9E68_09D08D1C776F__INCLUDED_)
#define AFX_STDAFX_H__E01B4F25_9E02_42E0_9E68_09D08D1C776F__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers



// TODO: reference additional headers your program requires here

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_STDAFX_H__E01B4F25_9E02_42E0_9E68_09D08D1C776F__INCLUDED_)

#include <stdio.h>
#include <cstdio>
#include<iostream>
#include<string.h>
#include<WinSock2.h>
#include <sys/types.h>

#pragma comment(lib,"ws2_32.lib")
using namespace std;

const int PORT = 8000;
#define MaxClient 10
#define MaxBufSize 1024
#define _CRT_SECURE_NO_WARINGS
//服务线程
DWORD WINAPI ServerThread(LPVOID lpParameter) {
	SOCKET *ClientSocket = (SOCKET*)lpParameter;
	int receByt = 0;
	char RecvBuf[MaxBufSize];
	char SendBuf[MaxBufSize];
	while (1) {
		receByt = recv(*ClientSocket, RecvBuf, sizeof(RecvBuf), 0);
		//buf[receByt]='\0';
		if (receByt > 0) {
			cout << "从" << *ClientSocket << "接收到的消息是:" << RecvBuf << endl;
			// cout<<receByt<<endl;
		}
		else
		{
			cout << "与" << *ClientSocket << "接收消息结束,连接线程关闭!" << endl;
			break;
		}
		memset(RecvBuf, 0, sizeof(RecvBuf));
		cout << "请输入要发送到客户端" << *ClientSocket << "的信息(q退出):" << endl;
		gets_s(SendBuf);
		int k = 0;
		k = send(*ClientSocket, SendBuf, sizeof(SendBuf), 0);
		if (k < 0) {
			cout << "发送到" << *ClientSocket << "消息失败" << endl;
		}
		if (*SendBuf == 'q')
			break;
		memset(SendBuf, 0, sizeof(SendBuf));
	}//while
	cout << "与" << *ClientSocket << "之间连接线程关闭" << endl;
	closesocket(*ClientSocket);
	free(ClientSocket);
	return 0;
}

int main() {
	WSAData wsd;
	WSAStartup(MAKEWORD(2, 2), &wsd);
	SOCKET ListenSocket = socket(AF_INET, SOCK_STREAM, 0);
	SOCKADDR_IN ListenAddr;
	ListenAddr.sin_family = AF_INET;
	ListenAddr.sin_addr.S_un.S_addr = INADDR_ANY;//表示填入本机ip
	ListenAddr.sin_port = htons(PORT);
	int n;
	n = bind(ListenSocket, (LPSOCKADDR)&ListenAddr, sizeof(ListenAddr));
	if (n == SOCKET_ERROR) {
		cout << "listen端口绑定失败!" << endl;
		return -1;
	}
	else {
		cout << "listen端口绑定成功:" << PORT << endl;
	}
	int l = listen(ListenSocket, 20);
	cout << "服务端listening......" << endl;
	int close_l = 0;



	while (1) {
		//循环接收客户端连接请求并创建服务线程
		SOCKET *ClientSocket = new SOCKET;
		ClientSocket = (SOCKET*)malloc(sizeof(SOCKET));

		//接收客户端连接请求
		*ClientSocket = accept(ListenSocket, 0, 0);

		cout << "一个客户端已连接到服务器,socket返回值是:" << *ClientSocket << endl;
		CreateThread(NULL, 0, &ServerThread, ClientSocket, 0, NULL);
	}//while
	closesocket(ListenSocket);
	WSACleanup();
	return(0);
}//main

(2)TCP服务器端
#ifndef _CLIENT_H
#define _CLIENT_H
#include <winsock2.h>


#define TIMEFOR_THREAD_CLIENT		500		//线程睡眠时间

#define	MAX_NUM_CLIENT		10				//接受的客户端连接最多数量
#define	MAX_NUM_BUF			48				//缓冲区的最大长度
#define INVALID_OPERATOR	1				//无效的操作符
#define INVALID_NUM			2				//分母为零
#define ZERO				0				//零

//数据包类型
#define EXPRESSION			'E'				//算数表达式
#define BYEBYE				'B'				//消息byebye
#define HEADERLEN			(sizeof(hdr))	//头长度

//数据包头结构,该结构在win32下为4byte
typedef struct _head
{
	char			type;	//类型		
	unsigned short	len;	//数据包的长度(包括头的长度)
}hdr, *phdr;

//数据包中的数据结构
typedef struct _data
{
	char	buf[MAX_NUM_BUF];//数据
}DATABUF, *pDataBuf;


class CClient
{
public:
	CClient(const SOCKET sClient, const sockaddr_in &addrClient);
	virtual ~CClient();

public:
	BOOL		StartRuning(void);					//创建发送和接收数据线程
	void		HandleData(const char* pExpr);		//计算表达式
	BOOL		IsConning(void) {					//是否连接存在
		return m_bConning;
	}
	void		DisConning(void) {					//断开与客户端的连接
		m_bConning = FALSE;
	}
	BOOL		IsExit(void) {						//接收和发送线程是否已经退出
		return m_bExit;
	}

public:
	static DWORD __stdcall	 RecvDataThread(void* pParam);		//接收客户端数据
	static DWORD __stdcall	 SendDataThread(void* pParam);		//向客户端发送数据

private:
	CClient();
private:
	SOCKET		m_socket;			//套接字
	sockaddr_in	m_addr;				//地址
	DATABUF		m_data;				//数据
	HANDLE		m_hEvent;			//事件对象
	HANDLE		m_hThreadSend;		//发送数据线程句柄
	HANDLE		m_hThreadRecv;		//接收数据线程句柄
	CRITICAL_SECTION m_cs;			//临界区对象
	BOOL		m_bConning;			//客户端连接状态
	BOOL		m_bExit;			//线程退出
};
#endif

#include <cstdio>
#include<iostream>
#include<string>
#include<WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
const int PORT = 8000;
#define MaxBufSize 1024
#define _CRT_SECURE_NO_WARINGS
int main() {
	WSADATA wsd;
	WSAStartup(MAKEWORD(2, 2), &wsd);
	SOCKET SocketClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	SOCKADDR_IN  ClientAddr;

	ClientAddr.sin_family = AF_INET;
	ClientAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
	ClientAddr.sin_port = htons(PORT);
	int n = 0;
	n = connect(SocketClient, (struct sockaddr*)&ClientAddr, sizeof(ClientAddr));
	if (n == SOCKET_ERROR) {
		cout << "连接失败" << endl;
		return -1;
	}
	cout << "已经连接到服务器" << n << ",可以向服务器发送消息了!" << endl;
	char info[1024], SendBuff[MaxBufSize], RecvBuff[MaxBufSize];
	while (1) {
		cout << "请输入要发送的信息,按回车结束发送(q退出):" << endl;
		gets_s(info);
		if (*info == 'q')
			break;
		strcpy(SendBuff, info);
		memset(info, 0, sizeof(info));

		n = send(SocketClient, SendBuff, sizeof(SendBuff), 0);
		memset(SendBuff, 0, sizeof(SendBuff));
		if (n < 0) {
			cout << "发送失败" << endl;
		}
		Sleep(500);
		int n = 0;
		n = recv(SocketClient, RecvBuff, sizeof(RecvBuff), 0);
		if (n > 0) {
			cout << "接收到服务器的消息为:" << RecvBuff << endl;
		}
		if (*RecvBuff == 'q')
			break;
		memset(RecvBuff, 0, sizeof(RecvBuff));
	}
	cout << "与服务器连接线程关闭" << endl;
	closesocket(SocketClient);
	WSACleanup();
	return 0;

}

2、UDP并发“服务器-客户端”

这个程序中UDP的交互中对应客户端每个消息不必回应。

(1)UDP服务器端

// stdafx.h : include file for standard system include files,
//  or project specific include files that are used frequently, but
//      are changed infrequently
//

#if !defined(AFX_STDAFX_H__E01B4F25_9E02_42E0_9E68_09D08D1C776F__INCLUDED_)
#define AFX_STDAFX_H__E01B4F25_9E02_42E0_9E68_09D08D1C776F__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#define WIN32_LEAN_AND_MEAN		// Exclude rarely-used stuff from Windows headers



// TODO: reference additional headers your program requires here

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_STDAFX_H__E01B4F25_9E02_42E0_9E68_09D08D1C776F__INCLUDED_)

#include <stdio.h>
#include <cstdio>
#include<sstream>
#include<iostream>
#include<string.h>
#include<WinSock2.h>
#include <sys/types.h>

#pragma comment(lib,"ws2_32.lib")
using namespace std;

const int PORT = 8000;
#define MaxClient 10
#define MaxBufSize 1024
#define _CRT_SECURE_NO_WARINGS
//服务线程
DWORD WINAPI ServerThread(LPVOID lpParameter) {
	int port = *(int*)lpParameter;
	port--;
	SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, 0);
	//创建地址结构体.
	SOCKADDR_IN addrClient;
	addrClient.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrClient.sin_family = AF_INET;
	addrClient.sin_port = htons(port);
	//绑定套接字和地址.
	bind(sockClient, (SOCKADDR *)&addrClient, sizeof(SOCKADDR));
	char recvBuf[100];
	char sendBuf[100];
	memset(recvBuf, 0, sizeof(recvBuf) / sizeof(char));
	memset(sendBuf, 0, sizeof(sendBuf) / sizeof(char));
	int len = sizeof(SOCKADDR);
	int i = 0;
	while (i < 20) {
		i++;
		recvfrom(sockClient, recvBuf, 100, 0, (SOCKADDR *)&addrClient, &len);
		cout << port << " says:" << recvBuf << endl;
		if (*recvBuf == 'q') {
			sendto(sockClient, "q", 1, 0, (SOCKADDR *)&addrClient, sizeof(SOCKADDR));
			cout << "The connection with " << port << " ends!" << endl;
			break;
		}
		ostringstream oss;
		oss << "echo:" << recvBuf;
		strcpy(sendBuf, oss.str().c_str());
		cout << "The data returned to " << port << " is: " << sendBuf << endl;
		//发送数据.
		sendto(sockClient, sendBuf, 100, 0, (SOCKADDR*)&addrClient, sizeof(SOCKADDR));
	}
	closesocket(sockClient);
	return 0;
}

int main() {
	WORD wVersionRequested;

	WSADATA wsaData;

	int err;
	wVersionRequested = MAKEWORD(1, 1);
	err = WSAStartup(wVersionRequested, &wsaData);
	if (err != 0) {
		return 0;
	}
	if (LOBYTE(wsaData.wVersion) != 1 ||
		HIBYTE(wsaData.wVersion) != 1) {
		WSACleanup();
		return 0;
	}

	//创建套接字
	SOCKET sockSrv = socket(AF_INET, SOCK_DGRAM, 0);
	//创建地址结构体.
	SOCKADDR_IN addrSrv;
	addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
	addrSrv.sin_family = AF_INET;
	addrSrv.sin_port = htons(6000);
	//绑定套接字和地址.
	bind(sockSrv, (SOCKADDR *)&addrSrv, sizeof(SOCKADDR));
	char recvBuf[100];
	char sendBuf[100];
	SOCKADDR_IN addrClient;
	int len = sizeof(SOCKADDR);

	int i = 6000;
	cout << "listening..." << endl;
	while (1) {
		i++;

		//接收客户端连接请求
		recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR *)&addrClient, &len);

		cout << "一个客户端已连接到服务器,新端口是:" << i << endl;
		string i_s;
		stringstream ss;
		ss << i;
		ss >> i_s;
		strcpy(sendBuf, i_s.c_str());

		sendto(sockSrv, sendBuf, 100, 0, (SOCKADDR*)&addrClient, sizeof(SOCKADDR));
		CreateThread(NULL, 0, &ServerThread, &i, 0, NULL);
	}//while
	closesocket(sockSrv);
	WSACleanup();
	return(0);
}//main

(2)UDP客户端
#include <Winsock2.h>
#include <stdio.h>
#include <cstdio>
#include<iostream>
#include<string>
#pragma comment(lib,"ws2_32.lib")
using namespace std;

void main() {

	WORD wVersionRequested;

	WSADATA wsaData;

	int err;



	wVersionRequested = MAKEWORD(1, 1);



	err = WSAStartup(wVersionRequested, &wsaData);

	if (err != 0) {

		return;

	}



	if (LOBYTE(wsaData.wVersion) != 1 ||

		HIBYTE(wsaData.wVersion) != 1) {

		WSACleanup();

		return;

	}



	SOCKET sockClient = socket(AF_INET, SOCK_DGRAM, 0);

	SOCKADDR_IN addrClient;

	addrClient.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

	addrClient.sin_family = AF_INET;

	addrClient.sin_port = htons(6000);



	char recvBuf[100];

	char sendBuf[100];

	int len = sizeof(SOCKADDR);
	int i = 0;


	while (i < 20) {
		i++;

		printf("please input date:");

		gets_s(sendBuf);

		sendto(sockClient, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&addrClient, len);

		if (*sendBuf == 'q')
			break;
		recvfrom(sockClient, recvBuf, 100, 0, (SOCKADDR*)&addrClient, &len);
		cout << "The message from server is:" << recvBuf << endl;
		if (i == 1) {
			int port = atoi(recvBuf);
			cout << "port:" << port << endl;
			addrClient.sin_port = htons(port);
		}

	}
	closesocket(sockClient);
	WSACleanup();
	return;
}
  • 4
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值