当与服务器的连接超过1个时,c++tcp服务器(windows操作系统)-recv()会延迟

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

本文关键字:服务器 操作系统 windows -recv 延迟 c++tcp 连接 1个时      更新时间:2023-10-16

当我试图连接到只有一个客户端的服务器时,服务器上的recv()函数不会延迟。

但是,当我启动客户端控制台超过1次(大约7次)时,在向服务器发送带有send()功能的数据包后,会有大约2000毫秒的延迟,直到服务器将数据包打印到控制台中。

有没有任何解决方案不为每个客户端启动一个线程?(Windows限制每个进程的线程数)。

该代码是用Visual Studio 2008编译的,这是完整的服务器代码:

#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: %sn", 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;
}

以下是完整的客户端代码:

#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;
}

服务器似乎"空闲"了2秒,因为一些客户端在2个sleep秒后处理,每个客户端1秒。

这显然不是在服务器上处理多个客户端的正确方法。您可能需要查看select()-参考。

一个非常好的套接字编程教程是Beej的