【发布时间】: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, &set, &set, &set, 0);第一个参数不能为0,必须是最大socket值+1。第三个和第四个参数应该为 NULL,因为代码不检查写入可用性或 I/O 异常。 -
关于:
socket_t ns = accept(sock, (struct sockaddr *) &client, &c);第三个参数的类型应该是socklen_t,而不是int
标签: c sockets winapi webserver winsock