C HTTP 套接字过早关闭并明显出现读取错误
C HTTP sockets closing prematurely and apparently giving read errors
td;lr:试图将 "Hello World" 回显到 HTTP 客户端,但遇到套接字关闭过快的问题以及来自 wrk 基准工具的神秘读取错误。
我正在尝试使用 picoev 事件循环库制作一个简单的 "Hello World" HTTP 服务器,但是 client/peer 连接掉线太快了,wrk 基准测试工具 returns出于我不知道的任何原因读取错误。这是我正在使用的代码:
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include "picoev.h"
#define HOST 0 /* 0x7f000001 for localhost */
#define PORT 8080
#define MAX_FDS 1024 * 128
#define TIMEOUT_SECS 10
char buf[1024];
ssize_t response;
int listen_sock;
static void close_conn(picoev_loop* loop, int fd)
{
picoev_del(loop, fd);
close(fd);
}
static void write_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
{
// check whether neither events nor timeouts are present
if ((events & PICOEV_TIMEOUT) != 0) {
/* timeout */
close_conn(loop, fd);
} else if ((events & PICOEV_READ) != 0) {
/* update timeout, and read */
picoev_set_timeout(loop, fd, TIMEOUT_SECS);
ret = read(fd, buf, sizeof(buf));
if (ret == 0 | ret == -1) {
close_conn(loop, fd);
}
else {
write(fd, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 13\r\nConnection: close\r\n\r\nHello, world!", ret);
close_conn(loop, fd);
}
}
}
static void accept_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
{
int newfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
if (newfd != -1) {
picoev_add(loop, newfd, PICOEV_READ, TIMEOUT_SECS, write_callback, NULL);
}
}
int main(void)
{
picoev_loop* loop;
/* listen to port */
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, 1, sizeof(1));
struct sockaddr_in listen_addr;
listen_addr.sin_family = AF_INET;
listen_addr.sin_port = htons(PORT);
listen_addr.sin_addr.s_addr = htonl(HOST);
bind(listen_sock, (struct sockaddr*)&listen_addr, sizeof(listen_addr));
listen(listen_sock, 1000000);
/* init picoev */
picoev_init(MAX_FDS);
/* create loop */
loop = picoev_create_loop(60);
/* add listen socket */
picoev_add(loop, listen_sock, PICOEV_READ, 1, accept_callback, NULL);
/* loop */
while (1) {
// Picoev async call to write etc..
picoev_loop_once(loop, 10);
}
/* cleanup */
picoev_destroy_loop(loop);
picoev_deinit();
return 0;
}
冰壶 curl http://0.0.0.0:8080/ -v
returns:
* Trying 0.0.0.0...
* TCP_NODELAY set
* Connected to 0.0.0.0 (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: 0.0.0.0:8080
> User-Agent: curl/7.52.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/html
< Content-Length: 13
* transfer closed with 13 bytes remaining to read
* Curl_http_done: called premature == 1
* stopped the pause stream!
* Closing connection 0
curl: (18) transfer closed with 13 bytes remaining to read
或在多次尝试对数千个并发连接进行基准测试后出现以下情况:
* Trying 0.0.0.0...
* TCP_NODELAY set
* connect to 0.0.0.0 port 8080 failed: Connection refused
* Failed to connect to 0.0.0.0 port 8080: Connection refused
* Closing connection 0
curl: (7) Failed to connect to 0.0.0.0 port 8080: Connection refused
和wrk -t1 -c400 http://0.0.0.0:8080/
returns 正在读取所有错误:
Running 10s test @ http://0.0.0.0:8080/
1 threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 0.00us 0.00us 0.00us -nan%
Req/Sec 0.00 0.00 0.00 -nan%
0 requests in 10.08s, 9.05MB read
Socket errors: connect 0, read 249652, write 0, timeout 0
Requests/sec: 0.00
Transfer/sec: 0.90MB
我不明白问题是套接字关闭太快、响应(ret) 不正确、僵尸fd 没有被杀死还是它们的组合。尝试跟踪程序并没有提供任何关于问题所在的有价值的信息,只是提供了很多 epoll_wait 的信息。我已经尝试了许多 HTTP 响应变体,但都无济于事,正如您所看到的,我正试图在必要时尽快杀死任何僵尸或错误的 fd,但要么我做错了,要么问题出在其他地方。有人可以帮我查明问题所在吗?
在这行代码中:
write(fd, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 13\r\nConnection: close\r\n\r\nHello, world!", ret);
您使用 ret
作为调用 write()
的第三个参数。此参数用于指示 write()
应写入多少字节。
然而,ret
用于存储调用 read()
的结果。因此,传递给 write()
的值与您要发送的消息的大小之间没有任何关系。
通过使用您要发送的消息的长度初始化 ret
来解决此问题。
const char *msg = "HTTP/1.1 ...";
ret = strlen(msg);
write(fd, msg, ret);
td;lr:试图将 "Hello World" 回显到 HTTP 客户端,但遇到套接字关闭过快的问题以及来自 wrk 基准工具的神秘读取错误。
我正在尝试使用 picoev 事件循环库制作一个简单的 "Hello World" HTTP 服务器,但是 client/peer 连接掉线太快了,wrk 基准测试工具 returns出于我不知道的任何原因读取错误。这是我正在使用的代码:
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include "picoev.h"
#define HOST 0 /* 0x7f000001 for localhost */
#define PORT 8080
#define MAX_FDS 1024 * 128
#define TIMEOUT_SECS 10
char buf[1024];
ssize_t response;
int listen_sock;
static void close_conn(picoev_loop* loop, int fd)
{
picoev_del(loop, fd);
close(fd);
}
static void write_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
{
// check whether neither events nor timeouts are present
if ((events & PICOEV_TIMEOUT) != 0) {
/* timeout */
close_conn(loop, fd);
} else if ((events & PICOEV_READ) != 0) {
/* update timeout, and read */
picoev_set_timeout(loop, fd, TIMEOUT_SECS);
ret = read(fd, buf, sizeof(buf));
if (ret == 0 | ret == -1) {
close_conn(loop, fd);
}
else {
write(fd, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 13\r\nConnection: close\r\n\r\nHello, world!", ret);
close_conn(loop, fd);
}
}
}
static void accept_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
{
int newfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
if (newfd != -1) {
picoev_add(loop, newfd, PICOEV_READ, TIMEOUT_SECS, write_callback, NULL);
}
}
int main(void)
{
picoev_loop* loop;
/* listen to port */
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, 1, sizeof(1));
struct sockaddr_in listen_addr;
listen_addr.sin_family = AF_INET;
listen_addr.sin_port = htons(PORT);
listen_addr.sin_addr.s_addr = htonl(HOST);
bind(listen_sock, (struct sockaddr*)&listen_addr, sizeof(listen_addr));
listen(listen_sock, 1000000);
/* init picoev */
picoev_init(MAX_FDS);
/* create loop */
loop = picoev_create_loop(60);
/* add listen socket */
picoev_add(loop, listen_sock, PICOEV_READ, 1, accept_callback, NULL);
/* loop */
while (1) {
// Picoev async call to write etc..
picoev_loop_once(loop, 10);
}
/* cleanup */
picoev_destroy_loop(loop);
picoev_deinit();
return 0;
}
冰壶 curl http://0.0.0.0:8080/ -v
returns:
* Trying 0.0.0.0...
* TCP_NODELAY set
* Connected to 0.0.0.0 (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: 0.0.0.0:8080
> User-Agent: curl/7.52.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/html
< Content-Length: 13
* transfer closed with 13 bytes remaining to read
* Curl_http_done: called premature == 1
* stopped the pause stream!
* Closing connection 0
curl: (18) transfer closed with 13 bytes remaining to read
或在多次尝试对数千个并发连接进行基准测试后出现以下情况:
* Trying 0.0.0.0...
* TCP_NODELAY set
* connect to 0.0.0.0 port 8080 failed: Connection refused
* Failed to connect to 0.0.0.0 port 8080: Connection refused
* Closing connection 0
curl: (7) Failed to connect to 0.0.0.0 port 8080: Connection refused
和wrk -t1 -c400 http://0.0.0.0:8080/
returns 正在读取所有错误:
Running 10s test @ http://0.0.0.0:8080/
1 threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 0.00us 0.00us 0.00us -nan%
Req/Sec 0.00 0.00 0.00 -nan%
0 requests in 10.08s, 9.05MB read
Socket errors: connect 0, read 249652, write 0, timeout 0
Requests/sec: 0.00
Transfer/sec: 0.90MB
我不明白问题是套接字关闭太快、响应(ret) 不正确、僵尸fd 没有被杀死还是它们的组合。尝试跟踪程序并没有提供任何关于问题所在的有价值的信息,只是提供了很多 epoll_wait 的信息。我已经尝试了许多 HTTP 响应变体,但都无济于事,正如您所看到的,我正试图在必要时尽快杀死任何僵尸或错误的 fd,但要么我做错了,要么问题出在其他地方。有人可以帮我查明问题所在吗?
在这行代码中:
write(fd, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 13\r\nConnection: close\r\n\r\nHello, world!", ret);
您使用 ret
作为调用 write()
的第三个参数。此参数用于指示 write()
应写入多少字节。
然而,ret
用于存储调用 read()
的结果。因此,传递给 write()
的值与您要发送的消息的大小之间没有任何关系。
通过使用您要发送的消息的长度初始化 ret
来解决此问题。
const char *msg = "HTTP/1.1 ...";
ret = strlen(msg);
write(fd, msg, ret);