Windows环境下C++多线程TCP通信

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/sinat_23118323/article/details/71006073

最近突然对传输层的东西有了兴趣...发现自己虽然学过计网也学过C++,却没有真正实现过客户端和服务器之间的通信,于是恶补了一下socket知识,再加上大佬们的指导,模仿着写了个demo...

直接上代码。

首先是服务器端,要注意如果一开始写了using namespace std; 那么就要注意std里面的bind和socket相关的bind是同名的...如果直接调用bind,实际上调用的是std::bind,而不是socket的bind方法。解决途径是调用::bind,感谢万能的StackOverflow...

因为这个问题我昨晚调试到半夜三点依然没解决...同时也感谢cao大佬的耐心指导,顺便贴个友链:http://showlinkroom.me/

Sever代码如下:

#include "stdafx.h"
#include <WinSock2.h> //windows socket的头文件
#include <Windows.h>
#include <iostream>
#include <thread>
#include <mutex>
#include <process.h>

#pragma comment(lib, "ws2_32.lib") //连接winsock2.h的静态库文件

using namespace std;

mutex m;

//定义结构体用来设置
typedef struct my_file
{
	SOCKET clientSocket; //文件内部包含了一个SOCKET 用于和客户端进行通信
	sockaddr_in clientAddr; //用于保存客户端的socket地址
	int id; //文件块的序号
}F;

DWORD WINAPI transmmit(const LPVOID arg)
{
	//实际上这里为了追求并发性不应该加锁,上锁是为了方便看输出
	m.lock();

	F *temp = (F*)arg;
	//获取文件的序号
	//int file_id = temp->id;
	//获取客户机的端口号
	//ntohs(temp -> clientAddr.sin_port); 
	cout << "测试开始,等待客户端发送消息..." << endl;
	//从客户端处接受数据
	char Buffer[MAXBYTE] = { 0 }; //缓冲区
	recv(temp->clientSocket, Buffer, MAXBYTE, 0); //recv方法 从客户端通过clientScocket接收
	cout << "线程" << temp->id << "从客户端的" << ntohs(temp->clientAddr.sin_port) << "号端口收到:" << Buffer << endl;

	//发送简单的字符串到客户端
	const char* s = "Server file";
	send(temp->clientSocket, s, strlen(s)*sizeof(char)+1, NULL);
	cout << "线程" << temp->id << "通过客户端的" << ntohs(temp->clientAddr.sin_port) << "号端口发送:" << s << endl;

	m.unlock();

	return 0;
}



int main()
{
	WSADATA wsaData;
	//第一个参数是winsocket load的版本号(2.2)
	WSAStartup(MAKEWORD(2, 3), &wsaData);
	//创建服务器端的socket(协议族, sokcet类型)
	SOCKET servSocket = socket(AF_INET, SOCK_STREAM, 0);//如果改成SOCK_DGRAM则使用UDP
	sockaddr_in servAddr; //服务器的socket地址,包含sin_addr表示IP地址,sin_port保持端口号和sin_zero填充字节
	memset(&servAddr, 0, sizeof(SOCKADDR)); //初始化socket地址
	servAddr.sin_family = PF_INET; //设置使用的协议族
	servAddr.sin_port = htons(2017); //设置使用的端口
	servAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); //define s_addr = S_un.S_addr
	::bind(servSocket, (SOCKADDR *)&servAddr, sizeof(SOCKADDR)); //将之前创建的servSocket和端口,IP地址绑定

	HANDLE hThread[20]; //获取句柄
	listen(servSocket, 20); //监听服务器端口
	for (int i = 0; i < 20; i++)
	{
		F *temp = new F; //创建新的传输结构体
		sockaddr_in clntAddr;
		int nSize = sizeof(SOCKADDR);
		SOCKET clientSock = accept(servSocket, (SOCKADDR*)&clntAddr, &nSize);
		//temp数据成员赋值
		temp->clientSocket = clientSock;
		temp->id = i + 1;
		temp->clientAddr = clntAddr;
		//通过句柄创建子线程
		hThread[i] = CreateThread(NULL, 0, &transmmit, temp, 0, NULL);
	}
	//等待子线程完成
	WaitForMultipleObjects(20, hThread, TRUE, INFINITE);
	cout << WSAGetLastError() << endl; //查看错误信息

	//关闭socket,释放winsock
	closesocket(servSocket);
	WSACleanup();

	cout << "服务器连接已关闭。" << endl;
	system("pause");

	return 0;
}

注意bind之后要监听(listen),并且accpet请求队列中的socket以建立连接。

大概流程就是bind -> listen -> if someone connects -> accept,如果是UDP就可以省略listen和accept(这两个分别是监听和接受新连接,然而UDP并不需要建立连接),直接收发(sendto和recvfrom)。


客户端则只需要创建一个socket,填写好地址信息,通过connect发送连接请求,之后就可以send或者recv了。如果是UDP则直接sendto和recvfrom,不需要connect(UDP无连接)。

Client代码:

#include "stdafx.h"
#include <WinSock2.h> //windows socket的头文件
#include <Windows.h>
#include <iostream>
#include <thread>
#include <process.h>

#pragma comment(lib, "ws2_32.lib") //连接winsock2.h的静态库文件

using namespace std;

int main()
{
	//加载winsock库
	WSADATA wsadata;
	WSAStartup(MAKEWORD(2, 3), &wsadata);

	//客户端socket
	SOCKET clientSock = socket(PF_INET, SOCK_STREAM, 0);
	//初始化socket信息
	sockaddr_in clientAddr;
	memset(&clientAddr, 0, sizeof(SOCKADDR));
	//clientAddr.sin_addr.s_addr = htonl(INADDR_ANY);
	clientAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
	clientAddr.sin_family = PF_INET;
	clientAddr.sin_port = htons(2017);
	//建立连接
	connect(clientSock, (SOCKADDR*)&clientAddr, sizeof(SOCKADDR));

	cout << "已建立连接。" << endl;

	char* s = new char[100];
	cout << "请输入你要发送的文字消息: ";
	cin >> s;
	send(clientSock, s, strlen(s)*sizeof(char) + 1, NULL);
	cout << "已发送:" << s << endl;

	system("pause");
	char Buffer[MAXBYTE] = { 0 };
	recv(clientSock, Buffer, MAXBYTE, 0);

	cout << "通过端口:" << ntohs(clientAddr.sin_port) << "接收到:" << Buffer << endl;
	
	closesocket(clientSock);
	WSACleanup();

	cout << "客户端连接已关闭。" << endl;
	system("pause");

	return 0;
}
这里将TCP最大监听连接数设置成了20,最多可以同时接受20个连接请求。

这里实现的只是最基础的字符串传输,之后还要实现文件传输...Orz



阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页