【问题标题】:Basic Socket Programming in CC中的基本套接字编程
【发布时间】:2014-09-28 14:19:14
【问题描述】:

所以我在我的程序中遇到了一个奇怪的问题,它基本上是 C 中的客户端/服务器套接字编程示例。我已经从这个问题中抽出时间,但我似乎无法找到导致问题。

如果需要,我可以发布更多详细信息,但以下内容不起作用。

       while(1)
{
char word[256];
gets(word);
printf("%s",word);

     if(strcmp(word, "TRANSLATE") == 0 || strcmp(word, "GET") == 0 ||strcmp(word, "STORE") == 0 || strcmp(word, "EXIT") == 0)
{
    if(strcmp(word, "TRANSLATE") == 0)
    {
        if(send(sock , word , strlen(word) , 0)<0)
        {
            printf("Send Error\n");
            return 1;
        }
        bzero(server_reply,2000);
        if(recv(sock , server_reply , 2000 , 0)<0)
        {
            puts("Recieving Error\n");
        }
        //Print out 200ok message
        printf("%s\n", server_reply);
        //Get the first word
        gets(word2);
        while(strcmp(word2,"." )!= 0)
        {

            if(send(sock , word2 , strlen(word2) , 0)<0)
            {
                printf("Send Error\n");
                return 1;
            }
            gets(word2);    
        }
        if(recv(sock , server_reply , 2000 , 0)<0)
        {
            puts("Recieving Error\n");
        }
            printf("%s\n", server_reply);
        //STILL NEED TO WORK ON PARSING OUT SPACES BECAUSE RESULT IS IN A STRING, JUST WITH SPACES.
        //ALSO NEED TO DO SERVER SIDE CAPITALIZATION
    }
    if(strcmp(word, "GET") == 0)
    {

        printf("Getting");
    /*
        if(send(sock , word , strlen(word) , 0)<0)
        {
            printf("Send Error\n");
            return 1;
        }
        bzero(server_reply,2000);
        if(recv(sock , server_reply , 2000 , 0)<0)
        {
            puts("Recieving Error\n");
        }
        //Print out 200ok message
        printf("%s\n", server_reply);
        /*
         if(recv(sock , server_reply , 2000 , 0)<0)
        {
            puts("Recieving Error\n");
        }
        //Print out the KANSAS message
        printf("%s\n", server_reply);
        */
    }
}

一旦我在“GET”选项中取消注释,代码就会中断,它甚至不会在 if 语句之前命中 printf() 语句。如果我将它排除在外,它会完美地命中所有正确的语句。我错过了什么吗? 另外,我认为这可能与服务器通信有关,所以这是服务器代码。

  #include<stdio.h>
  #include<string.h>  //strlen
#include<sys/socket.h>
#include<arpa/inet.h>   //inet_addr
#include<unistd.h>  //write

int main(int argc , char *argv[])
{
   int socket_desc , client_sock , c , read_size;
   struct sockaddr_in server , client;
   char client_message[2000];

   //Create socket
   socket_desc = socket(AF_INET , SOCK_STREAM , 0);
   if (socket_desc == -1)
   {
       printf("Could not create socket");
   }
   puts("Socket created");

   //Prepare the sockaddr_in structure
   server.sin_family = AF_INET;
   server.sin_addr.s_addr = INADDR_ANY;
   server.sin_port = htons( 5149 );

   //Bind
   if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
   {
       //print the error message
       printf("bind failed. Error");
       return 1;
   }
   puts("bind done");

   //Listen
   listen(socket_desc , 3);

   //Accept and incoming connection
   puts("Waiting for incoming connections...");
   c = sizeof(struct sockaddr_in);

   //accept connection from an incoming client
   client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
   if (client_sock < 0)
   {
       printf("accept failed");
       return 1;
   }
   puts("Connection accepted");

   //Receive a message from client
   while( (read_size = recv(client_sock , client_message , 2000 , 0)) > 0 )
   {
    if(strcmp(client_message,"TRANSLATE")==0)
    {
        *(client_message + read_size) = '\0';
        char word[256]="200 OK TRANSLATE";
        write(client_sock, word, strlen(word));
        /*
         while( (read_size = recv(client_sock , client_message , 2000 , 0)) > 0 )
         {
            *(client_message + read_size) = '\0';
            puts(client_message);
            write(client_sock,client_message,strlen(client_message));
            if(strcmp(client_message,".")==0)
            {
                printf("breaking");
                break;
            }
            printf("IN while loop");
         }

        printf("Out of while loop");
        */
    }
    if(strcmp(client_message,"GET")==0)
    {
        *(client_message + read_size) = '\0';
        char word[256]="200 OK GET";
        write(client_sock, word , strlen(word));
        char word2[256]="I don't think we're in Kansas anymore.";
        write(client_sock, word2 , strlen(word2));
    }
    if(strcmp(client_message,"STORE")==0)
    {
        char word[256]="200 OK STORE";
        write(client_sock, word , strlen(word));
    }
   }

   if(read_size == 0)
   {
       puts("Client disconnected");
       fflush(stdout);
   }
   else if(read_size == -1)
   {
       printf("recv failed");
   }

   return 0;
}

【问题讨论】:

  • 你有没有strace 客户端和服务器程序?
  • 我没有,我对 Linux 编程非常陌生,我只是想弄清楚如果我直接从 translate 语句中复制的 if 语句会崩溃,这对 istelf 来说效果很好跨度>
  • 不要使用gets。它不安全,已从 C 标准中删除。

标签: c sockets if-statement


【解决方案1】:

您应该在每个printf 格式控制字符串的末尾添加一个\n 和/或调用fflush(3)

您程序中的主要问题是错误地认为给定的send(或write)到socket(7) 对应于另一侧的单个recv(或read)。

tcp(7) 实现只是一个字节流;没有隐含的消息边界。因此,您的应用程序应该缓冲并将该流显式拆分为有意义的消息。

可能发生,并且它确实发生,一侧能够同时sendwrite 4000字节,然后再发射1000字节,但对方会先recv(或read),例如39 个字节,然后是 1 个字节,然后是 1960 个字节,然后是 3000 个字节。然后你就会明白“消息”不是完整的,只有一个字节流。

另一个困难是这种行为是不可重现的。下次您运行服务器和客户端程序时,它们的行为会有所不同!

在实践中,很好地定义消息边界在哪里是有帮助的。您可以在HTTP 中执行类似操作,即在每条消息的开头放置一些内容长度(在 HTTP 中,回复标头中的 Content-Length: 属性)。您还可以决定一条消息是一条长行,并用一个换行符终止它(然后,您可能需要一个约定来转义内部换行符),例如在JSON。当然,你应该在两边缓冲。

您可能希望使用像 poll(2) 之类的多路复用调用(可能在您的 event loop 中)来测试套接字(或管道)的可读性或可写性

我强烈建议阅读Advanced Linux Programming 和一些Linux Socket Tutorial

顺便说一句,请始终测试您的系统调用是否失败。请参阅intro(2) 并使用perror(3)errno(3)(通常与strerror(3) 一起使用)。

当然要使用所有警告和调试信息 (gcc -Wall -g) 进行编译,并使用调试器 (gdb)(也许还有 strace(1))。您可能希望在不同的终端中使用 两个 gdb 会话(一个用于服务器,一个用于客户端)进行调试。

顺便说一句,不要使用过时的已弃用 bzero 函数,而是使用标准的memset(3)

【讨论】:

  • 即使我正在控制另一台服务器,并且知道服务器正在响应?这是一种非常基本的、受控的交流方式,并且在翻译中效果很好?
  • 是的,特别是发射器和接收器之间的路由器(例如服务器和客户端)可以分段或合并数据包。
  • 就像我试图解释的那样,我猜,第一个也是最明显的错误是,一旦我在 get 命令中取消对 print server_reply 的注释,整个 if 语句就崩溃了,它甚至没有 printf("获取");在它之前。客户只是休息。服务器根本没有收到任何通知。
  • @eso92:您是否使用gcc -Wall -g) 编译并使用gdb 调试器?
  • 错误方法:使用调试器进行调试是您应该知道的第一件事。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-07
  • 2011-10-09
  • 2013-06-05
  • 1970-01-01
相关资源
最近更新 更多