如何正确地从套接字读取数据?

时间:2020-04-08 17:42:13

标签: c sockets

我有一个简单的客户端/服务器应用程序。用户在控制台中写入字符串。当他按Enter键时,将发送字符串。我可以正确地传输一行,但是在将第一个发送的单词的第一个字母替换为每个后一个字母之后。例如,如果用户发送“ 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;
   } 

客户代码:

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

#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; 
}

1 个答案:

答案 0 :(得分:2)

如果用户发送“ 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) {

否则,在发送缓冲区并将 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 );

您每次在缓冲区中填充空字符,但是如果您读取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 < / p>


客户的其他评论:

  • 变量 hello 没用

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

  • 请勿将读取的char与文字10和27进行比较,而与'\ n'和'\ e'

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

在服务器中,变量 step 是无用的


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

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

服务器也有可能读取由客户端分别发送的几个缓冲区的部分或全部串联。

您想要那种行为吗?如果没有,那么您需要在每个缓冲区之前发送大小,以知道多少次读取甚至构成完整的已发送缓冲区(另一个优点是您不必每个

读取字节