简体   繁体   English

C非阻塞发送不起作用

[英]C non-blocking send not working

I'm trying to send the large amount of data to the server which should accept the data and parse it. 我正在尝试将大量数据发送到服务器,该服务器应该接受数据并对其进行解析。 So as I know, when you send() the data in blocking mode in one call, it splits data into chunks and then sends the chunks to the target. 因此,据我所知,当您在一次调用中以阻塞模式send()数据时,它将数据拆分成多个块,然后将这些块发送到目标。 But I need to mark each chunk with a small identifier in the beginning of the data (let's say I'm placing a header in each chunk), so I decided to use non- blocking send. 但是我需要在数据的开头用小标识符标记每个块(假设我在每个块中放置一个标头),因此我决定使用非阻塞发送。 I thought, when I do non-blocking send, it sends the max the buffer allows and then returns, leaving the chunking work for me, but it seems that's not happening. 我以为,当我执行非阻塞发送时,它将发送缓冲区允许的最大值,然后返回,从而为我留下了分块工作,但似乎没有发生。

My code is: 我的代码是:

    struct sockaddr_in target;

    SOCKET connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
    target.sin_family = AF_INET;
    target.sin_addr.s_addr = inet_addr(host);
    target.sin_port = htons(port);

     ULONG NonBlock;
     NonBlock = 1;
     if (ioctlsocket(connection, FIONBIO, &NonBlock) == SOCKET_ERROR)
     {

         return WSAGetLastError();

     }

      fd_set write_fds;

        FD_ZERO(&write_fds);            
        FD_SET(connection, &write_fds);
        struct timeval tv;
        tv.tv_sec=1;
        tv.tv_usec=0;
    int result = connect(connection,(SOCKADDR*)&target,sizeof(target));
    if(result==SOCKET_ERROR)
    {
        while(true)
        {
            result= select(connection+1,NULL,&write_fds,NULL,&tv);
            printf("connect: result=%d\r\n",result);
            if(result== -1)
            {
                return WSAGetLastError();
            }
            else break;
        }

    }

    //later on

fd_set write_set;
int bytes_sent= 0;
int total_sent = 0;
int length = 0;
char *tmp = malloc(sizeof(header)+data_size); //data_size is the size of the large buffer
memcpy(tmp,packet,sizeof(header));
memcpy(tmp+sizeof(header),data,data_size);


int result;

FD_ZERO(&write_set);
FD_SET(connection,&write_set);

struct timeval time_out;

time_out.tv_sec=0;
time_out.tv_usec=1500;

while(total_sent < data_size)
{
    length= (data_size+sizeof(my_header))-total_sent;

    result = select(connection+1,NULL,&write_set,NULL,&time_out);

    if(result== SOCKET_ERROR) return -1;
    if(result!=0 && FD_ISSET(connection, &write_set))
    {
        bytes_sent = send(connection,tmp,length,0);
    }

    if(bytes_sent == SOCKET_ERROR)
    {
        return SOCKET_ERROR;
    }
    if(bytes_sent > 0)
    {
        //here i need to append a header to the new chunk
    }
    else break;


}

So basically my question is: why the send on non-blocking socket, still blocks and doesn't return after sending the first chunk, and acts just like regular blocking send? 所以基本上我的问题是:为什么在非阻塞套接字上发送时,仍然在发送第一个块后仍然阻塞并且不返回,并且行为就像常规阻塞发送一样? What i want to achieve is send() sending one chunk of data of the length that the system allows, so i put the length of the whole data, assuming that non-blocking send will return after sending the first chunk, because the buffer is to big, to be sent as one block. 我要实现的是send()发送一个系统允许的长度的数据块,因此,我将整个数据的长度放在一旁,假设非阻塞发送将在发送第一个块后返回,因为缓冲区是大,作为一个块发送。

UPDATE some runnable code: 更新一些可运行的代码:

#include <windows.h>
#include <winsock.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <types.h>

typedef struct hdr{
 uint8_t super_id;
}my_header,*pmy_header;

SOCKET connection;
int start_winsock()
{
    WSADATA check;

    int result = WSAStartup(MAKEWORD(2,2),&check);  
    return result;
}
int create_connection(char* host,int port)
{
     struct sockaddr_in target;

    connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 
    target.sin_family = AF_INET;
    target.sin_addr.s_addr = inet_addr(host);
    target.sin_port = htons(port);
    int result =    UnblockSocket();
    if(result!=0) return WSAGetLastError();

        fd_set write_fds;
        FD_ZERO(&write_fds);            
        FD_SET(connection, &write_fds);
         struct timeval tv;

        tv.tv_sec=1;
        tv.tv_usec=0;

        result = connect(connection,(SOCKADDR*)&target,sizeof(target));
        if(result==SOCKET_ERROR)
        {
          while(true)
          {
            result= select(connection+1,NULL,&write_fds,NULL,&tv);
            if(result== -1)
            {
                 return WSAGetLastError();
            }
             else break;
        }

    }

    return 0;

}


 int UnblockSocket()
 {
   ULONG NonBlock;
   NonBlock = 1;
   if (ioctlsocket(connection, FIONBIO, &NonBlock) == SOCKET_ERROR)
   {

      return WSAGetLastError();

   }
    return 0;
 }



 int SendMyData(pmy_header header,char * data,int data_size)
 {

    fd_set write_set;
    int bytes_sent= 0;
    int total_sent = 0;
    int length = 0;
    char *tmp = malloc(sizeof(my_header)+data_size);
    memcpy(tmp,packet,sizeof(my_header));
    memcpy(tmp+sizeof(my_header),data,data_size);

    int result;

    FD_ZERO(&write_set);
    FD_SET(connection,&write_set);

    struct timeval time_out;

    time_out.tv_sec=0;
    time_out.tv_usec=1500;

    header->super_id=0xdead;
    while(total_sent < data_size)
    {
        length= (data_size+sizeof(my_header))-total_sent;

        if(result== SOCKET_ERROR) return -1;
        if(result!=0 && FD_ISSET(connection, &write_set))
        {
            bytes_sent = send(connection,tmp,length,0);
        }

            printf("bytes sent per iteration=%d\n",bytes_sent);
        if(bytes_sent == SOCKET_ERROR)
        {
            return SOCKET_ERROR;
        }
        if(bytes_sent > 0)
        {
         total_sent+= bytes_sent-sizeof(my_header);

         tmp = realloc(tmp,sizeof(my_header)+(data_size-total_sent));


         memcpy(tmp,header,sizeof(my_header));
         memcpy(tmp+sizeof(my_header),data,data_size-total_sent);
        }
        else break;


    }
    free(tmp);
    return total_sent;
}

int main(int argc, char *argv[])
{
     start_winsock();
     int result = create_connection("2.2.2.2",88);
     if(result!=0) { printf("Cannot connect\n"); return 0; }

     pmy_header *header = malloc(sizeof(my_header));

     int buf_size = 500000;
     char buffer_test[buf_size];
     ZeroMemory(buffer_test,buf_size);
     int count=0;
     for(count;count<buf_size;count++)
     {
       strcat(buffer_test,"4");
     }
     result = SendMyData(header,buffer_test,buf_size);

} 

send() is not guaranteed to send everything you ask it to send. send()不能保证发送您要求发送的所有内容。 It may send less. 它可能发送较少。 You MUST take the return value into account. 您必须考虑返回值。 If it is less than the amount you requested, you have to call send() again to re-send the remaining bytes, before then sending new bytes. 如果小于所需的数量,则必须再次调用send()重新发送剩余的字节,然后再发送新的字节。 And in the case of non-blocking, you have to take WSAEWOULDBLOCK into account as well. 而且在非阻塞的情况下,您还必须考虑WSAEWOULDBLOCK

And you don't put on a header on each chunk that send() sends. 而且,您不必在send()发送的每个块上都放置标题。 You put a header on each chunk you tell send() to send. 你把一个头在每个块告诉send()来发送。 You do your own chunking, don't worry about the chunking that TCP does internally. 您自己进行分块,不必担心TCP在内部进行的分块。 That is a network implementation, it does not affect your protocol. 那是一个网络实现,它不会影响您的协议。 The receiver should be paying attention to your chunk headers, calling recv() as many times as needed to receive your full header and data to account for TCPs chunking. 接收方应注意您的块头,根据需要多次调用recv()来接收完整的头和数据,以解决TCP块化问题。

Try something more like this instead: 尝试类似这样的方法:

SOCKET connection = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
if (connection == INVALID_SOCKET)
{
     return WSAGetLastError();
}

ULONG NonBlock = 1;
in result = ioctlsocket(connection, FIONBIO, &NonBlock);
if (result == SOCKET_ERROR)
{
    result = WSAGetLastError();
    closesocket(connection);
    return result;
}

struct sockaddr_in target;
memset(&target, 0, sizeof(target));
target.sin_family = AF_INET;
target.sin_addr.s_addr = inet_addr(host);
target.sin_port = htons(port);

result = connect(connection, (SOCKADDR*)&target, sizeof(target));
if (result == SOCKET_ERROR)
{
    result = WSAGetLastError();
    if (result != WSAEWOULDBLOCK)
    {
        closesocket(connection);
        return result;
    }

    fd_set write_fds;
    FD_ZERO(&write_fds);            
    FD_SET(connection, &write_fds);

    struct timeval tv;
    tv.tv_sec = 5;
    tv.tv_usec = 0;

    result = select(0, NULL, &write_fds, NULL, &tv);
    if (result == SOCKET_ERROR)
    {
        result = WSAGetLastError();
        closesocket(connection);
        return result;
    }

    if (result == 0)
    {
        closesocket(connection);
        return WSAETIMEDOUT;
    }
}

char *tmp_data = data;
int data_remaining = data_size;

while (data_remaining > 0)
{
    int pkt_data_size = min(data_remaining, 1024); // replace 1024 with whatever maximum chunk size you want...

    int pkt_size = sizeof(header) + pkt_data_size;
    char *pkt = malloc(pkt_size);
    if (!pkt) return -1;

    // fill header as needed...
    memcpy(pkt+sizeof(header), tmp_data, pkt_data_size);

    tmp_data += pkt_data_size;
    data_remaining -= pkt_data_size;

    char *tmp_pkt = pkt;
    while (pkt_size > 0)
    {
        result = send(connection, tmp_pkt, pkt_size, 0);
        if (result == SOCKET_ERROR)
        {
            result = WSAGetLastError();
            if (result != WSAEWOULDBLOCK)
            {
                free(pkt);
                return -1;
            }

            fd_set write_set;
            FD_ZERO(&write_set);
            FD_SET(connection, &write_set);

            struct timeval time_out;
            time_out.tv_sec = 5;
            time_out.tv_usec = 0;

            result = select(0, NULL, &write_set, NULL, &time_out);
            if (result != 1)
            {
                free(pkt);
                return -1;
            }

            continue;
        }

        tmp_pkt += result;
        pkt_size -= result;
    }

    free(pkt);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM