0

我有这个 C 代码:

#include <errno.h>
#include <sys/errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<fcntl.h>
#include <unistd.h>
#include <sys/stat.h>

char *buf;
int c,s; int port=45678;

int recv_m(int c,char *buf);

void get(){

char fileNameBuf[20];
int i=0;
char *s = buf;

if (*s=='/') {
    s++;
    while (*s!=' ') {

        fileNameBuf[i]=*s;
        *s++; 
        i++;
    }
        fileNameBuf[i]='\0';    

}
}

int main(){

//server connected
while ((c = accept(s, (struct sockaddr *) &client, (socklen_t *) &clientlen)) > 0){
    // Do whatever a web server does.

    char recv_buf[50];
    char *r=recv_buf;
    while(recv(c, r , 1, 0)!=0){
         //stores the received message in recv_buf
    }


    recv_buf[i]='\0';

          if (strncmp(recv_buf, "GET ", 4)==0){
                            buf=recv_buf+4;
            get();
          }

}
    return (0);
}

*buf 指向字符串/index.html HTTP/1.0。在函数结束时,fileNameBuf 应该存储字符串index.html

while 循环中的次数应为 10。当我运行此代码时,i = 381 并且出现分段错误(核心转储)。

我究竟做错了什么?

这是整个代码,所以 *buf 是问题所在?

4

2 回答 2

2

看来 buf 是一个字符数组。如果是这样,您应该使用 char 指针访问 buf。尝试这个:

int main ()  {

    char buf[] = "/index.html HTTP/1.0";
    char fileNameBuf[10];
    int i=0;

    char *s = buf;
    if (*s=='/') {

        s++;
        while (*s!=' ') {
            fileNameBuf[i]=*s;
            *s++;
            i++;
            printf("%d\n",i);
        }
    }
}

如果 buf 是一个 char 数组,并且即使数组和指针有几个共同点,在 C 中执行 buf++ 也是不合法的。这是来自 Kernighan/Ritchie C 书籍的文本。您可能也将 buf 声明为数组。

必须牢记数组名称和指针之间的一个区别。指针是一个变量,所以 pa=a 和 pa++ 是合法的。但是数组名不是变量;像 a=pa 和 a++ 这样的结构是非法的。

由于这个原因,在下面的代码中执行“arr++”将是一个错误。

int main() {
    int arr[10];
    int *ptr = arr;

    arr++; // Would be illegal.
    ptr++; // This is okay.
}
于 2013-09-14T04:18:00.517 回答
2

要么您对内容的假设buf一定是错误的,要么我们对您所说的意思的解释有误:

*buf指向字符串"/index.html HTTP/1.1"

如果您声明char **buf;并设置:

char *str = "/index.html HTTP/1.1";
char **buf = str;

然后*buf指向字符串的开头。这就是为什么创建 SSCCE(简短、独立、正确的示例)很重要的原因;它消除了歧义。


一个SSCCE

这段代码:

#include <stdio.h>

const char *buf = "/index.html HTTP/1.1";

static
void get(void)
{
    char fileNameBuf[10];
    int i=0;

    if (*buf=='/')
    { 
        buf++;
        while (*buf!=' ')
        {
            fileNameBuf[i]=*buf;
            buf++; 
            i++;
            printf("%d\n", i);
        }
    }
    printf("%.*s\n", (int)sizeof(fileNameBuf), fileNameBuf);
}

int main(void)
{
    get();
    return 0;
}

产生这个输出:

1
2
3
4
5
6
7
8
9
10
index.html

当然,我必须注意不要打印超出数组的末尾。你的数组是最小的;它不能包含包含文件名的字符串(空终止符没有空格)。但它不应该崩溃——如果char *buf = "/index.html HTTP/1.1";


完成的代码 - 阶段 1

这与作为程序提交的内容密切相关。它编译得很干净——我没有试过运行它。

#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <sys/socket.h>

char *buf;
int c, s; int port = 45678;
struct sockaddr_in server, client;
char *ipaddress = "127.0.0.1";
int clientlen = sizeof(client);
int portset = 0;

int recv_m(int c, char *buf);

static
void get(void)
{
    printf("in get method\n");
    char fileNameBuf[20];
    int i = 0;
    printf("%s\n", buf);
    char *s = buf;

    if (*s == '/')
    {
        printf("buf==/\n");
        s++;
        while (*s != ' ')
        {
            // printf("%c\n",*buf);
            // printf("in while\n");
            fileNameBuf[i] = *s;
            s++;
            i++;
            printf("%d\n", i);
        }
        fileNameBuf[i]='\0';
        printf("<<%s>>\n", fileNameBuf);
    }
    else
    {
        printf("!= '/'\n");
    }
}

int main(void)
{
    bzero(&server, sizeof(server));

    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(port);

// if (!inet_aton(ipaddress, &server.sin_addr))
//  fprintf (stderr, "inet_addr() conversion error\n");

    s = socket(AF_INET, SOCK_STREAM, 0); // Create socket
    if (!s) {
        perror("socket");
        exit(0);
    }

    if (bind(s, (struct sockaddr *) &server, sizeof(server)) < 0) {
        perror("bind");
        exit(0);
    }
    printf("binded\n");
    if (listen(s, SOMAXCONN) < 0) {
        perror("listen");
        exit(0);
    }
    printf("Waiting for connection\n");
    while ((c = accept(s, (struct sockaddr *) &client, (socklen_t *) &clientlen)) > 0)
    {
        // Do whatever a web server does.
        printf("got connected\n");
        char recv_buf[50];
        char el[4] = "\r\n\r\n";
        int h = 0; int i = 0;
        char *r = recv_buf;
        while (recv(c, r, 1, 0) != 0)
        {

            if (h == 4) break;

            if (*r == el[h]) {
                h++;
            }
            r++;
            i++;

            if (h == 4) break;
        }

        recv_buf[i] = '\0';

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


        if (  strncmp(recv_buf, "GET ", 4) == 0) {
            printf("check get\n");
            buf = recv_buf+4;
            printf("%s\n", buf);
            get();
        }

    }
    return(0);

}

这不是 SSCCE。所有与设置套接字和从套接字读取相关的代码都应该与手头的问题相切。

精简代码——第 2 阶段

减少过程包括消除不必要的。

#include <stdio.h>
#include <string.h>

char *buf;

static void get(void)
{
    printf("in get method\n");
    char fileNameBuf[20];
    int i = 0;
    printf("%s\n", buf);
    char *s = buf;

    if (*s == '/')
    {
        printf("buf==/\n");
        s++;
        while (*s != ' ')
        {
            fileNameBuf[i] = *s;
            s++;
            i++;
            printf("%d\n", i);
        }
        fileNameBuf[i]='\0';
        printf("<<%s>>\n", fileNameBuf);
    }
    else
    {
        printf("!= '/'\n");
    }
}

int main(void)
{
    char recv_buf[50];
    strcpy(recv_buf, "GET /index.html HTTP/1.1\r\n\r\n");

    printf("<<%s>>\n", recv_buf);

    if (strncmp(recv_buf, "GET ", 4) == 0)
    {
        printf("check get\n");
        buf = recv_buf+4;
        printf("%s\n", buf);
        get();
    }
    return(0);
}

这也可以干净地编译;不幸的是,它也为我成功运行(GCC 4.8.1,Mac OS X 10.8.4):

<<GET /index.html HTTP/1.1

>>
check get
/index.html HTTP/1.1


in get method
/index.html HTTP/1.1


buf==/
1
2
3
4
5
6
7
8
9
10
<<index.html>>

有时会发生这种情况;你清理的太狠了。所以,你必须回到前面的代码,更慢地删除东西。

裁员——第三阶段

让我们从第 1 阶段获取完整代码并在本地运行它。浏览器可以连接,localhost:45678/index.html输出为:

binded
Waiting for connection
got connected
GET /index.html HTTP/1.1
Host: localhost:45678

check get
/index.html HTTP/1.1
Host: localhost:45678

in get method
/index.html HTTP/1.1
Host: localhost:45678

buf==/
1
2
3
4
5
6
7
8
9
10
<<index.html>>

没有任何内容发送回等待的浏览器(它仍在等待,但很快就会超时)。代码循环回到下一个接受;目前尚不清楚它是否正确关闭了商店,但它并没有在第一个周期崩溃。

所以,这是一个有点徒劳的练习......你的代码似乎工作正常。它仍然应该改进——首先将这些全局变量中的每一个都变成一个局部变量main(),然后传递给一个带有签名buf的修改变量。get()void get(char *buf)

您显示的代码真的会崩溃吗?如果是这样,调试器为什么会崩溃?


防弹——第四阶段

在确定所指向的字符串buf实际上是"/index.html\r\n\r\n" and not“/index.html HTTP/1.1\r\n\r\n”`之后,很明显,我没有确保代码不会读到 null 结尾终止的字符串也不写超出缓冲区的末端。然而,这正是 SSCCE 如此重要的原因,以及诊断打印如此重要的原因。如果问题包含正在扫描的实际字符串,则发现问题会简单得多。

这段代码更接近于防弹。在其他主要更改中,它尝试在单个recv()操作中读取请求,而不是逐字节读取请求。这将责任放在避免recv(). 所有的全局变量都消失了;作为参数 buf传递给。已编写用于检测 EOS 和超长名称,以及处理名称直到第一个空格。它仍然具有文件名中每个字符的调试代码。中的代码get()get()main()已被修饰以发送回一个有效 HTTP 或足够有效 HTTP 的响应,其中包含每次处理时都会更改的一些 HTML。看到浏览器发出的请求很有趣。还有一个错误报告函数,它写入标准错误,采用格式字符串和参数,与printf()et al 一样,还为系统错误添加正确的错误号和消息,然后以失败状态退出。这使得错误报告不那么痛苦;对于每个错误,一行调用就足够了,而不是 3 或 4 行(取决于您选择的格式)。这些错误可能比其他错误更具表现力perror()

#include <ctype.h>
#include <errno.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <sys/socket.h>
#include <unistd.h>

static void err_exit(const char *fmt, ...);

static
void get(char *buf)
{
    printf("in get method\n");
    char fileNameBuf[256];
    size_t i = 0;
    printf("%s\n", buf);
    char *s = buf;

    if (*s == '/')
    {
        printf("buf==/\n");
        s++;
        while (*s != '\0' && *s != ' ' && i < sizeof(fileNameBuf))
        {
            printf("i = %3d: c = %3d = 0x%.2X = '%c'\n",
                   (int)i, *s, *s & 0xFF, isprint(*s) ? *s : '.');
            fileNameBuf[i++] = *s++;
        }
        fileNameBuf[i]='\0';
        printf("<<%s>>\n", fileNameBuf);
    }
    else
    {
        printf("!= '/'\n");
    }
}

int main(void)
{
    char *buf;
    int fd;
    int s;
    int port = 45678;
    struct sockaddr_in server, client;
    int clientlen = sizeof(client);
    int msgnum = 314159;

    bzero(&server, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(port);

    s = socket(AF_INET, SOCK_STREAM, 0);
    if (!s)
        err_exit("socket()\n");
    if (bind(s, (struct sockaddr *) &server, sizeof(server)) < 0)
        err_exit("bind()\n");
    printf("bound to address\n");
    if (listen(s, SOMAXCONN) < 0)
        err_exit("listen()\n");
    printf("Waiting for connection\n");

    while ((fd = accept(s, (struct sockaddr *) &client, (socklen_t *) &clientlen)) > 0)
    {
        printf("got connection\n");
        char recv_buf[4096];
        char el[5] = "\r\n\r\n";
        ssize_t length;
        /* Read message in one go; leave space for a null at the end */
        if ((length = recv(fd, recv_buf, sizeof(recv_buf)-1, 0)) > 0)
        {
            recv_buf[length] = '\0';
            if (strstr(recv_buf, el) == 0)
                err_exit("Incomplete message (%d bytes and no CRLF, CRLF pair)\n", length);
            printf("%d: <<%s>>\n", (int)length, recv_buf);

            if (strncmp(recv_buf, "GET ", 4) == 0)
            {
                printf("check get\n");
                buf = recv_buf + 4;
                printf("<<%s>>\n", buf);
                get(buf);
                char message[256];
                char format1[] =
                    "<html><head><title>Hello World!</title></head>"
                    "<body><h1>This is no fun at all (%d).</h1></body></html>\r\n\r\n";
                int msg_len = snprintf(message, sizeof(message), format1, msgnum++);
                char format2[] =
                    "HTTP/1.1 200 OK\r\n"
                    "Content-Type: text/html\r\n"
                    "Content-Length: %d\r\n"
                    "Content-Encoding: UTF-8\r\n\r\n%s";
                char response[1024];
                size_t nbytes = snprintf(response, sizeof(response), format2,
                                         msg_len, message);
                write(fd, response, nbytes);
            }
        }
        close(fd);
    }
    return(0);
}

static void err_exit(const char *fmt, ...)
{
    int errnum = errno;
    va_list args;
    va_start(args, fmt);
    vfprintf(stderr, fmt, args);
    va_end(args);
    fprintf(stderr, "%d: %s\n", errnum, strerror(errnum));
    exit(1);
}
于 2013-09-14T04:22:20.473 回答