1、UDP多个通信对象时,端口号绑定与端口号监听问题
if (bind((this->sockfds)[i], (sockaddr*)&(this->serverAddr), sizeof((this->serverAddr))) == SOCKET_ERROR) {
std::cerr << "Bind failed" << std::endl;
closesocket((this->sockfds)[i]);
WSACleanup();
return -1;
}
int n = recvfrom((sthis->sockfds)[i], buffer, 1024, 0, (struct sockaddr*)&clientAddr, &clientAddrLen);
if (n == SOCKET_ERROR) {
std::cerr << "recvfrom failed" <<i++<< std::endl;
continue;
}
其中bind函数可以将serverAddr中的端口号绑定到sockfds数组中,而在resvfrom函数可以监听sockfds[i]对应套接字的端口号信息,当有数据发到该端口号时,recvfrom函数可以接收到该消息。
2、多线程绑定函数问题
void UdpCommunication::udp_receive(int i) {
// 设置服务器地址结构
struct sockaddr_in clientAddr;
memset(&clientAddr, 0, sizeof(clientAddr));
char buffer[1024];
while (true) {
int clientAddrLen = sizeof(clientAddr);
//cout << "brfore recv:" << i << endl;
//监听编号为i的端口号,消息没发过来的时候处于阻塞状态
int n = recvfrom((sthis->sockfds)[i], buffer, 1024, 0, (struct sockaddr*)&clientAddr, &clientAddrLen);
if (n == SOCKET_ERROR) {
std::cerr << "recvfrom failed" <<i++<< std::endl;
continue;
}
// 获取发送方的 IP 地址和端口号
char ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &clientAddr.sin_addr, ip, INET_ADDRSTRLEN);
int remotePort = ntohs(clientAddr.sin_port); //发送端端口号
buffer[n] = '\0';
cout << "getdata:" << buffer << ":" << remotePort << ":" << ip << endl;
//接收到消息后,需将消息发送到消息队列中
}
}
//创建多个线程分别监听对应端口号数量的消息,每个线程需都需调用udp_receive(int i)函数
void UdpCommunication::udp_receives() {
//根据开放的端口数量,创建数据接收线程,每个端口号开放一个监听线程
for (int i = 0; i < ipInfo.size(); i++) {
thread newThread(udp_receive, i);
//使用detach运行线程,不会阻塞其他线程,各线程根据系统分配获取时间片
newThread.detach();
}
}
使用newThread创建线程时,因为udp_receive函数为类的成员函数,但创建时newThread无法绑定类的普通成员函数,对于类内的函数,newThread只能绑定类的静态成员函数,因此udp_receive函数只能用static修饰。
因为udp_receive为静态成员函数,所以只能访问类的静态成员变量,如果该函数内要使用类的普通成员函数,可以将该类初始化一个静态指针。
static UdpCommunication *sthis;
在类的构造函数中,可以将该静态指针指向当前对象。
//类的静态成员变量在.h文件中声明,如果要在cpp文件中使用,要在cpp文件中链接
UdpCommunication* UdpCommunication::sthis;
UdpCommunication::UdpCommunication(vector<IPInfo> info) {
sthis = this; //将当前对象指令静态当前对象
}
这样在静态成员函数中即可使用类的普通成员变量。
int n = recvfrom((sthis->sockfds)[i], buffer, 1024, 0, (struct sockaddr*)&clientAddr, &clientAddrLen);
3、VS2022使用gitee推送最新代码
在按照网上的方法创建了gitee仓库,使用vs2022git功能连接远程仓库后,在推送了第一次程序后如果后面程序还需要更新,前提是先从仓库克隆上一次提交的程序,在克隆的程序基础上进行修改,修改后,需要提交时,可点击vs2022界面右下角的git更改。
随后更改数的地方会出现跟上次推送相比,程序发生了改变的文件。
随后在输入框内输入本次提交的描述,点击提交选项小箭头,选择全部提交并推送即可
4、类模版不能定义在.h文件,实现在.cpp文件
像:
这样会报错,同一个函数使用模版必须只在.h文件或者.cpp文件定义和实现
5、VS2022读取XML文件内中文乱码问题
解决方法:
在编码的地方设置编码格式为简体中文
此时XML文件的编码格式也会自动发生变化
这个时候中文将不会乱码
6、C++使用map的find函数获取map<key,value>中的value
map中的find函数返回的是一个key对应值的迭代器位置,即返回的是一个:
map<string, vector<Fault>>::iterator it = faultsMap.find(u.systemName);
要想获取该key对应的value值,只需获取该迭代器的second即可:
(*it).second;
这里获取的就是key为 u.systemName 时对应的 vector<Fault>
7、C++父类对象调用子类的函数问题
在父类定义函数时,如果采用该方法定义messageCopy函数
class ObjectInterface {
public:
int faultID;
string systemName;
void messageCopy(char* s, int length);
}
子类的实现:
class UWMU:public ObjectInterface{
public:
//接收报文
unsigned short A;
unsigned short B;
void messageCopy(char *s, int length) { //将报文拷贝到指定地方
memcpy(&(this->A), s, length);
cout <<"this.A" << this->A << endl;
cout << "this.C" << this->C << endl;
}
}
使用父类的接口调用子类的函数,其中getSysObj会返回UWMU对象:
ObjectInterface *obj = getSysObj(remotePort); //根据远程端口区分不同的系统
obj->messageCopy(buffer, n);
------------------------------------------------------------------------------
#include "Global.h"
#include <string>
UWMU *uwmu=new UWMU;
ObjectInterface* getSysObj(int port) {
switch (port)
{
case 2005:
return uwmu;
default:
return NULL;
break;
}
}
--------------------------------------------------------------------------------
在使用上述方法调用memageCopy函数后,编译报错,无法解析的外部符号:
解决方法:在父类函数定义是使该函数定义为虚函数且使用{}使该函数的定义更完整。
class ObjectInterface {
public:
int faultID;
string systemName;
virtual void messageCopy(char* s, int length) { } //注意这里函数的定义要加{} ,如果用;结尾会报错
}