简体   繁体   English

C套接字编程写/读脏内存

[英]C Socket Programming write/read dirty memory

I wrote two custom function for write and read on my C program that my university teacher suggested: 我写了两个自定义函数,供大学老师建议在我的C程序上进行读写:

ssize_t FullRead(int fd, void *buff, size_t count) {
    size_t nleft;
    ssize_t nread;
    nleft = count;
    while(nleft > 0) {
        nread = read(fd, buff, nleft);
        if(errno == EINTR) {
            continue;
        }
        else {
            return nread;
        }
        if(nread == 0) {
            break;
        }
        nleft -= nread;
        buff += nread;
    }
    buff = 0;
    return nleft;
}

And

ssize_t FullWrite(int fd, const void *buff, size_t count) {
    size_t nleft;
    ssize_t nwritten;
    nleft = count;
    while(nleft > 0) {
        if((nwritten=write(fd, buff, nleft)) < 0) {
            if(errno == EINTR) {
                continue;
            }
            else {
                return nwritten;
            }
        }
        nleft -= nwritten;
        buff += nwritten;
    }
    buff = 0;
    return nleft;
}

But everytime I try to pass data from client and server, I always get special characters (dirty memory), this is what I tried to do: 但是每次我尝试从客户端和服务器传递数据时,我总是会收到特殊字符(脏内存),这就是我试图做的事情:

  • I have done a memset() before the FullWrite 我在FullWrite之前做了一个memset()
  • Tried with a fflush(stdin) , even if I know its not a good practice 尝试使用fflush(stdin) ,即使我知道这不是一个好习惯
  • Tried removing buff = 0 at the end of my functions 尝试在功能结束时删除buff = 0

This is what I do to call two consecutive FullRead and FullWrites. 这就是我将两个连续的FullRead和FullWrites调用的方法。

This is on the server : 这是在服务器上

FullRead(connfd, buff, strlen(buff));
printf("Buff: %s\n", buff);
FullRead(connfd, buff, strlen(buff));
printf("Buff: %s", buff);

And this is on the client : 这是在客户端上

memset((void *)buff, 0, sizeof(char)*1000);
scanf("%s", buff);
fflush(stdin);
FullWrite(sockfd, buff, strlen(buff));

memset((void *)buff, 0, sizeof(char)*1000);
scanf("%s", buff);
fflush(stdin);
FullWrite(sockfd, buff, strlen(buff));

If I write on my linux terminal something like "001" from the server, on the client I see "001 t" or similar things.. things like that. 如果我在Linux终端上从服务器上写类似“ 001”的内容,则在客户端上我会看到“ 001t”或类似的内容。 Can't come out from this situation. 无法摆脱这种情况。


I tried changing my code removing buff = 0 from FullWrite() and FullRead(), removed the fflush() and the memset, calling the functions like this: 我尝试更改代码,从FullWrite()和FullRead()中删除buff = 0,删除了fflush()和memset,并调用了以下函数:

Client 客户

scanf("%s", buff);
FullWrite(sockfd, buff, sizeof(buff));

Server 服务器

length = FullRead(connfd, buff, strlen(buff));
printf("Buffer: %.*s\n", length, buff);

Still the server reads and prints junk. 服务器仍然读取并打印垃圾邮件。

Paul R and EJP identified the immediate problem: you assume, effectively, that your buffer initially contains a C string of the same length as the one you want to read. Paul R和EJP确定了紧迫的问题:您有效地假设缓冲区最初包含的C字符串的长度与您要读取的长度相同。 Only in that case does both strlen(buff) specify the correct number of bytes, and the result reliably end up null-terminated. 只有在这种情况下,两个strlen(buff)指定正确的字节数,并且结果可靠地以null终止。

But that's only the tip of the iceberg. 但这只是冰山一角。 I observe next that your FullWrite() and FullRead() implementations are buggy. 接下来,我观察到您的FullWrite()FullRead()实现是错误的。 Taking the latter as representative, consider this excerpt from the read loop: 以后者为代表,请考虑一下read循环中的以下摘录:

        if(errno == EINTR) {
            continue;
        }
        else {
            return nread;
        }

Observe (1) that it cycles the loop only in the event that the read was interrupted by a signal (maybe or maybe not before any bytes were read), and (2) that the code following, up to the end of the loop body, is never reached under any circumstance. 观察到(1)仅在读取被信号中断的情况下才循环循环(可能在读取任何字节之前或可能未中断), 以及 (2)紧随其后的代码直到循环主体结束,在任何情况下都不会达到。 There are many more reasons than being interrupted by a signal why a read() call might transfer fewer bytes than expected and desired. 除了被信号打断之外,还有很多原因导致为什么read()调用可能传输的字节数少于预期和期望的字节数。 The usual implementation of such a loop reads repeatedly until the specified number of bytes have been transferred, or a read() call returns -1 . 这种循环的通常实现是重复读取,直到已传输指定数量的字节,或者read()调用返回-1为止。 Your loop includes the bits to implement that, but it never uses them. 您的循环包括实现该目标的位,但从未使用它们。

Also, your function returns the wrong value in the event that fewer bytes are actually read than were requested. 另外,如果实际读取的字节少于请求的字节,则函数返回错误的值。

Note, too, that the semantics of performing arithmetic on a void * are not defined by C. Some compilers treat it as if the pointer were of type char * instead, but that's not portable. 还要注意,在void *上执行算术的语义不是由C定义的。某些编译器将其视为好像指针是char *类型,但这不是可移植的。 If you want to treat the value as a char * then you should cast or assign it to that type. 如果要将值视为char * ,则应将其强制转换或分配给该类型。

Furthermore, it is useless (albeit not harmful) for the functions to assign 0 to the buffer pointer before returning. 此外,函数在返回之前将0分配给缓冲区指针是没有用的(尽管不是有害的)。

Here's an improved implementation of FullRead() : 这是FullRead()的改进实现:

ssize_t FullRead(int fd, void *buff, size_t count) {
    size_t nleft = count;
    char *next = buff;

    while (nleft > 0) {
        ssize_t nread = read(fd, next, nleft);

        if (nread < 0) {
            if (errno == EINTR) {
                /* interrupted by a signal */
                /* 
                 * It is not specified by POSIX whether this condition can
                 * arise if any bytes are successfully transerred.  In the GNU
                 * implementation, it does not arise in that case.
                 */
                continue;
            } else {
                /* other error */
                return -1;
            }
        } else if (nread == 0) {
            /* end of file */
            break;
        } else {
            /* 'nread' bytes successfully transferred */
            nleft -= nread;
            next += nread;
        }
    }

    return (count - nleft);
}

Your FullWrite function suffers from an analogous problem. 您的FullWrite函数有一个类似的问题。 I leave it up to you to fix that. 我将由您自己解决。

But we're not done yet. 但是我们还没有完成。 Even (especially, in fact) with correct implementations of FullWrite() and FullRead() , you still have a key problem to solve: how many bytes should the server in fact (try to) read? 即使(尤其是实际上)使用FullWrite()FullRead()正确实现,您仍然要解决一个关键问题:实际上(尝试)读取服务器应读取多少字节? There are only two ways it can know: 它只能知道两种方式:

  1. The number of bytes can somehow be communicated out of band. 字节数可以以某种方式带外通信。 The most likely implementation of that would be making it a pre-arranged fixed size. 该方法最有可能的实现方式是使其成为预先设置的固定大小。

  2. The client must tell the server in advance how many bytes to expect. 客户端必须提前告知服务器要期待多少字节。

A pre-arranged size is not a very good fit to variable-length data such as strings, but it works fine for fixed-length data such as binary numeric values. 预先设置的大小不适用于可变长度的数据(例如字符串),但对于固定长度的数据(例如二进制数值)则效果很好。 One solution, then, would be to transmit your strings as a (length, content) pair. 那么,一种解决方案是将您的字符串作为(长度,内容)对传输。 The length comes first as, say, a uint16_t in network byte order, then the specified number of bytes of string data. 长度首先出现,例如,以网络字节顺序排列的uint16_t ,然后是字符串数据的指定字节数。 The receiver is responsible for providing string termination. 接收者负责提供字符串终止。 This is analogous to HTTP's use of the Content-Length header to specify the size of a message body. 这类似于HTTP使用Content-Length标头指定消息正文的大小。

Two problems: 两个问题:

  • you are passing strlen(buff) to FullRead() - you probably want sizeof(buff) (hard to tell without seeing how you declare/initialise this buffer). 您正在将strlen(buff)传递给FullRead() -您可能想要sizeof(buff) (很难看出来,而不必知道如何声明/初始化此缓冲区)。

  • you are not terminating the string returned from FullRead() - you need a '\\0' immediately after the received data otherwise the subsequent printf() will most likely emit garbage characters beyond the end of the string. 您不会终止从FullRead()返回的字符串-您需要在接收到的数据后立即添加一个'\\0' ,否则后续的printf()很可能会在字符串末尾发出垃圾字符。

Also, as you already know, you should not be calling fflush(stdin) . 另外,您已经知道,不应调用fflush(stdin)

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

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