I am facing a very strange problem. I have a server application that runs UDP socket and wait for incoming data. As soon as it gets the command it begins to send back a stream. Just for testing, I limit the server to sending only one piece of data 8000 bytes long. I don't provide the server code since it work as expected. It receives the command and sends data back, I can see it with Wireshark. My problem is the client size.
The issue: I instantiate a client non-blocking UDP socket and send "Hello" to the server that responses with 8000 bytes of data. I'm trying to read data in a loop in chunks of 1024 bytes. But the problem that only one chunk of data has read. the next loop returns -1 infinitely. If I try to read 8000 bytes in recv
I read it successfully, If I try to read 8100 bytes in recv
I read 8000 bytes that sent. I mean that only one call to recv
succeed. All subsequent calls return an error although not all data has read yet.
Here is a simplified code:
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];
}
The testcase:
ClienSocket client;
client.Init();
client.Run();
client.Write("hello", 5);
clientWait();
According to Wireshard 8000 bytes has sent:
Target system: Ubuntu 22.04
The output:
1024, 0
-1, 11
-1, 11
-1, 11
-1, 11
-1, 11
...