【问题标题】:How to read data from socket correctly?如何正确从套接字读取数据?
【发布时间】:2020-04-08 17:42:13
【问题描述】:

我有一个简单的客户端/服务器应用程序。用户在控制台中写入字符串。当他按下输入字符串时发送。我可以正确传输一行,但是在它将第一个发送的单词的第一个字母替换为下一行之后。例如,如果用户发送“Hello”,服务器会收到“Hello”,但之后,如果我再次发送“Hello”,服务器会收到“HHello”。如果我在发送后尝试在客户端清除缓冲区,它就再也不会发送任何东西了。

服务器代码:

// Server side C/C++ program to demonstrate Socket programming 
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>

#define PORT 57174
int main(int argc, char const *argv[])
{
int server_fd, new_socket, valread;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);

if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
    perror("socket failed");
    exit(EXIT_FAILURE);
}


if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT,
               &opt, sizeof(opt)))
{
    perror("setsockopt");
    exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );


if (bind(server_fd, (struct sockaddr *)&address,
         sizeof(address))<0)
{
    perror("bind failed");
    exit(EXIT_FAILURE);
}
if (listen(server_fd, 3) < 0)
{
    perror("listen");
    exit(EXIT_FAILURE);
}
if ((new_socket = accept(server_fd, (struct sockaddr *)&address,
                         (socklen_t*)&addrlen))<0)
{
    perror("accept");
    exit(EXIT_FAILURE);
}

char buffer[1024];
bzero(buffer, sizeof(buffer));
int step = 0;
   while(1){
   valread = read( new_socket , buffer, 1024);


   if(valread == 0)
       break;

   printf("%s", buffer );
printf("\n");

   bzero(buffer, sizeof(buffer));   


};

       return 0;
   } 

客户端代码:

//客户端C/C++程序演示Socket编程

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h> 
#include <unistd.h> 
#include <string.h>
#define PORT 57174

int main(int argc, char const *argv[]) 
{ 
    int sock = 0, valread;
    struct sockaddr_in serv_addr;
    char *hello = "Hello from client";
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    { 
        printf("\n Socket creation error \n"); 
        return -1; 
    } 

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(atoi(argv[2]));

    // Convert IPv4 and IPv6 addresses from text to binary form 
    if(inet_pton(AF_INET, argv[1], &serv_addr.sin_addr)<=0)
    {
        printf("\nInvalid address/ Address not supported \n");
        return -1;
    }

    if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) 
    { 
        printf("\nConnection Failed \n"); 
        return -1; 
    }
//    char buffer[1024] = {0};

    unsigned int N = 10, delta=10, i = 0;
    char* buf = (char*) malloc (sizeof(char)*N);
    while (1)  {
        buf[i] = getchar();
        if(buf[i] == 27)
            break;

        if(buf[i] == 10){
            send(sock , buf , strlen(buf) , 0 );
//            bzero(buf, sizeof(buf));
            N = 10;
            buf = (char*) realloc (buf, sizeof(char)*N);
            i = 0;
        }

        if (++i >= N) {
            N += delta;
            buf = (char*) realloc (buf, sizeof(char)*N);
        }
    }



    return 0; 
}

【问题讨论】:

标签: c sockets


【解决方案1】:

如果用户发送“Hello”,服务器会收到“Hello”,但之后,如果我再次发送“Hello”,服务器会收到“HHello”

这是因为您在客户中错过了一个else,在

   if(buf[i] == 10){
       send(sock , buf , strlen(buf) , 0 );
   //            bzero(buf, sizeof(buf));
       N = 10;
       buf = (char*) realloc (buf, sizeof(char)*N);
       i = 0;
   }
   if (++i >= N) {
       N += delta;
       buf = (char*) realloc (buf, sizeof(char)*N);
   }

你需要更换

if (++i >= N) {

通过

else if (++i >= N) {

else 在您发送缓冲区并将 i 设置为 0 后,您将其递增,您将记住索引 1 处的下一个字符,索引 0 处的字符仍然存在,您将发送一遍又一遍


您的客户也有问题

send(sock , buf , strlen(buf) , 0 );

因为您没有在 strlen 所需的 buff 中放入空字符以返回预期值,因此行为未定义。其实你不需要strlen,只要做

 send(sock , buf , i , 0 );

假设您不想发送\n


在你的服务器端

char buffer[1024];
...
valread = read( new_socket , buffer, 1024);
if(valread == 0)
  break;
printf("%s", buffer );

您每次 buffer 都用空字符填充,但如果您读取 1024 个字符,则缓冲区中没有空字符,并且 printf 将从 缓冲区具有未定义的行为

警告 read 出错时返回 -1,valread == 0 错误

删除你所有的 bzero 就可以了

 char buffer[1024];
 ...
 while ((valread = read(new_socket, buffer, sizeof(buffer)-1)) > 0) {
   buffer[valread ] = 0;
   printf("%s", buffer);
 }

注意我使用了 sizeof(buffer) 而不是 1024,即使你调整大小 buffer


对客户的其他说明:

  • 变量hello没用

  • 根据定义 sizeof(char) 的值为 1,因此 sizeof(char)*N 可以在任何地方被 N 替换

  • 不要将读取的字符与文字 10 和 27 进行比较,与 '\n' 和 '\e' 进行比较

  • 您不管理输入中的 EOF,因为您需要将读取的字符保存在 int 而不是 char 中(如 buf[i] is) 将其与 EOF 进行比较

在服务器中,变量 step 没用


除此之外,您使用 SOCK_STREAM,因此您的套接字是 stream(tcp 而不是 udp),这意味着您不能假设您每次调用读取时读取的数据,我的意思是如果客户端发送 N 个字节,这并不意味着服务器将在相应的读取时读取 N 个字节(如果我可以说“对应”,因为没有对应关系 ;-))。

假设如果您输入 azeqsd\n 其他问题得到解决,您发送 azeqsd 但可能在服务器端您将阅读 azeq所以打印 azeq\n 并在下一个循环中读取 sd 并打印 sd\n。

服务器也可能读取客户端单独发送的多个缓冲区的部分或全部连接。

你想要那种行为吗?如果没有,您需要在每个缓冲区之前发送大小,以了解要读取多少次,以构成完整的发送缓冲区(另一个优点是您没有不读取每个字节

【讨论】:

  • 这对我没有帮助,我仍然有问题。还能是什么?
猜你喜欢
  • 2013-09-05
  • 1970-01-01
  • 2014-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-20
  • 1970-01-01
相关资源
最近更新 更多