Search code examples
c++windowstcpdelay

c++ tcp server (windows OS) - recv() delays when there is more then 1 connection to the server


When I'm trying to connect to the server with only 1 client, the recv() function on the server does not delay.

But when I'm starting the client console more then 1 time (something like 7 times), there is a delay of something like 2000ms after you send to the server packet with the function send() until the server will print the packet in is console.

Is there any solution without starting a thread for each client? (Windows limits the number of threads for each process).

The code is compiled with Visual Studio 2008, and this is the full server code:

#include <WinSock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#include <Windows.h>
#include <stdio.h>

struct sslv3
{
#define max_clients 1024
private:
    int cClient;
public:
    SOCKET fd;
    int CurrentClient()
    {
        return cClient;
    }
    struct client
    {
        client()
        {
            Valid = false;
        }
        bool Valid;
        DWORD ip;
        WORD port;
        char ipstr[33];
        char portstr[33];
        SOCKET fd;
        void StrGen()
        {
            wsprintf(ipstr, "%d.%d.%d.%d", ip & 0xFF, (ip & 0xFF00)/0x100, (ip & 0xFF0000)/0x10000, (ip & 0xFF000000)/0x1000000);
            wsprintf(portstr, "%d", port);
        }
    } clients[max_clients];
    //
    sslv3(bool server_client)
    {
        WSADATA wsaData;
        WSAStartup(MAKEWORD(2, 2), &wsaData);
        cClient = 0;
        fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        //
        DWORD timeout = 1;
        setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(DWORD));
    }
    int Bind(WORD port)
    {
        int ret = 0;
        sockaddr_in local;
        local.sin_addr.s_addr = htonl(INADDR_ANY);
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        if((ret = bind(fd, (struct sockaddr *)&local, sizeof(local)))
            != SOCKET_ERROR)
            listen(fd, SOMAXCONN);
        return ret;
    }
    int Accept()
    {
        SOCKET clientfd;
        sockaddr_in client;
        int addrlen = sizeof(client);
        clientfd = accept(fd, (struct sockaddr *)&client, &addrlen);
        if(clientfd == -1)
            return -1;
        clients[cClient].ip = client.sin_addr.S_un.S_addr;
        clients[cClient].port = client.sin_port;
        clients[cClient].StrGen();
        clients[cClient].fd = clientfd; 
        clients[cClient].Valid = true;
        //
        DWORD timeout = 1;
        setsockopt(clients[cClient].fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(DWORD));
        cClient++;
        if(cClient >= max_clients)
        {
            cClient = 0;
            return max_clients - 1;
        }
        return cClient - 1;
    }
    int Connect(char ip[], WORD port)
    {
        sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = inet_addr(ip);
        addr.sin_port = htons(port);
        return connect(fd, (const struct sockaddr*)&addr, sizeof(addr));
    }
    int Send(SOCKET sfd, void* buffer, int length)
    {
        return send(sfd, (char*)buffer, length, 0);
    }
    int Read(SOCKET sfd, void* buffer, int length)
    {
        return recv(sfd, (char*)buffer, length, 0);
    }
};

sslv3 cssl(true);

DWORD WINAPI ReadThread(void* args)
{
    while(true)
    {
        for(int j = 0; j <= cssl.CurrentClient(); j++)
        {
            if(cssl.clients[j].Valid)
            {
                char rpack[1024];
                for(int i = 0; i < sizeof(rpack); i++)
                    rpack[i] = 0;
                if(cssl.Read(cssl.clients[j].fd, rpack, sizeof(rpack)) > 0){
                    printf("%s:%s says: %s\n", cssl.clients[j].ipstr, cssl.clients[j].portstr, rpack);
                }
            }
        }
        Sleep(1);
    }
    return TRUE;
}

int main()
{
    cssl.Bind(1234);
    CreateThread(0,0,ReadThread,0,0,0);
    while(true)
    {
        Sleep(1);
        int cid = cssl.Accept();
        if(cid != -1){
            printf("%s:%s connected!\n", cssl.clients[cid].ipstr, cssl.clients[cid].portstr);
        }
    }
    return 0;
}

The following is a full client code:

#include <WinSock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
#include <Windows.h>
#include <stdio.h>

#include <iostream>
using namespace std;

struct sslv3
{
#define max_clients 1024
private:
    int cClient;
public:
    SOCKET fd;
    int CurrentClient()
    {
        return cClient;
    }
    struct client
    {
        client()
        {
            Valid = false;
        }
        bool Valid;
        DWORD ip;
        WORD port;
        char ipstr[33];
        char portstr[33];
        SOCKET fd;
        void StrGen()
        {
            wsprintf(ipstr, "%d.%d.%d.%d", ip & 0xFF, (ip & 0xFF00)/0x100, (ip & 0xFF0000)/0x10000, (ip & 0xFF000000)/0x1000000);
            wsprintf(portstr, "%d", port);
        }
    } clients[max_clients];
    //
    sslv3(bool server_client)
    {
        WSADATA wsaData;
        WSAStartup(MAKEWORD(2, 2), &wsaData);
        cClient = 0;
        fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        //
        DWORD timeout = 1;
        setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(DWORD));
    }
    int Bind(WORD port)
    {
        int ret = 0;
        sockaddr_in local;
        local.sin_addr.s_addr = htonl(INADDR_ANY);
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        if((ret = bind(fd, (struct sockaddr *)&local, sizeof(local)))
            != SOCKET_ERROR)
            listen(fd, SOMAXCONN);
        return ret;
    }
    int Accept()
    {
        SOCKET clientfd;
        sockaddr_in client;
        int addrlen = sizeof(client);
        clientfd = accept(fd, (struct sockaddr *)&client, &addrlen);
        if(clientfd == -1)
            return -1;
        clients[cClient].ip = client.sin_addr.S_un.S_addr;
        clients[cClient].port = client.sin_port;
        clients[cClient].StrGen();
        clients[cClient].fd = clientfd; 
        clients[cClient].Valid = true;
        //
        DWORD timeout = 1;
        setsockopt(clients[cClient].fd, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(DWORD));
        cClient++;
        if(cClient >= max_clients)
        {
            cClient = 0;
            return max_clients - 1;
        }
        return cClient - 1;
    }
    int Connect(char ip[], WORD port)
    {
        sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_addr.s_addr = inet_addr(ip);
        addr.sin_port = htons(port);
        return connect(fd, (const struct sockaddr*)&addr, sizeof(addr));
    }
    int Send(SOCKET sfd, void* buffer, int length)
    {
        return send(sfd, (char*)buffer, length, 0);
    }
    int Read(SOCKET sfd, void* buffer, int length)
    {
        return recv(sfd, (char*)buffer, length, 0);
    }
};

sslv3 cssl(false);

int main()
{
    cssl.Connect("127.0.0.1", 1234);
    while(true)
    {
        printf("say: ");
        char buf[1024];
        for(int i = 0; i < sizeof(buf); i++)
            buf[i] = 0;
        cin >> buf;
        int len = strlen(buf);
        cssl.Send(cssl.fd, buf, len);
    }
    return 0;
}

Solution

  • The server seems 'idle' for 2 seconds, because some clients are handled after 2 sleeps, 1 second each.

    This is clearly not the right way to handle more than one client on a server. You may want to check on select() - reference.

    A very good tutorial for socket programming is Beej's