recv all socket(直到消息结束符号),C

时间:2017-03-24 17:39:16

标签: c sockets

我想在C中为socket编写recvall函数。我假设我的协议中的每条消息都以\r\n结尾。我在下面写了这样的话:

int recvall (int socket, char *buffer, int numBytes)
{
    int bytesRcvd = 0;
    numBytes = numBytes - 1;

    while(bytesRcvd < numBytes)
    {
        int chunk = recv(socket, buffer + bytesRcvd, numBytes - bytesRcvd, 0);

        if (chunk == 0 || chunk == -1)
            break;

        if(strstr(buffer, "\r\n"))
            break;

        bytesRcvd += (chunk);

    }
    buffer[bytesRcvd] = '\0';
    return bytesRcvd;
}

但它告诉我它返回并读取0个字节。另一方面,当我删除:

if(strstr(buffer, "\r\n"))
            break;
它挂起了。怎么改进呢?

2 个答案:

答案 0 :(得分:3)

这里的一个错误是strstr期望一个以零结尾的字符串。修复:

buffer[chunk] = 0;
if(strstr(buffer, "\r\n"))
    break;

但是,"\r\n"之后可能会有更多数据,并且此处的数据会丢失。

接收数据的常见设计模式是:

  1. 有一个与发送/接收缓冲区保持连接的类。
  2. 当它将数据接收到接收缓冲区时,它会调用一条消息,解析通过bufferbuffer_size的回调。
  3. 消息解析回调消耗缓冲区中所有可用的完整消息,并返回消耗的字节数(就像你一样)。
  4. 解析回调的消息调用另一个回调,向其传递完整的消息。
  5. 这样的事情:

    typedef struct
    {
        int socket;
    
        // Message-parsing callback.
        size_t(*on_recv_cb)(void*, void*, size_t);
        void* on_recv_cb_data;
    
        unsigned char recv_buffer[256 * 1024];
        size_t recv_buffer_size; // Initialized with 0;
    } Connection;
    
    void Connection_on_socket_ready_for_read(Connection* self) {
        // Assumes a non-blocking socket. Read once to avoid starvation of other sockets.
        ssize_t received = read(self->socket, self->recv_buffer + self->recv_buffer_size, sizeof self->recv_buffer - self->recv_buffer_size);
        if(received > 0) {
            self->recv_buffer_size += received;
            size_t consumed = self->on_recv_cb(self->on_recv_cb_data, self->recv_buffer, self->recv_buffer_size);
            self->recv_buffer_size -= consumed;
            memmove(self->on_recv_cb_data, self->recv_buffer + consumed, self->recv_buffer_size);
        }
        else if(received < 0) {
            if(EAGAIN == errno)
                return;
            perror("error");
            // Handle error.
        }
        else {
            // Handle EOF.
        }
    }
    
    typedef struct {
        // Message callback.
        void(*on_message_cb)(void*, char*, size_t);
        void* on_message_cb_data;
    } MessageParserCrLf;
    
    size_t MessageParserCrLf_on_recv(void* cb_data, void* data, size_t data_size) {
        MessageParserCrLf* self = cb_data;
        char* message_begin = data;
        char* message_end = data;
        while(data_size - (message_end - (char*)data) >= 2) {
            if(message_end[0] == '\r' && message_end[1] == '\n') {
                message_end += 2;
                self->on_message_cb(self->on_message_cb_data, message_begin, message_end - message_begin);
                message_begin = message_end;
            }
            else {
                ++message_end;
            }
        }
        return message_begin - (char*)data;
    }
    
    void on_message(void* cb_data, char* message, size_t message_size) {
        (void)cb_data; // Unused here.
        printf("on_message: %.*s\n", (int)message_size, message);
    }
    
    int main() {
        MessageParserCrLf message_parser = {on_message, NULL};
        Connection connection = {0, MessageParserCrLf_on_recv, &message_parser, {}, 0};
        Connection_on_socket_ready_for_read(&connection);
    }
    

    输出:

    [~/src/test]$ cat lines.txt 
    abc
    def
    end
    
    [~/src/test]$ ./test < lines.txt 
    on_message: abc
    
    on_message: def
    
    on_message: end
    

答案 1 :(得分:1)

如果有&#34; \ r \ n&#34;在接收缓冲区中,以下行将中断并结束while循环,

if(strstr(buffer,&#34; \ r \ n&#34;))中断;

但是bytesRcvd没有增加,所以函数将以bytesRcvd = 0返回。

在判断之前尝试将bytesRcvd增加,

bytesRcvd + =(chunk);

if(strstr(buffer,&#34; \ r \ n&#34;))

中断;