我的程序挂起,不会爆发

时间:2014-05-21 00:04:00

标签: c sockets

我正在尝试运行LIST命令来显示文件,但是当我运行它时,它会显示我想要的所有文件,但它只是挂在那里,并且不会破坏菜单。列表的最后3个字符总是一个换行符后跟一个句号然后换行符,所以我把它放在一个if语句中来检查突破并关闭套接字,但它没有,我错过了什么?

case 'l':
 case 'L':
  //Handle L case
 sprintf(buff, "LIST\n");
 send(sockfd, buff, 1000, 0);
 int length = strlen(buff);
 while ((rsize = recv(sockfd, buff, 1000, 0)) > 0)
 {
     fwrite(buff, rsize, 1, stdout);
    if ( buff[length-3] == '\n' && buff[length-2] == '.' && buff[length-1] == '\n' )
    {

        break;
    }
 }
 close(sockfd);
 break;

2 个答案:

答案 0 :(得分:2)

这是你的问题:

if ( buff[length-3] ...

length来自strlen(buff),此时buff包含您发送的数据,而非您收到的数据,因此buff[length-3]可能甚至不接近输入数据的末尾,最长可达1000个字符。

您应该专注于rsize,这是您收到的字节数,而不是length

编辑:正如评论中曾经提到过的那样(编辑2:现在在一个单独的答案中),你会在任何时候遇到问题recv()在你的结尾中意外停止 - 行序列,特别是如果它在读取少于三个字符后停止,那么你将非法使用数组的负索引。最好编写一个函数来从套接字中读取整行并将其存储在缓冲区中,然后只需调用if ( !strcmp(buffer, ".") )即可知道何时完成。

以下是一个例子:

#include <unistd.h>
#include <errno.h>
#include <string.h>

ssize_t socket_readline(const int socket, char * buffer, const size_t max_len) {
    ssize_t num_read, total_read = 0;
    bool finished = false;

    memset(buffer, 0, max_len);

    for ( size_t index = 0; !finished && index < (max_len - 1); ++index ) {
        num_read = read(socket, &buffer[index], 1);

        if ( num_read == -1 ) {
            if ( errno == EINTR ) {
                continue;      /*  Interrupted by signal, so continue  */
            }
            else {
                return -1;  /*  Other read() error, return error code  */
            }
        }
        else {
            if ( buffer[index] == '\n' ) {
                buffer[index] = '\0';              /*  Remove newline  */
                finished = true;             /*  End of line, so stop  */
            }
            else {
                ++total_read;
            }
        }
    }

    return total_read;
}

对每个角色使用系统调用是一个开销,但如果你不这样做,你将不得不存储你在某处读取的其他角色,所以除非你想编写自己的缓冲设施,这是最好的选择。

顺便说一句,您还应该检查send()(以及所有系统调用)的返回,因为不保证一次性发送所有字符,您可能需要额外的尝试。

答案 1 :(得分:1)

你不能单靠rsize。想想如果对第一个recv()的{​​{1}}的一次调用结束,然后下一个'\n'收到recv(),会发生什么。或者,如果'.'没有收到&gt; = 3个字节开头。您将无法在单个if语句中检查recv(),就像您要尝试的那样。

你应该做的是将套接字数据读入缓冲区,直到遇到"\n.\n"(不将其存储在缓冲区中),然后根据需要处理缓冲区并清除它,然后重复直到缓冲区本身只包含'\n'

尝试这样的事情:

'.'

可替换地:

case 'l':
case 'L':
{
    //Handle L case
    int linecapacity = 1000;
    char *line = (char*) malloc(linecapacity);
    if (line)
    {
        int linelength = 0;

        if (send(sockfd, "LIST\n", 5, 0) == 5)
        {
            bool stop = false;
            while (!stop)
            {
                rsize = recv(sockfd, buff, 1000, 0);
                if (rsize <= 0) break;

                fwrite(buff, rsize, 1, stdout);

                char *start = buff;
                char *end = &buff[rsize];

                while ((start < end) && (!stop))
                {
                    char *ptr = (char*) memchr(start, '\n', end-start);
                    if (!ptr) ptr = end;

                    length = (ptr - start);

                    int needed = (linelength + length);
                    if (needed > linecapacity)
                    {
                        char *newline = realloc(line, needed);
                        if (!newline)
                        {
                            stop = true;
                            break;
                        }
                        line = newline;
                        linecapacity = needed;
                    }

                    memcpy(buff, &line[linelength], length);
                    linelength += length;

                    if ((linelength == 1) && (line[0] == '.'))
                    {
                        stop = true;
                        break;
                    }

                    // process line up to linelength characters as needed...

                    linelength = 0;

                    start = ptr + 1;
                }
            }
        }

        free(line);
    }

    close(sockfd);
    break;
}