我写了 2 个简单的 udp 类(通过削减一些服务器和客户端代码)。
#include "usefull.h"
#include <winsock2.h>
#include <Ws2tcpip.h>
#pragma comment (lib, "Ws2_32.lib")
class IADDR
{
public:
sockaddr_in addr;
IADDR()
{
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
}
IADDR(string ip,int16_t port)
{
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
char buff1[100];
InetPton(AF_INET, ip.c_str(), &addr.sin_addr.s_addr);
addr.sin_port = htons(port);
//addr.sin_addr.s_addr = htonl(addr.sin_addr.s_addr);
}
};
class UDP
{
public:
UDP()
{
ok = 0;
if (!is_st)
{
WSADATA w;
int res = WSAStartup(MAKEWORD(2, 2), &w);
if (res != 0)
{
return;
}
is_st = 1;
}
}
~UDP()
{
if (ok == 1)
{
closesocket(sock);
ok = 0;
}
}
void set(IADDR a)
{
if (ok == 1)
{
closesocket(sock);
ok = 0;
}
if (is_st == 0)
return;
addr = a;
sock = socket(AF_INET, SOCK_DGRAM, 0);
u_long nMode = 1;
if (ioctlsocket(sock, FIONBIO, &nMode) == SOCKET_ERROR)
{
closesocket(sock);
}
ok = 1;
}
void send(vector<byte> v)
{
send(&v[0], v.size());
}
void send(const byte* buff, uint32_t size)
{
if (ok == 0)
return;
int len = sizeof(addr.addr);
sendto(sock, (char*)buff, size, 0, (sockaddr*)&addr.addr, len);
}
bool recv(vector<byte> &data)
{
if (ok == 0)
return 0;
data.resize(1024);
sockaddr_in serverInfo;
int fromlen = sizeof(addr.addr);
uint len = recvfrom(sock, (char*)&data[0], 1024, 0, (sockaddr*)&serverInfo, &fromlen);
if (len != SOCKET_ERROR)
{
data.resize(len);
return 1;
}
else
{
data.resize(0);
return 0;
}
}
private:
int sock;
IADDR addr;
int ok;
static int is_st;
friend class UDPSERVER;
};
class UDPSERVER
{
public:
UDPSERVER()
{
ok = 0;
if (!UDP::is_st)
{
WSADATA w;
int res = WSAStartup(MAKEWORD(2, 2), &w);
if (res != 0)
{
return;
}
UDP::is_st = 1;
}
}
~UDPSERVER()
{
if (ok == 1)
{
closesocket(sock);
ok = 0;
}
}
void set(USHORT port)
{
if (UDP::is_st == 0)
return;
if (ok == 1)
{
closesocket(sock);
ok = 0;
}
struct sockaddr_in sv_ip;
sv_ip.sin_family = AF_INET;
sv_ip.sin_port = htons(port);
sv_ip.sin_addr.s_addr = INADDR_ANY;
sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock == INVALID_SOCKET)
{
return;
}
u_long nMode = 1;
if (ioctlsocket(sock, FIONBIO, &nMode) == SOCKET_ERROR)
{
closesocket(sock);
return;
}
if (bind(sock, (sockaddr*)&sv_ip, sizeof(sv_ip)) == SOCKET_ERROR)
{
closesocket(sock);
return;
}
ok = 1;
}
void send(vector<byte> v, IADDR &a)
{
send(&v[0], v.size(), a);
}
void send(const byte* buff, uint32_t size, IADDR &a)
{
if (ok == 0)
return;
int fromlen = sizeof(a.addr);
sendto(sock, (char*)buff, size, 0, (sockaddr*)&a.addr, fromlen);
}
bool recv(vector<byte> &data, IADDR &a)
{
if (ok == 0)
return 0;
data.resize(1024);
int fromlen = sizeof(a.addr);
int len = recvfrom(sock, (char*)&data[0], 1024, 0, (sockaddr*)&a.addr, &fromlen);
if (len != SOCKET_ERROR)
{
data.resize(len);
return 1;
}
else
{
data.resize(0);
return 0;
}
}
private:
SOCKET sock;
IADDR addr;
int ok;
};
int UDP::is_st = 0;
我对套接字一无所知。它似乎工作正常,但它会崩溃吗?并且有必要以某种方式将其转移到 Linux,但是如何?使用 boost 是不可接受的!(因为安装需要 2 小时,甚至不是 1 次)。也不应该使用 QT。他有执照。
Win 编译 VS 和 Linux QTCreator。
我什至没有尝试在 Linux 下编译。显然在Windows下。
要将代码转换为 Linux,您需要将套接字系统调用更改为 POSIX。按顺序,您的第一个调用是将定义 IPv4 地址的字符串通过
InetPton
. 它需要替换为以下内容:该函数在成功时返回 1。
Linux 上不需要调用
WSAStartup
以及与之相关的所有内容。该函数
closesocket
必须替换为close
。该参数是一个套接字描述符sock
。创建套接字的调用
socket
不会改变,它们在两个系统上是相同的。如果成功,该函数将返回一个非负类型描述符int
。调用
ioctlsocket
需要更改为以下内容:fcntl
失败时返回 -1。作为替代方案,还有FIONBIO
一个选项,但O_NONBLOCK
它包含在 POSIX 标准中,FIONBIO
我不建议使用它:两个系统上的调用
sendto
是相同的,就像recvfrom
. 两个函数都在失败时返回 -1。因此,在所有调用中,有必要重新检查函数返回的值。同样在 Linux 上,套接字描述符的类型为
int
.笔记
如果我是你,我会重新考虑你的代码结构的组织。您的两个类在许多方面相互重复,并且在某些地方存在未使用的变量。
此外,正如test123 所指出的,您正在以非阻塞模式使用套接字,因此在套接字不存在时尝试从套接字获取数据将导致函数错误
recvfrom
。如果确实需要非阻塞套接字模式,可以使用select
或pselect
等待输入。如果你使用
Qt
,那么它也有自己的通过类处理套接字的版本QUdpSocket
。它的描述在官方文档中。