使用C语言从TCP套接字未正确接收数据

3
我正在使用Ubuntu 12.04 32位版本。我编写了一个程序,从TCP客户端接收XML文件。同样的程序也通过Unix域套接字从另一个进程接收数据。为此,我使用了poll()系统调用。
我的问题是,有时我无法正确地获取XML数据,或者有时它会丢失。但由于我正在使用TCP,如果有数据丢失,客户端将知道。但客户端没有显示任何错误。请问这是为什么?
我可以提供一些代码:
// 配置服务器TCP
int config_server_tcp(int port)
{   
    int sockfd = -1;
    struct sockaddr_in my_addr;                     // 我的地址信息
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) 
    {
        perror("socket() 失败.");
    }
    else
    {
        my_addr.sin_family = AF_INET;
        my_addr.sin_port = htons(port);
        my_addr.sin_addr.s_addr = htonl(INADDR_ANY);    // 自动填充我的IP
        memset(&(my_addr.sin_zero), 0, 8);              // 清空结构体的其余部分
        if (bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) 
        {
            perror("bind() 失败.");
        }
        else
        {
            if (listen (sockfd, 8) == -1)
            {
                perror("listen() 失败.");
            }
        }
    }
    return sockfd;
}
// 发送TCP消息到服务器 int send_to_tcp_server(unsigned char * message, int size, char * server_ip, int port) { int sockfd; struct sockaddr_in their_addr; int numbytes = -1; if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror("socket() 失败."); } else { their_addr.sin_family = AF_INET; their_addr.sin_port = htons(port); their_addr.sin_addr.s_addr=inet_addr(server_ip); memset(&(their_addr.sin_zero), '\0', 8); // 清空结构体的其余部分 if (connect(sockfd, (struct sockaddr *)&their_addr, sizeof (their_addr)) == -1) { perror("connect() 失败."); } else { if ((numbytes=send(sockfd , message, size, 0)) == -1) { printf ("发送失败.\n"); } } close (sockfd); } return numbytes; }
// TCP处理函数,在循环中被调用 void process_tcp (int sock) { struct sockaddr_in their_addr; // 对方的地址信息 int received; socklen_t addr_len; char buffer[BUFF_SIZE];
addr_len = sizeof (their_addr); int clientfd = accept (sock, (struct sockaddr *)&their_addr, &addr_len); // 接受客户端连接请求 if (clientfd == -1) { perror("accept() 失败."); } else { do { received = recv(clientfd, buffer, BUFF_SIZE, 0); // 接收数据 if (received == -1) { perror("recv() 失败."); break; } else { //做些什么 } } while (received != 0); close (clientfd); // 关闭客户端连接 } }

process_tcp 函数在循环中被调用。


6
请展示你的代码,很可能你正在将TCP视为数据报协议(这是错误的),但如果没有代码,我们无法确定。 - KillianDS
1
你上面的代码缺少组装消息的代码和处理整个消息的代码。你已经用 //do something 替换了组装代码,而处理消息的代码必须放在 while 循环结束后(因为关闭连接是表示应用程序级别消息结束的方式)。代码中最关键的两个部分都缺失了。 - David Schwartz
2
@Hari: 可能是的。那是最关键的代码。那里你必须实现你的“XML over TCP”协议。Bug 很可能在你的“XML over TCP”协议设计或实现中。 - David Schwartz
1
@KarolyHorvath:他发布了他的代码。这就是他的做法。if ((numbytes=send(sockfd , message, size, 0)) == -1) { printf ("发送失败。\n"); } close (sockfd); - David Schwartz
1
@Hari:那是你的问题。你有应用程序消息,但没有应用程序协议。只有应用程序协议才能提供应用程序消息。所以你基本上是期望它通过魔法工作。 - David Schwartz
显示剩余14条评论
1个回答

5
这个bug几乎肯定出现在您没有展示的代码中,也就是按照您的XML-over-TCP协议组装应用程序级别消息的代码。下面是一种实现方式:
void process_tcp (int sock)
{
    struct sockaddr_in their_addr;
    int received, total_received;
    socklen_t addr_len;
    char buffer[BUFF_SIZE];

    addr_len = sizeof (their_addr);
    int clientfd = accept (sock, (struct sockaddr *)&their_addr, &addr_len);
    if (clientfd == -1)
    {
        perror("accept() failed.");
    }       
    else
    {
        total_received = 0;
        do
        {
            received = recv(clientfd, buffer + total_received,
                            BUFF_SIZE - total_received, 0);
            if (received == -1) 
            {
                perror("recv() failed.");
                break;
            }
            if (received > 0)
                total_received += received;
        }
        while (received != 0);
        buffer[total_received] = 0;
        // here we can do something with 'buffer'              
        close (clientfd);
    }
}

请注意,缺少很多错误检查。

谢谢大卫。我会尝试这个代码。 - Harikrishnan
很抱歉,它仍然没有接收到正确的数据。 - Harikrishnan
你能描述一下它接收到了什么,以及哪些方面是不正确的吗? - David Schwartz
好的。我第一次发送XML时,间隔为10秒,没有出现任何错误。之后,我以大约1秒的间隔发送它,它接收到了一部分数据,然后是最后一部分数据,然后是中间的一些部分。正如我在问题中所说的那样,我正在使用poll()从UNIX域套接字接收数据。这会引起什么问题吗? - Harikrishnan
1
哦,我觉得我知道是什么了。你的套接字是非阻塞的,对吧?如果是这样,你需要一个每个套接字应用程序消息缓冲区。否则,你就没有地方组装应用程序消息了。我上面的代码适用于阻塞套接字。但对于非阻塞的套接字,你需要把应用层协议状态保存在某个地方。 - David Schwartz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接