从x86发送到x64时,C套接字卡在读取状态

时间:2013-12-01 02:55:19

标签: c linux sockets

我正在为课堂项目编写一个小型电子邮件服务器/客户端。当我在同一台机器上运行客户端和服务器时,一切正常。当我在x86机器上运行服务器和x64机器上的客户端时,我陷入了阅读困境。当我在x86机器上的x64机器和客户机上运行服务器时,读取完成,但是当我打印字符串时,它的空白。以下是我用来阅读的功能

/*----------------------------------------------------------------*/
int readn(int sd, char *buf, int n) {
    printf("readn via utils. %d, %s, %d\n", sd, buf, n);
    int toberead;
    char * ptr;

    toberead = n;
    ptr = buf;
    while (toberead > 0) {
        int byteread;
        fprintf(stderr, "toberead: %d, byteread: %d\n",toberead, byteread );
        byteread = read(sd, ptr, toberead);
        fprintf(stderr, "toberead: %d, byteread: %d\n",toberead, byteread );

        if (byteread <= 0) {
            fprintf(stderr, "byteread val: %d",byteread);
            if (byteread == -1)
                perror("read");
            raise (6);
            return (0);
        }

        toberead -= byteread;
        ptr += byteread;
    }

    fprintf(stderr, "Finished readn. %s\n", buf);
    return (1);
}

对readn

使用或影响的其他实用功能

/*----------------------------------------------------------------*/

Packet *recvpkt(int sd)
{
    printf("Recvpkt via utils.\n");
    Packet *pkt;

    /* allocate space for the pkt */
    pkt = (Packet *) calloc(1, sizeof(Packet));
    if (!pkt) {
        fprintf(stderr, "error : unable to calloc\n");
        return(NULL);
    }

    /* read the message type */
    if (!readn(sd, (char *) &pkt->type, sizeof(pkt->type))) {
        free(pkt);
        return(NULL);
    }

    /* read the message length */
    if (!readn(sd, (char *) &pkt->lent, sizeof(pkt->lent))) {
        free(pkt);
        return(NULL);
    }
    pkt->lent = ntohl(pkt->lent);

    /* allocate space for message text */
    if (pkt->lent > 0) {
        pkt->text = (char *) malloc(pkt->lent);
        if (!pkt) {
            fprintf(stderr, "error : unable to malloc\n");
            return(NULL);
        }

        /* read the message text */
        if (!readn(sd, pkt->text, pkt->lent)) {
            freepkt(pkt);
            return(NULL);
        }
    }

    fprintf(stderr, "Reading packet complete succesfully.\n");

    /* done reading */
    return(pkt);
}

int sendpkt(int sd, char typ, long len, char *buf)
{
    fprintf(stderr, "Send packet via utils.\n");
    char tmp[8];
    long siz;

    /* write type and lent */
    bcopy(&typ, tmp, sizeof(typ));
    siz = htonl(len);
    bcopy((char *) &siz, tmp+sizeof(typ), sizeof(len));
    write(sd, tmp, sizeof(typ) + sizeof(len));

    /* write message text */
    if (len > 0)
        write(sd, buf, len);
    return(1);
}

void freepkt(Packet *pkt)
{
    fprintf(stderr, "Freeing packet.\n");
    free(pkt->text);
    free(pkt);
}

第一种情况(x86到x64)的输出结果如下。

readn via utils. 3, , 1
toberead: 1, byteread: 0
toberead: 1, byteread: 1
Finished readn. 
readn via utils. 3, , 8
toberead: 8, byteread: 1
toberead: 8, byteread: 8
Finished readn. 
readn via utils. 3, , 57
toberead: 57, byteread: 58
toberead: 57, byteread: 53
toberead: 4, byteread: 53

从x64到x86的函数输出低于

readn via utils. 3, , 1
toberead: 1, byteread: -1079631112
toberead: 1, byteread: 1
Finished readn. 
readn via utils. 3, , 4
toberead: 4, byteread: 1
toberead: 4, byteread: 4
Finished readn. 
readn via utils. 3, , 57
toberead: 57, byteread: -1079631112
toberead: 57, byteread: 57
Finished readn. 
Reading packet complete succesfully.
>> 

应该在&gt;&gt;

之后打印数据

如果我的问题是明确的或是否有任何其他信息,请告诉我。我花了整整2天试图解决这个问题但是徒劳无功。

更新:调用readn的函数已更新。

2 个答案:

答案 0 :(得分:2)

在x64上,您正在读取1个字节,然后是8个字节(然后是更多字节) 在x86上,您正在读取1个字节,然后是4个字节(然后是更多字节) 这是关于程序的x86和x64版本之间不匹配的线索。

也许你这样叫sendn

long l;
l = /* something */;
sendn(sd, &l, sizeof l);

long在32位Linux上为32位宽,在64位Linux上为64位宽。

答案 1 :(得分:0)

代码有几个问题:

  • 第一个printf(""readn via utils ...)假定buf指向的内存已初始化,至少buf[0]应设置为'\0',否则beaviour这个电话是未定义的。

  • 第一个fprintf(stderr, ...引入了未定义的行为,因为byteread未被初始化。执行int byteread = 0;会解决此问题并使日志输出更有意义。

  • 同样read()会返回ssize_t,而代码会size_t而非int。这个错误会导致代码在大块数据中失败。

  • perror()的调用可能无法反映调用read()时发生的错误,因为调用{{1}可能会更改errno的值在fprintf()之前完成。

  • 如果收到的数据没有携带perror()作为最后一个字符,那么此行'\0'也会引入未定义的行为。更改它以逐字节打印缓冲区或测试尾随fprintf(stderr, "Finished readn. %s\n", buf);并在打印“字符串”之前“手动”添加它。

根据我上面的评论对代码进行了一些修复:

0

作为最终评论:这种从套接字读取数据的结构要么假定调用者事先知道要发送多少字节,要么在发送的数据少之后关闭连接,否则(这是更少的字节)然后发送int readn(int sd, char * buf, size_t n) { printf("readn via utils. %d, %s, %zu\n", sd, buf, n); size_t toberead = n; char * ptr = buf; while (toberead > 0) { int errno_save = 0; fprintf(stderr, "toberead: %zu\n", toberead); ssize_t byteread = read(sd, ptr, toberead); errno_save = errno; fprintf(stderr, "toberead: %zu, byteread: %zd\n", toberead, byteread); if (byteread <= 0) { fprintf(stderr, "byteread val: %zd\n", byteread); if (byteread == -1) { errno = errno_save; perror("read"); } raise(6); return (0); } toberead -= byteread; ptr += byteread; } if ('\0' != buf[n]) /* This assumes buf is one byte **larger** then n. */ { buf[n] = '\0'; } fprintf(stderr, "Finished readn. %s\n", buf); return (1); } 并且连接关闭)对n的调用将阻止。

请参阅immibis's answer,了解后者可能是您设置的问题(32位与64位)。