Windows 多网卡并行通信程序设计(C++)

1.任务要求

在PC上同时插入多个无线USB网卡,利用5个USB网卡并行通信。

2.实验准备

  1. 开发环境:VS2015
  2. 测试工具:wireshark抓包软件(服务器端)
  3. 硬件条件:多个USB网卡(系统需具备相应驱动),最好是3个以上

3.项目分析

平常我们的PC都是利用一个网卡来通信(无线或者有线),应用程序也只会绑定一个网卡。但现在是实现多个网卡实现并行的收发数据!当多个无线USB网卡接入电脑,都连接同一个AP,怎么才能让自己的程序绑定接入的USB网卡、并行收发数据呢?

  1. 获取接入PC的USB网卡信息(名称、MAC地址、IP地址等);
  2. 创建多线程,一个线程绑定一个网卡,依据网卡上的信息来异步执行任务;
  3. 最后,Windows Socket编程,与服务器建立TCP、UDP连接,收发数据。

4.实验过程

4.1 PIP_ADAPTER_INFO结构体获取网卡信息

结构体ADAPTER_INFO用于于获取PC上网络适配器的IPv4信息,该结构体通过调用GetAdaptersInfo函数获取值。

          //PIP_ADAPTER_INFO结构体指针存储本机网卡信息
	PIP_ADAPTER_INFO pIpAdapterInfo = new IP_ADAPTER_INFO();
	//得到结构体大小,用于GetAdaptersInfo参数
	unsigned long stSize = sizeof(IP_ADAPTER_INFO);
	//调用GetAdaptersInfo函数,填充pIpAdapterInfo指针变量;其中stSize参数既是一个输入量也是一个输出量
	int nRel = GetAdaptersInfo(pIpAdapterInfo, &stSize);
	//记录网卡数量
	int netCardNum = 0;
	//记录每张网卡上的IP地址数量
	int IPnumPerNetCard = 0;
	if (ERROR_BUFFER_OVERFLOW == nRel){
		//如果函数返回的是ERROR_BUFFER_OVERFLOW
		//释放原来的内存空间
		delete pIpAdapterInfo;
		//重新申请内存空间用来存储所有网卡信息
		pIpAdapterInfo = (PIP_ADAPTER_INFO)new BYTE[stSize];
		//再次调用GetAdaptersInfo函数,填充pIpAdapterInfo指针变量
		nRel = GetAdaptersInfo(pIpAdapterInfo, &stSize);
	}
	if (ERROR_SUCCESS == nRel){
		//输出网卡信息
		getUSBWirelessAdapter(pIpAdapterInfo, IPnumPerNetCard, netCardNum);
	}
	//释放内存空间
	if (pIpAdapterInfo){
		delete pIpAdapterInfo;
	}

其中getUSBWirelessAdapter(PIP_ADAPTER_INFO pIpAdapterInfo,int IPnumPerNetCard, int netCardNum)函数用与筛选出所需要的网卡和分配到的IP地址等信息,结果如下图。其中依据USB关键字在程序里筛选出USB无线网卡。

4.2 多线程创建

CreateThread函数创建多个线程,利用(LPVOID)LocalIP[i]ip地址作为参数传入每个线程。每个线程要执行的就是各自与服务器端建立TCP,传输数据。多线程创建结果如下图。

HANDLE hthread[5];//创建线程句柄
DWORD threadid[5];//保存线程的编号;
for (i = 0; i < 5; i++)
{
	hthread[i] = CreateThread(
		NULL,//表示线程内核对象的安全属性;
		NULL,//表示线程堆栈空间的大小;
		mymsg,//表示新线程所执行的线程函数地址;
		(LPVOID)LocalIP[i],//函数的参数;
		0,//立刻执行;
		&threadid[i]//将返回线程的ID号;
	);
}

4.3 socket编程

每个线程绑定到不同的ip地址,各自与服务器进行TCP通信。

DWORD WINAPI mymsg(LPVOID lp)
{
	char *ip = (char *)lp;
	printf("我是子线程, pid = %d\n", GetCurrentThreadId());   //输出子线程pid
	/*********************
	 *@SOCKET配置
	 *********************/
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA data;
	if (WSAStartup(sockVersion, &data) != 0){
		return 0;
	}
	
	SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	struct sockaddr_in localaddr;
        //要发送的数据
	char  sendData[] = "this is USB wireless Adapter!";
	printf("%s\n", strcat(sendData, ip));

	localaddr.sin_family = AF_INET;
	localaddr.sin_addr.s_addr = inet_addr(ip);//绑定本地ip地址
	localaddr.sin_port = 0;  // Any local port will do
	bind(sclient, (struct sockaddr *)&localaddr, sizeof(localaddr));

	if (sclient == INVALID_SOCKET){
		printf("invalid socket !");
		return 0;
	}
	sockaddr_in serAddr;
	serAddr.sin_family = AF_INET;
	serAddr.sin_port = htons(8080);//服务器端的ip地址和端口号
	serAddr.sin_addr.S_un.S_addr = inet_addr("192.168.50.155");
	if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR){
		printf("connect error !");
		closesocket(sclient);
		return 0;
	}
	//循环发送数据,便于观察
	for (int i = 0; i < 200; ++i) {
		send(sclient, sendData, strlen(sendData), 0);
	}
        //断开连接
	closesocket(sclient);
	WSACleanup();
	return 0;
}

下图是在服务器端利用wireshark抓包的结果,可见五个网卡同时与服务器建立了连接,异步传输数据 。但后续的传输数据部分没有截图保留结果。

 5 实验总结 

该实验是项目之前的小测试,需求感觉很奇怪。。。完成只用了一个晚上,算是比较轻松。

程序需要完善的地方有很多!!!

  1. 多个网卡连接热点需要手动输入密码去连接,对多个网卡来说,太繁琐了。最好在程序里预先定义好,只要插入一个USB网卡就连接到AP,进而创建线程开始数据传输,这样效率更高。
  2. socket编程只进行了一个TCP数据的发送,没有进行数据的接收。服务器端的程序也没来得及去编写,以后有时间再说吧~~
  3. 线程只是绑定了网卡上的IP,不知道能不能像Linux系统那样直接绑定到网卡。
  4. 不对,多个线程对应的是同一个ip地址和端口,在服务器端,数据包的处理还是要排队的,并不是真正的并行。

6 代码

#include <stdio.h>
#include <stdlib.h>

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <process.h> 
#include <iostream>
#include <string>
#include <WinSock2.h>
#include <Iphlpapi.h>
using namespace std;

#pragma comment(lib,"ws2_32.lib")
#pragma comment(lib,"Iphlpapi.lib") //需要添加Iphlpapi.lib库

char LocalIP[5][16];
int ipadd = 0;
DWORD WINAPI mymsg(LPVOID lp)
{
	char *ip = (char *)lp;
	printf("我是子线程, pid = %d\n", GetCurrentThreadId());   //输出子线程pid
	/*********************
	 *@SOCKET配置
	 *********************/
	WORD sockVersion = MAKEWORD(2, 2);
	WSADATA data;
	if (WSAStartup(sockVersion, &data) != 0)
	{
		return 0;
	}
	
	SOCKET sclient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
	struct sockaddr_in localaddr;

	char  sendData[] = "this is USB wireless Adapter!";
	printf("%s\n", strcat(sendData, ip));

	localaddr.sin_family = AF_INET;
	localaddr.sin_addr.s_addr = inet_addr(ip);
	localaddr.sin_port = 0;  // Any local port will do
	bind(sclient, (struct sockaddr *)&localaddr, sizeof(localaddr));

	if (sclient == INVALID_SOCKET)
	{
		printf("invalid socket !");
		return 0;
	}
	sockaddr_in serAddr;
	serAddr.sin_family = AF_INET;
	serAddr.sin_port = htons(8080);
	serAddr.sin_addr.S_un.S_addr = inet_addr("192.168.50.155");
	if (connect(sclient, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR){
		printf("connect error !");
		closesocket(sclient);
		return 0;
	}
	//发送数据
	for (int i = 0; i < 200; ++i) {
		send(sclient, sendData, strlen(sendData), 0);
	}
	//接收服务器返回字符串
/*	char recData[255];
	int ret = recv(sclient, recData, 255, 0);
	if (ret > 0)
	{
		recData[ret] = 0x00;
		printf(recData);
	}*/
	closesocket(sclient);
	WSACleanup();
	return 0;
}
void getUSBWirelessAdapter(PIP_ADAPTER_INFO pIpAdapterInfo,int IPnumPerNetCard, int netCardNum) {

	//可能有多网卡,因此通过循环去判断
	while (pIpAdapterInfo)
	{
		//识别当前接入的USB网卡
		if (strstr(pIpAdapterInfo->Description, "USB")) {
			cout << "网卡描述:" << pIpAdapterInfo->Description << endl;

			//输出网卡物理地址
			cout << "网卡MAC地址:";
			for (int i = 0; i < pIpAdapterInfo->AddressLength; i++)
				if (i < pIpAdapterInfo->AddressLength - 1)
				{
					printf("%02X-", pIpAdapterInfo->Address[i]);
				}
				else {
					printf("%02X\n", pIpAdapterInfo->Address[i]);
				}
				cout << "网卡IP地址如下:" << endl;
				//可能网卡有多IP,因此通过循环去判断
				IP_ADDR_STRING *pIpAddrString = &(pIpAdapterInfo->IpAddressList);
				do
				{

					cout << "IP 地址:" << pIpAddrString->IpAddress.String << endl;

					strcpy(LocalIP[IPnumPerNetCard], pIpAddrString->IpAddress.String);

					cout << "该网卡上的IP数量:" << ++IPnumPerNetCard << endl;
					cout << "子网掩码:" << pIpAddrString->IpMask.String << endl;
					cout << "网关地址:" << pIpAdapterInfo->GatewayList.IpAddress.String << endl;
					pIpAddrString = pIpAddrString->Next;

				} while (pIpAddrString);
				cout << "--------------------------------------------------------------------" << endl;
		}
		netCardNum++;
		pIpAdapterInfo = pIpAdapterInfo->Next;
	}
	cout << "网络设备:" << netCardNum << " 台" << endl;

}
int main(int argc, char* argv[])
{
	/*********************
	*@多线程配置
	*********************/
	int i;
	HANDLE hthread[5];
	DWORD threadid[5];//保存线程的编号;
	/*
	STARTUPINFO si = { sizeof(STARTUPINFO) };//在产生子进程时,子进程的窗口相关信息
	PROCESS_INFORMATION pi;                  //子进程的ID/线程相关信息
	DWORD returnCode;//用于保存子程进的返回值;
	*/
	/*********************
	*@网卡信息获取
	*********************/
	//PIP_ADAPTER_INFO结构体指针存储本机网卡信息
	PIP_ADAPTER_INFO pIpAdapterInfo = new IP_ADAPTER_INFO();
	//得到结构体大小,用于GetAdaptersInfo参数
	unsigned long stSize = sizeof(IP_ADAPTER_INFO);
	//调用GetAdaptersInfo函数,填充pIpAdapterInfo指针变量;其中stSize参数既是一个输入量也是一个输出量
	int nRel = GetAdaptersInfo(pIpAdapterInfo, &stSize);
	//记录网卡数量
	int netCardNum = 0;
	//记录每张网卡上的IP地址数量
	int IPnumPerNetCard = 0;
	if (ERROR_BUFFER_OVERFLOW == nRel)
	{
		//如果函数返回的是ERROR_BUFFER_OVERFLOW
		//则说明GetAdaptersInfo参数传递的内存空间不够,同时其传出stSize,表示需要的空间大小
		//这也是说明为什么stSize既是一个输入量也是一个输出量
		//释放原来的内存空间
		delete pIpAdapterInfo;
		//重新申请内存空间用来存储所有网卡信息
		pIpAdapterInfo = (PIP_ADAPTER_INFO)new BYTE[stSize];
		//再次调用GetAdaptersInfo函数,填充pIpAdapterInfo指针变量
		nRel = GetAdaptersInfo(pIpAdapterInfo, &stSize);
	}
	if (ERROR_SUCCESS == nRel)
	{
		//输出网卡信息
		getUSBWirelessAdapter(pIpAdapterInfo, IPnumPerNetCard, netCardNum);
	}
	//释放内存空间
	if (pIpAdapterInfo)
	{
		delete pIpAdapterInfo;
	}
	for (int i = 0; i < 5; i++) {
		//当前获取到的IP地址
		puts(LocalIP[i]);
	}
	for (i = 0; i < 5; i++)
	{
		hthread[i] = CreateThread(
			NULL,//表示线程内核对象的安全属性;
			NULL,//表示线程堆栈空间的大小;
			mymsg,//表示新线程所执行的线程函数地址;
			(LPVOID)LocalIP[i],//函数的参数;
			0,//立刻执行;
			&threadid[i]//将返回线程的ID号;
		);
	}
	//mymsg(NULL);
	for (i = 0; i < 5; i++) {
		CloseHandle(hthread[i]);
	}
	system("pause");
	return 0;
}

 

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

我楚狂声

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

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

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

打赏作者

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

抵扣说明:

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

余额充值