【发布时间】:2022-12-14 03:05:27
【问题描述】:
我面临一个非常奇怪的问题。我有一个运行 UDP 套接字并等待传入数据的服务器应用程序。一旦它收到命令,它就会开始发回一个流。只是为了测试,我限制服务器只发送一条 8000 字节长的数据。我不提供服务器代码,因为它按预期工作。它接收命令并发回数据,我可以用 Wireshark 看到它。我的问题是客户规模。
问题:我实例化一个客户端非阻塞 UDP 套接字并将“Hello”发送到响应 8000 字节数据的服务器。我正在尝试以 1024 字节的块循环读取数据。但问题在于只有一个数据块已读取。下一个循环无限返回 -1。如果我尝试读取 recv 中的 8000 个字节,我会成功读取它,如果我尝试读取 recv 中的 8100 个字节,我会读取已发送的 8000 个字节。我的意思是只有一次调用 recv 成功。尽管尚未读取所有数据,但所有后续调用都会返回错误。
这是一个简化的代码:
class ClienSocket
{
public:
void Init()
{
pollfd m_poll = {};
m_poll.fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(m_poll.fd == -1)
{
throw std::runtime_error(GetLastError());
}
int optval = 1;
setsockopt(m_poll.fd, SOL_SOCKET, SO_REUSEADDR, static_cast<const void *>(&optval), sizeof(int));
int on = 1;
if(ioctl(m_poll.fd, FIONBIO, &on) < 0)
{
throw std::runtime_error(std::string("failed to set the client socket non-blocking: ") + strerror(errno));
}
}
void Run()
{
struct sockaddr_in serv_addr;
m_servaddr.sin_family = AF_INET;
m_servaddr.sin_addr.s_addr = inet_addr(m_address.c_str());
m_servaddr.sin_port = htons(static_cast<uint16_t>(m_port));
m_poll.events = POLLIN;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(m_port);
m_running = true;
if(pthread_create(&m_readThread, nullptr, &ClienSocket::ReadThreadWrapper, this) != 0)
{
m_running = false;
throw std::runtime_error(std::string("thread creating error");
}
}
void ClienSocket::Write(const char *data, size_t size)
{
sendto(m_poll.fd, data, size, MSG_NOSIGNAL, reinterpret_cast<const struct sockaddr *>(&(m_servaddr)), sizeof(sockaddr_in));
}
static void *ClienSocket::ReadThreadWrapper(void *ptr)
{
ClienSocket *instance = static_cast<ClienSocket *>(ptr);
if(instance != nullptr)
{
return instance->ReadThreadFunc();
}
return nullptr;
}
void *ClienSocket::ReadThreadFunc()
{
while(m_running)
{
retval = poll(&m_poll, 1, 1000);
if(retval > 0)
{
if(m_poll.revents == POLLIN)
{
bool readMore = true;
do
{
ssize_t readBytes = recv(m_poll.fd, m_readBuffer, READ_BUFFER_SIZE, 0);
std::cout << readBytes << ", " << errno << std::endl;
if (readBytes < 0)
{
if (errno != EWOULDBLOCK)
{
throw std::runtime_error(std::string("socket error");
}
}
else if(readBytes == 0)
{
readMore = false;
}
else
{
ProcessData(m_readBuffer, readBytes);
}
}
while(readMore == true);
}
}
}
return nullptr;
}
void ClienSocket::Wait()
{
if(m_running)
{
pthread_join(m_readThread, nullptr);
}
}
void ProcessData(const char *data, size_t length)
{
std::cout << length << std::endl;
}
private:
bool m_running = false;
int m_port = 3335;
std::string m_address = "192.168.5.1";
struct sockaddr_in m_servaddr;
pollfd m_poll = {};
pthread_t m_readThread;
static constexpr size_t READ_BUFFER_SIZE = 1024;
char m_readBuffer[READ_BUFFER_SIZE];
}
测试用例:
ClienSocket client;
client.Init();
client.Run();
client.Write("hello", 5);
clientWait();
目标系统:Ubuntu 22.04
输出:
1024, 0
-1, 11
-1, 11
-1, 11
-1, 11
-1, 11
...
【问题讨论】:
标签: c++ sockets udp nonblocking