【问题标题】:Socket Programming C, receiving messages is carrying over from previous messages套接字编程C,接收消息是从以前的消息中继承的
【发布时间】:2019-02-26 09:33:26
【问题描述】:

您好,我正在尝试在我的服务器和我的客户端应用程序之间创建一个简单的用户名/密码读写。目前我的客户端运行良好。我遇到的问题在我的服务器端。当我在客户端输入用户名时,它会被我的服务器正确捕获。见以下代码:

void authenticate_process(int cli_sockfd){

    /* Authentication Process */
    write(cli_sockfd, "USN", 3);
    char *username;
    username = recv_msg(cli_sockfd);
    printf("[DEBUG] Client username is %s.\n", username);

    write(cli_sockfd, "PSW", 3);
    char *password;
    password = recv_msg(cli_sockfd);
    printf("[DEBUG] Client Password is %s.\n", password);

}

问题是,例如,用户键入“Johnabscaras”作为用户名,然后代码将最后一个“ras”放入密码变量中。我的 recv_msg 函数如下所示:

/* Reads a message from the server socket. */
char *recv_msg(int sockfd)
{
    int length;
    char *msg;
    msg = (char*) malloc (9);

    /* All messages are 9 bytes. */
    int n = read(sockfd, msg, 9);
    return msg;

}

由于用户名和密码永远不会超过 9 个字节,因此为此设置了它。但我发现,在第一次输入用户名时,如果输入超过 9 个字符,多余的字符将被修改为密码变量。如果您键入的字符少于 9 个,则代码会跳过并且密码变量会由于某种原因立即设置为“”。有人可以解释一下并告诉我如何解决这个问题吗?

【问题讨论】:

  • 对我来说很有意义,发送了超过 9 个字节,但您只读取了 9 个,其余字节将在下一次读取时返回。也许在读取名称和密码之间尝试 fflush(sockfd)。
  • 您看到的是预期的行为——请记住,TCP 实现的是字节流,而不是消息流;也就是说,TCP 不保留消息边界,因此您将始终按照发送的顺序接收发送的字节,但不一定在相同的分组中。对于您的情况,简单的解决方法是修改发送程序以在发送之前截断字符串,使其永远不会超过 9 个字节。
  • 这是用户名这是密码,请告诉我这台可怜的电脑怎么可能知道哪个是哪个?

标签: c sockets pointers char


【解决方案1】:

看起来您正在使用流套接字(SOCK_STREAM,即 TCP)。流套接字基本上(从您的程序的角度来看)只是一个双向管道。

除了您的应用程序强加的内容之外,没有“消息”的概念。套接字仅发送和接收单个长字节流。 write(fd, "foo", 3); write(fd, "bar", 3);write(fd, "foobar", 6); 具有相同的效果。它以相同的顺序发送相同的字节。

如果您希望您的流传输多个单独的消息,您必须以某种方式对这些消息边界进行编码。

  • 例如,您可以决定每条消息的长度正好是 100 字节,然后总是发送/接收正好 100 字节。
  • 或者您可以使用显式终止符(例如'\0')标记消息的结尾。然后接收代码必须循环,直到看到'\0'
  • 或者您可以发送一个长度字段作为每条消息的第一部分;这样接收代码就可以提前知道它需要分配和读取多少字节。

您代码中的此注释表明您选择了选项 1:

/* All messages are 9 bytes. */

但显然发送代码有一个错误,因为它试图传输超过 9 个字节的消息,然后当然会被解释为多条消息。如果客户端发送 12 个字节,那么这是一个 9 个字节的消息,然后是另一个 3 个字节的(不完整的)消息。 “目前我的客户端工作正常”?不,不是;至少如果您的接收代码正确,则不会。

另一个问题是

printf("[DEBUG] Client username is %s.\n", username);

%s 需要一个 C 字符串,即一个以 nul 结尾的字符序列。 username 没有 NUL 终止符(除非客户端决定显式发送 '\0' 字节作为消息的一部分)。事实上,recv_msg 的用户无法知道收到了多少数据。如果总是正好 9 个字节,

printf("%.9s", username);

会起作用,但recv_msg 不能确保是这种情况:

int n = read(sockfd, msg, 9);

它只是忽略了n,因此msg 的全部或部分可能未初始化,调用代码无法分辨。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-02
    • 2017-09-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多