c - 套接字编程在C中用循环发送和接收消息

标签 c sockets network-programming

我刚开始学习套接字编程,我正在尝试使用 TCP 在客户端和服务器之间发送和接收。首先,我从服务器向客户端发送当前目录的大小,客户端接收得非常好。然后我想从服务器直接发送当前的每个文件名,所以我创建了一个循环来这样做。在客户端中,我还有一个循环来接收所有文件名,该文件名的执行次数与文件数(目录大小)一样多。问题是当我打印出循环中收到的内容时,缓冲区是空白的。我意识到第一个循环接收到的字节是 55,其余的是 0,但缓冲区总是空白的。这是我的代码片段:

服务器:

if(strcmp(buffer, "ls-remote") == 0){ //display files from server directory

   // get the size of the directory 
    unsigned long size = htonl(directorySize());
    n = send(newsockfd, &size, sizeof(size), 0);
    if(n < 0) syserr("can't send to server"); 

    DIR *d = opendir(".");
    struct dirent *dir;
    if (d)
      {
        while((dir = readdir(d))!= NULL)
        { memset(&buffer[0], 0, sizeof(buffer)); // clear buffer
          strcat(buffer,  dir->d_name);
          n = send(newsockfd, buffer, strlen(buffer), 0);
          if(n < 0) syserr("can't send to server"); 
        }
      closedir(d);
    }
    else{
       syserr("Error...could not get files from directory.");
    }
}

客户:

if(strcmp(buffer, "ls-remote") == 0){ //display files from server directory
     unsigned long size;

      n = recv(sockfd, &size, sizeof(uint32_t), 0);// recieve the size of the directory

      if(n < 0) syserr("can't receive from server");
      size = ntohl(size);

     while(size > 0){
       memset(&buffer[0], 0, sizeof(buffer)); // clear buffer
       n = recv(sockfd, buffer, 255, 0); // recieve directory from server 
       if(n < 0) syserr("can't send to server");
       buffer[strlen(buffer) - 1] = '\0';
       printf("recieving: %s\n", buffer); // print directory
       size--;
     }
 }       

最佳答案

这里的一个问题是发送目录大小和目录条目的服务器与接收它们的客户端之间没有同步。换言之,如果目录包含entry.1、entry.2和entry.3,则客户端可能接收到例如entry.1和entry.2entry.3,或者entry.1entry.2和entry.3。正如 JVene 所建议的,即使这里没有将 unicode 作为罪魁祸首,也是如此。

其他一些事情:

  • 缓冲区[strlen(缓冲区) - 1] = '\0';在客户端代码中将被切断 最后一个字符。应该是 buffer[strlen(buffer)] = '\0'; 反而。
  • 使用 sizeof() 运算符的类型使用的一致性很重要。 例如,在我的 Mac 上,sizeof(long) 是 8,而 sizeof(uint32_t) 是 4。 这会导致更有趣的副作用,即缺少
    客户端和服务器之间的同步。

这里的“同步”问题是由于当客户端开始从套接字读取时,服务器可能已经写入了多个目录条目,因此客户端会将它们全部读取为一个字符串。如果假设缓冲区大小不同,则将目录大小写入套接字并从套接字读取目录大小,事情会变得更加困惑;见上文。

经过一些额外的实验,我想出了似乎有效的代码。我确信可以进行改进,还有其他方法,并且存在代码未考虑的场景,例如如果目录在程序运行时发生变化怎么办。这只是一段代码的概念验证,有望帮助您朝着正确的方向前进。

这里的想法是让服务器将目录条目写入由 NULL 字符分隔的套接字。然后在客户端将它们用作分隔符以区分目录条目。请参阅代码中的注释。

服务器代码将目录条目写入套接字:

// Assume maximum entry length is 255
// The buffer is 256 bytes long to accommodate the NULL-terminator.
// The terminator is important for the client as direntry delimiter.
char buffer[256];
 // get the size of the directory
unsigned long size = htonl(dirSize());
int n = send(client_sock, &size, sizeof(size), 0);
if(n < 0) puts("can't send size to server");

DIR *d = opendir(".");
struct dirent *dir;
if (d)
  {
    while((dir = readdir(d))!= NULL)
    {
      memset(&buffer[0], 0, sizeof(buffer)); // clear buffer
      strcat(buffer,  dir->d_name);
      // Write up to 255 chars of direntry + the NULL-terminator.
      n = send(client_sock, buffer, strlen(buffer) + 1, 0);
      if(n < 0) puts("can't send entry to server");
    }
  closedir(d);
}
else{
   puts("Error...could not get files from directory.");
}

从套接字读取的客户端代码:

   char buffer[256];

   /*
    * We need this in case the beginning of a directory entry is in one buffer, but
    * the end is in the next.
    */
   char buf_1[256];
   unsigned long size;

   buf_1[0] = 0; // make sure strlen(buf_1) is 0.

   int n = recv(sockfd, &size, sizeof(long), 0);// recieve the size of the directory

   if(n < 0) puts("can't receive size from server");
   size = ntohl(size);

   while(size > 0){
       memset(&buffer[0], 0, sizeof(buffer)); // clear buffer
       n = recv(sockfd, buffer, 255, 0);  // keep last element of buffer as 0
       if(n < 0) puts("can't receive entry from server");

       int _start = 0;
       if (strlen(buf_1)) // something left over from previously read buffer
       {
          // buf_1 contains beginning of an entry, buffer - the end
          strcat(buf_1, buffer); // Assume there is a 0-terminator somewhere in buffer
          printf("receiving: %s\n", buf_1);  // buf_1 now has the entry, print it          buf_1[0] = 0;  // flag buf_1 as empty
          size--;  // we are one direntry down
          _start += strlen(buffer) + 1;  // move _start to char following 0-terminator
       }
       // Loop while _start is 0 - 254, the char at offset _start is not NULL,
       // and there are still entries to retrieve.
       while (_start < 255 && *(buffer + _start) && size > 0)
       {
          if (strlen( buffer + _start ) + _start >= 255) // no null terminator, need buf_1
          {
             strcpy(buf_1, buffer + _start);  // copy unfinished entry to buf_1
             // don't decrement size, we haven't extracted a full direntry.
             break;  // out of the inner while to read more from the socket.
          }
          else // we have a full direntry
          {
             printf("receiving: %s\n", buffer + _start); // print it
             _start += strlen(buffer + _start) + 1;  // move offset to next possible entry
             size--; // one entry down
          }
       }
    }

希望这对您有所帮助,祝您好运!如果您还有其他问题,请告诉我。

关于c - 套接字编程在C中用循环发送和接收消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32575983/

相关文章:

c# - 需要帮助解决有关异步服务器套接字的一些逻辑错误

c - 压入堆栈的问题

c++ - 如何打开仅具有 FILE_READ_ATTRIBUTES 权限的文件?

c - 当http header错误报告content-length时该怎么办

sockets - RST上的TCP重传-Windows和Linux上的套接字行为不同?

c# - socket 推荐

c - 查找客户端使用的端口号

c - 在linux环境下模拟不同IP地址的不同PC

c - 用按位运算符替换 “!=”

c - 如果已经比较了一个字符,我如何在两个 c_strings 之间进行比较检查?