【问题标题】:Multiple connections webserver C多连接网络服务器 C
【发布时间】:2018-04-18 11:53:08
【问题描述】:

我需要用 C 编写一个非常基本的网络服务器。下面是我的代码。

问题是我无法正确管理多个连接,因为当我在浏览器上打开新选项卡时,我总是收到消息“连接 #1”,而不是“连接 #2”、“连接 #3”...

错误在哪里?谢谢

这是我的代码https://codeshare.io/a393M4 或在这里:

#ifdef _WIN32
#include <WinSock2.h>
#define socket_t SOCKET
#else
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#define WSACleanup()
#define INVALID_SOCKET (~0)
#define SOCKET_ERROR -1
#define closesocket close
#define ioctlsocket ioctl
#define socket_t int
#endif
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>



void print_time(char *buf, size_t buf_len) {
    time_t rawtime;
    time(&rawtime);
    struct tm info;
#ifdef _WIN32
    gmtime_s(&info, &rawtime);
#else
    gmtime_r(&rawtime, &info);
#endif

    const char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
    const char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

    snprintf(buf, buf_len, "%s, %i %s %i %02i:%02i:%02i GMT", days[info.tm_wday], info.tm_mday, months[info.tm_mon], 1900 + info.tm_year,
            info.tm_hour, info.tm_min, info.tm_sec);
}


typedef struct sock_list {
    socket_t s;
    struct sock_list *next;
} sock_list;


void sock_list_insert(sock_list **list, socket_t s) {
    sock_list *i = *list;
    sock_list *j = *list;
    sock_list *n = malloc(sizeof(sock_list));

    n->s = s;
    n->next = 0;

    while (i) {
        j = i;
        i = i->next;
    }

    if (j) {
        j->next = n;
    }
    else {
        *list = n;
    }
}


int sock_list_len(sock_list *list) {
    int c = 0;
    sock_list *i = list;
    while (i) {
        ++c;
        i = i->next;
    }
    return c;
}


int main() {
    int port = 80;

#ifdef _WIN32
    WSADATA wsa;
    if (WSAStartup(MAKEWORD(2,2), &wsa) != 0) {
        return -1;
    }
#endif

    socket_t sock;
    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == INVALID_SOCKET) {
        printf("Cannot open socket (-2)\n");
        WSACleanup();
        return -2;
    }
    unsigned long mode = 1;
    ioctlsocket(sock, FIONBIO, &mode);  // set as non-blocking


    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = INADDR_ANY;
    server.sin_port = htons(port);


    if (bind(sock, (struct sockaddr *) &server, sizeof(server)) == SOCKET_ERROR) {
        printf("Cannot bind socket (-3)\n");
        closesocket(sock);
        WSACleanup();
        return -3;
    }


    listen(sock, SOMAXCONN);
    sock_list *conn_list = 0;


    for (;;) {

        sock_list *i = conn_list;
        fd_set set;
        FD_ZERO(&set);
        while (i) {
            FD_SET(i->s, &set);
            i = i->next;
        }
        select(0, &set, &set, &set, 0);


        struct sockaddr_in client;
        memset(&client, 0, sizeof(client));
        int c = sizeof(client);
        socket_t ns = accept(sock, (struct sockaddr *) &client, &c);
        if (ns != INVALID_SOCKET) {
            printf("new sock %u\n", ns);
            sock_list_insert(&conn_list, ns);
        }


        int count = 0;
        i = conn_list;
        while (i) {
            ++count;
            int recv_sz;
            char msg[4096];
            char tbuf[512];

            print_time(tbuf, 512);

            recv_sz = recv(i->s, msg, 4096, 0);

            if (recv_sz > 0) {
                char html[4096];

                snprintf(html, 4096, "<!DOCTYPE HTML><html><head><meta charset='utf-8'><title>Ciao</title>"
                                     "<style type='text/css'>body {margin: 200px auto;text-align: center;color: #393939;}</style>"
                                     "</head><body><h1>Connection %i</h1><h1>%s</h1></body></html>", count, tbuf);
                snprintf(msg, 4096, "HTTP/1.1 200 OK\r\nContent-Length: %i\r\nContent-Type: text/html; charset=utf-8\r\n\r\n%s",
                         strlen(html),
                         html);

                send(i->s, msg, strlen(msg), 0);
                printf("send to %u\n", i->s);

            }
            i = i->next;
        }

        fflush(stdout);
#ifdef _WIN32
        Sleep(1);
#else
        usleep(1000);
#endif
    }


    closesocket(sock);
    WSACleanup();


    return 0;
}

【问题讨论】:

  • “我需要用C写一个非常基本的webserver。”,这显然不是C的目的,你应该换成python或类似的。 C 代表“完整的 http 服务器”而不是“非常基本的”,因为您将需要大量代码仅用于“基本服务器”,而且您的服务器会很糟糕。
  • 不要重新发明轮子,而是使用一些库,例如:gnu.org/software/libmicrohttpd
  • 您的代码可能会卡在 recv() 中,因为您接受的套接字正在阻塞。此外,您还没有在任何 select() FDSET 中放置侦听套接字(您已将其设为非阻塞)
  • 关于:select(0, &amp;set, &amp;set, &amp;set, 0);第一个参数不能为0,必须是最大socket值+1。第三个和第四个参数应该为 NULL,因为代码不检查写入可用性或 I/O 异常。
  • 关于:socket_t ns = accept(sock, (struct sockaddr *) &amp;client, &amp;c); 第三个参数的类型应该是socklen_t,而不是int

标签: c sockets winapi webserver winsock


【解决方案1】:

由于以下代码,“计数”始终为 1:

    int count = 0;
    i = conn_list;

    while (i)
    {
        ++count;

建议将'count'的初始化移到之前:

for (;;)

那为什么每次新连接都要重写所有的 HTML 页面呢?

我认为只需要在找到新连接时编写一次特定的 HTML 页面

【讨论】:

    猜你喜欢
    • 2011-12-26
    • 1970-01-01
    • 2010-10-21
    • 2012-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-18
    相关资源
    最近更新 更多