使用TCP,如果客户端发送请求太快,服务端出现接收数据出错的情况

时间:2021-09-14 18:08:33
如程序上传文件,如果上传2G的文件分块4K一个进行上传,上传太快出现后面数据出错,上传端上延时不会出错, 为什么,解决方法

11 个解决方案

#1


处理好粘包问题就不会出错了。
QQ: 84162092

#2


4K有些大吧,1K试试。

#3


你按顺序发的话是不会有问题的,需要注意几点
1。客户端要保证下一次发送数据前,前一次发送的数据已经成功送出
2。分块不要分得太大,最好控制在1K左右
3。如果必要,可以采用 数据报长度+数据报内容来防止拈包

#4


谢谢大,如何处理这个问题
jebbthe(青苹果)
如果保证 客户端要保证下一次发送数据前,前一次发送的数据已经成功送出
是使用数据报长度+数据报内容 =1K吗?

#5


发送端缓冲满了,而导致数据出错吧?

#6


我设置为1k还是不行,有和我相同问题的人吗

#7


除非在局域网,否则连续发送包肯定数据错乱

#8


1.系统支持的最大的数据报你可以调用GetSockOpt来获得,我说的1K指的就是系统支持的最大的数据报长度。
2.一定得记得判断send的结果,很多粗心的程序员会忽略这点。
3. 确定你的程序到底是发送端有问题还是接收方有问题,我更怀疑是你的接收方可能有bug。
4.还是那个粘包问题,我不知道你的程序机制是怎么样的,会不会有这个问题得由你自己来判断了:)

#9


我也是有这种情况 采用将数据限制在1K也不行 也是苦恼的不行

#10


你是编程思路不对,把流格式的的TCP当成数据包格式的UDP来用了,一般做法是这样(写代码比较直观):

struct packet_t
{
    int len;
    int cmd;
    char data[1];
};

class peer_t
{
    ....
    // 两个缓存区,其中 buffer_t 是自增长
    buffer_t m_recvbuffer;
    buffer_t m_sendbuffer;
    
    bool is_alive();
    void stop();
    
    void on_read();
    
    void on_write()
    {
        handle_sendbuffer();
    }
    ...
    
    void send_data(void* data, int len)
    {
        // 都先放到发送缓存区
        m_sendbuffer.write(data, len);
        
        handle_sendbuffer();
    }
    
    void handle_recvbuffer();
    void handle_sendbuffer();
};

peer_t::on_read()
{
    char buf[4096];
    int recv_len = recv(....buf, sizeof(buf));
    
    if (recv_len > 0)
    {
        // 不管3721,都接收回缓存区再说,然后从缓存区去数据处理
        m_recvbuffer.write(buf, recv_len);
        handle_recvbuffer();
    } else if ...
}

peer_t::handle_recvbuffer()
{
    while (is_alive())
    {
        char* recv_buf = m_recvbuffer.buffer();
        int   recv_len = m_recvbuffer.length();
        
        packet_t* packet = (packet_t*) recv_buf;
        
        // 检查是否收够完整的一个包
        if (recv_len < 4 /* sizeof(packet_t.len) */) break;
        if (packet->len > MAX_PACKET_SIZE)
        {
            // 太大的包,可能是攻击包,或错误的包,不接受
            stop();
            break;
        }
        if (recv_len < 4 + packet->len) break;
        
        // 处理一个包
        process_packet(packet);
        
        // 把已处理的包清除出缓存区
        m_recvbuffer.descard(4 + packet->len);
    }   
}

peer_t::handle_sendbuffer()
{
    while (is_alive())
    {
        char* buf_ptr = m_sendbuffer.buffer();
        int   buf_len = m_sendbuffer.length();
        
        if (buf_len < 1) break;
        
        // 尽量发,能发多少就多少,剩下的下次再发
        int send_len = send(...buf_ptr, buf_len);
        
        if (send_len > 0)
        {
            // 把已发的清除出缓存区
            m_sendbuffer.descard(send_len);
        } else if ....
        
    }   
}

当然如果你是发送文件,一般做法是先发一个包头,然后源源不断的发文件数据,那就是另一个做法了,因为上面的代码是最起码收完一个完整包到缓存区才分析运作,而且限制了最大包头长度。稍为改一下就可以。

#11


谢谢大家,我以解决这个问题,是接收端的问题,我的程序会处理拈包问题,所以不会在这个问题上,最后确定SOCKET的接收函数revc使用阻塞方式,要求收1024可是收了640就返回了,

#1


处理好粘包问题就不会出错了。
QQ: 84162092

#2


4K有些大吧,1K试试。

#3


你按顺序发的话是不会有问题的,需要注意几点
1。客户端要保证下一次发送数据前,前一次发送的数据已经成功送出
2。分块不要分得太大,最好控制在1K左右
3。如果必要,可以采用 数据报长度+数据报内容来防止拈包

#4


谢谢大,如何处理这个问题
jebbthe(青苹果)
如果保证 客户端要保证下一次发送数据前,前一次发送的数据已经成功送出
是使用数据报长度+数据报内容 =1K吗?

#5


发送端缓冲满了,而导致数据出错吧?

#6


我设置为1k还是不行,有和我相同问题的人吗

#7


除非在局域网,否则连续发送包肯定数据错乱

#8


1.系统支持的最大的数据报你可以调用GetSockOpt来获得,我说的1K指的就是系统支持的最大的数据报长度。
2.一定得记得判断send的结果,很多粗心的程序员会忽略这点。
3. 确定你的程序到底是发送端有问题还是接收方有问题,我更怀疑是你的接收方可能有bug。
4.还是那个粘包问题,我不知道你的程序机制是怎么样的,会不会有这个问题得由你自己来判断了:)

#9


我也是有这种情况 采用将数据限制在1K也不行 也是苦恼的不行

#10


你是编程思路不对,把流格式的的TCP当成数据包格式的UDP来用了,一般做法是这样(写代码比较直观):

struct packet_t
{
    int len;
    int cmd;
    char data[1];
};

class peer_t
{
    ....
    // 两个缓存区,其中 buffer_t 是自增长
    buffer_t m_recvbuffer;
    buffer_t m_sendbuffer;
    
    bool is_alive();
    void stop();
    
    void on_read();
    
    void on_write()
    {
        handle_sendbuffer();
    }
    ...
    
    void send_data(void* data, int len)
    {
        // 都先放到发送缓存区
        m_sendbuffer.write(data, len);
        
        handle_sendbuffer();
    }
    
    void handle_recvbuffer();
    void handle_sendbuffer();
};

peer_t::on_read()
{
    char buf[4096];
    int recv_len = recv(....buf, sizeof(buf));
    
    if (recv_len > 0)
    {
        // 不管3721,都接收回缓存区再说,然后从缓存区去数据处理
        m_recvbuffer.write(buf, recv_len);
        handle_recvbuffer();
    } else if ...
}

peer_t::handle_recvbuffer()
{
    while (is_alive())
    {
        char* recv_buf = m_recvbuffer.buffer();
        int   recv_len = m_recvbuffer.length();
        
        packet_t* packet = (packet_t*) recv_buf;
        
        // 检查是否收够完整的一个包
        if (recv_len < 4 /* sizeof(packet_t.len) */) break;
        if (packet->len > MAX_PACKET_SIZE)
        {
            // 太大的包,可能是攻击包,或错误的包,不接受
            stop();
            break;
        }
        if (recv_len < 4 + packet->len) break;
        
        // 处理一个包
        process_packet(packet);
        
        // 把已处理的包清除出缓存区
        m_recvbuffer.descard(4 + packet->len);
    }   
}

peer_t::handle_sendbuffer()
{
    while (is_alive())
    {
        char* buf_ptr = m_sendbuffer.buffer();
        int   buf_len = m_sendbuffer.length();
        
        if (buf_len < 1) break;
        
        // 尽量发,能发多少就多少,剩下的下次再发
        int send_len = send(...buf_ptr, buf_len);
        
        if (send_len > 0)
        {
            // 把已发的清除出缓存区
            m_sendbuffer.descard(send_len);
        } else if ....
        
    }   
}

当然如果你是发送文件,一般做法是先发一个包头,然后源源不断的发文件数据,那就是另一个做法了,因为上面的代码是最起码收完一个完整包到缓存区才分析运作,而且限制了最大包头长度。稍为改一下就可以。

#11


谢谢大家,我以解决这个问题,是接收端的问题,我的程序会处理拈包问题,所以不会在这个问题上,最后确定SOCKET的接收函数revc使用阻塞方式,要求收1024可是收了640就返回了,