在TCP套接字程序中,客户端发送一些数据,但服务器需要多次读取。为什么?

时间:2016-09-05 08:12:24

标签: c sockets tcp

我有一个关于socket的问题。我从客户端向服务器发送N大小的数据,N大小小于100字节。所以我认为我的数据不应该拆分成多个tcp包。在我看来,客户端发送数据应该一次完成,服务器可以一次接收数据。但结果并不令人满意。实际情况是服务器需要调用读取数据。我不明白。关注代码:

epoll_server.cpp(仅接收数据。)

#include <sys/socket.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include <netdb.h>

#define BUFSIZE 1024
#define INITSIZE 1024
#define MAXEVENTCOUNT 10240

// add non-blocking to sockfd
int make_socket_non_blocking(int fd)
{
    // get initial flag
    int src_flags;
    src_flags= fcntl(fd, F_GETFL,0);           
    if(src_flags == -1)
    {                    
        perror("fcntl get error.");
        return-1;
    }

    // add non-blocking
    int new_flags = src_flags | O_NONBLOCK;
    int ret_value;
    ret_value = fcntl(fd, F_SETFL, new_flags);
    if(ret_value == -1)
    {
        perror("fcntl set error.");
        return-1;
    }

    return 0;
}


// main function
int main(int argc, char* argv[])
{
    int server_sockfd, client_sockfd;
    int server_len;
    struct sockaddr_in server_address;

    // create server socket fd
    server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
    // init server address struct
    bzero(&server_address, sizeof(server_address));
    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(9567);
    server_address.sin_addr.s_addr = INADDR_ANY;
    server_len = sizeof(server_address);

    // bind server address info for server fd
    if((bind(server_sockfd, (struct sockaddr*)&server_address, server_len)) == -1)
    {
        perror("bind error");
        exit(EXIT_FAILURE);
    }

    // let server is listened state
    listen(server_sockfd, 5);
    printf("server start waiting for connect...\r\n");

    // only suggestion
    int efd = epoll_create(INITSIZE);
    if(-1 == efd)
    {
        printf("epoll_create error happen.\n");
        return -1;
    }

    // set server_sockfd
    struct epoll_event server_event, event;
    server_event.data.fd = server_sockfd;
    server_event.events = EPOLLIN | EPOLLET;
    int ret_epollctl = epoll_ctl(efd, EPOLL_CTL_ADD, server_sockfd, &server_event);
    if(-1 == ret_epollctl)
    {
        printf("epoll_ctl error happen when efd is adding server_sockfd.\n");
        return -1;
    }

    /* event loop */
    struct epoll_event* return_events;
    // set timeout is 3000 ms
    int timeout_msecond = 3000;    
    return_events = (struct epoll_event*)malloc(MAXEVENTCOUNT*sizeof(struct epoll_event));
    int count = 0;
    while(1)
    {
        int ret_epollwait = epoll_wait(efd, return_events, MAXEVENTCOUNT, timeout_msecond);
        // part_1:epoll_wait error happen
        if(-1 == ret_epollwait)
        {
            printf("logged epoll_wait error happen.\n");
            continue;
        }
        // part_2:epoll_wait timeout
        if(0 == ret_epollwait)
        {
            printf("logged epoll_wait timeout.\n");
            continue;
        }
        // part_3:do some other event
        int index = 0;
        for(index = 0; index < MAXEVENTCOUNT; index++)
        {
            // part_3-1:hup ...
            if((return_events[index].events & EPOLLERR)
             || (return_events[index].events & EPOLLHUP)
             || !(return_events[index].events & EPOLLIN) )
            {                
                continue;
            }

            // part_3-2:is connection
            if(return_events[index].data.fd == server_sockfd)
            {
                struct sockaddr_in client_address;
                int client_len = sizeof(client_address);
                // server accept connection from client
                int client_sockfd = accept(server_sockfd, (struct sockaddr*)&client_address, (socklen_t*)&client_len);
                // part_3-2-1:connection error happen
                if(-1 == client_sockfd)
                {
                    if((EAGAIN == errno)
                     || (EWOULDBLOCK == errno) )
                    {                        
                        continue;
                    }
                    else
                    {
                        printf("accept error occured.\n");
                        continue;
                    }
                }
                else // part_3-2-2:normal connection
                {
                    // get clinet some information
                    char hostinfo_buf[BUFSIZE] = {0};
                    char servname_buf[BUFSIZE] = {0};
                    int tmp_ret = getnameinfo((struct sockaddr*)&client_address, client_len, hostinfo_buf, sizeof(hostinfo_buf), servname_buf, sizeof(servname_buf), NI_NUMERICHOST| NI_NUMERICSERV);
                    if(0 == tmp_ret)
                    {
                        printf("Accepted connection on descriptor %d:ip=%s, port=%s.\n", client_sockfd, hostinfo_buf, servname_buf);
                    }
                    // set client_sockfd to non-blocking
                    tmp_ret = make_socket_non_blocking(client_sockfd);
                    if(-1 == tmp_ret)
                    {
                        printf("set client_sockfd=%d to non-blocking error occured.\n", client_sockfd);
                        abort();
                    }

                    // set client_sockfd is EPOLLIN, EPOLLET
                    event.data.fd = client_sockfd;
                    event.events = EPOLLIN | EPOLLET;
                    tmp_ret = epoll_ctl(efd, EPOLL_CTL_ADD, client_sockfd, &event);
                    if(tmp_ret == -1)
                    {
                        printf("efd add %d has a error.\n", client_sockfd);
                        continue;
                    }
                    printf("add descriptor %d:ip=%s, port=%s successfully.\n", client_sockfd, hostinfo_buf, servname_buf);
                }
                continue;
            }

            // part_3-3:read data from client            
            printf("read data start++++\n");
            int temp = 0;

            // get recv_cache size start
            int recvsize = 0;
            socklen_t optlen = sizeof(recvsize);
            int err = getsockopt(return_events[index].data.fd, SOL_SOCKET, SO_RCVBUF, &recvsize, &optlen);
            printf("recv cache size :%d\n", recvsize);
            // get recv_cache size end

            while(1) // start while(1)
            {
                printf("%d times read data\n", ++temp);
                char* recv_buffer = (char*)malloc(1024+1);
                memset(recv_buffer, 0, 1025);
                // int ret_read = read(return_events[index].data.fd, recv_buffer, sizeof(recv_buffer));
                int ret_read = recv(return_events[index].data.fd, recv_buffer, sizeof(recv_buffer), 0);
                // part_3-3-1:read return error
                if(-1 == ret_read)
                {
                    if(EAGAIN != errno)
                    {
                        printf("read data from %d error occured, errno=%d, %s.\n", return_events[index].data.fd, errno, strerror(errno));
                    }
                    break;
                }
                // part_3-3-2:no data
                if(0 == ret_read)
                {
                    continue;
                }
                // part_3-3-3:output data. If data is 'bye', connection will close.
                if(ret_read > 0)
                {
                    printf("%d client's data:size=%dbyte, content=%s\n", return_events[index].data.fd, ret_read, recv_buffer);
                    // part_3-3-3-1:close connection and remove client_sockfd
                    if((recv_buffer[0] == 'b') 
                    && (recv_buffer[1] == 'y')
                    && (recv_buffer[2] == 'e') )
                    {

                        close(return_events[index].data.fd);
                        printf("close %d, ", return_events[index].data.fd);
                        int tmp_ret = epoll_ctl(efd, EPOLL_CTL_DEL, return_events[index].data.fd, NULL);
                        if(tmp_ret == -1)            
                        {     
                            printf("efd del %d has a error.\n", client_sockfd);        
                        }     
                        printf("remove descriptor %d successfully.\n", return_events[index].data.fd);
                    }
                }
            } // end of while(1)

            printf("read data finish------\n");
        }
    }

    free(return_events);
    // close server_sockfd
    shutdown(server_sockfd, 2);

    return 0;
}

epoll_client.cpp(仅发送数据。)

#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>

#define BUFSIZE 1024

int main(int argc, char* argv[])
{
    int sock_clientfd, ret_recvsize, i;
    struct sockaddr_in dest, mine;
    char send_buffer[BUFSIZE + 1];

    // create socket fd
    if ((sock_clientfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("Socket");
        exit(EXIT_FAILURE);
    }

    // init server address that client will connetct to.
    bzero(&dest, sizeof(dest));
    dest.sin_family = AF_INET;
    dest.sin_port = htons(9567);
    if(argc != 2)
    {
        printf("Usage: %s <dest ip>\n", argv[0]);
        printf("Usage: %s 127.0.0.1\n", argv[0]);
        return -1;
    }

    printf("-----\n");

    if (inet_aton(argv[1], (struct in_addr *) &dest.sin_addr.s_addr) == 0)
    {
        perror(argv[1]);
        exit(1);
    }

    // connect to server
    printf("will connect!\n");
    if (connect(sock_clientfd, (struct sockaddr *) &dest, sizeof(dest)) != 0)
    {
        perror("Connect ");
        exit(EXIT_FAILURE);
    }

    while(1)
    {
        bzero(send_buffer, BUFSIZE + 1);
        printf("input message:");
        fgets(send_buffer, BUFSIZE, stdin);
        send_buffer[strlen(send_buffer) - 1] = '\0';
        printf("%d\n", strlen(send_buffer));
        int send_retsize = send(sock_clientfd, send_buffer, strlen(send_buffer), 0);
        if(send_retsize == -1)
        {                                
            perror("send data to client error happen!");
            exit(EXIT_FAILURE);          
        }
        printf("send succ data:%s\n", send_buffer);
        if((send_buffer[0] == 'b')
         && (send_buffer[1] == 'y')
         && (send_buffer[2] == 'e') ) 
        {
            printf("client active close connect.\n");
            break;
        }
    }

    // close sock_clientfd
    close(sock_clientfd);

    return 0;
}

关注pircture是一些运行信息: epoll_server.png enter image description here

epoll_client.png enter image description here

服务器读取数据只有8个字节,内核设计epoll是这个吗? 我想原因如下: enter image description here

1 个答案:

答案 0 :(得分:3)

您没有收到一次读取中可用的所有内容的原因是因为您一次只能读取8个字节。

char* recv_buffer = (char*)malloc(1024+1);
int ret_read = recv(return_events[index].data.fd, recv_buffer, sizeof(recv_buffer), 0);
            // part_3-3-1:read return error

recv_bufferchar*而非数组,因此sizeof recv_buffer等于指针的大小,在您的情况下为8

请注意,您永远不应该依赖包裹中的数据。如果您的消息协议声明您应该获得10个字节,则永远不要期望所有10个字节一次可用。您应始终以能够处理分成多个读取的数据的方式进行编码。

如果线程处理单个套接字,那么简单的do { read... } while (total_bytes_received < expected_bytes);就足够了。

如果线程处理多个连接,则需要保存已读取的字节,然后继续管理其他已准备好的套接字,然后返回到将使用select/epoll等待更多数据的处理循环。