Re: TCP socket handling errors
Sad Clouds <[email protected]>
| Newsgroups | gmane.os.freebsd.devel.net |
|---|---|
| Message-ID | <[email protected]> |
On Wed, 3 Apr 2024 17:28:52 +0200 Michael Tuexen <[email protected]> wrote: > > On 3. Apr 2024, at 15:44, Sad Clouds <[email protected]> wrote: > > > > I found a bug that is still open from May 2010 and describes the same > > behaviour that I see with my application: > > > > https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=146845 > > > > If this hasn't been fixed over the last 14 years, then I guess I will > > add some code to simply ignore ECONNRESET on close(2) for FreeBSD and > > MacOS. This seems the be the general advice from other people who hit > > this issue. > I'll bring this up on the bi-weekly FreeBSD transport call. > > Best regards > Michael > > > Hello, I've attached a test program, this easily reproduces the issue on Raspberry Pi 4 within a few seconds of running it. Server output: $ ./econnreset server Server: accept() Server: shutdown() Server: close() .. Server: accept() Server: shutdown() Server: close() close() failed, error=Connection reset by peer Client output (aborts when server exists due to close() failure): $ while true; do ./econnreset client || break; done Client: connect() Client: shutdown() Client: close() .. Client: connect() Assertion failed: (int_val == 0), function client, file econnreset.c, line 156. Abort trap (core dumped)
econnreset.c
(text/x-csrc, 5.7 KB)
/*
* cc -D_POSIX_C_SOURCE=200809L -D__BSD_VISIBLE -D__XSI_VISIBLE \
* -O2 -std=c11 -Wpedantic -Wall -o econnreset econnreset.c
*/
#include <arpa/inet.h>
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <unistd.h>
#define ADDR "127.0.0.1"
#define PORT 9999
#define SIZE (10 * 1024U * 1024U)
#define CLI_ID "client"
#define CLI_ID_LEN 6
#define SRV_ID "server"
#define SRV_ID_LEN 6
static void server()
{
int int_val, sockfd, connfd;
struct sockaddr_storage socket_addr = {0};
socklen_t socket_addr_size;
uint8_t snd_buf[1024], rcv_buf[1024];
size_t snd_size, rcv_size, io_size;
ssize_t ssize_val;
/* Socket family */
((struct sockaddr_in *)&socket_addr)->sin_family = AF_INET;
/* Socket address */
int_val = inet_pton(AF_INET, ADDR, &(((struct sockaddr_in *)&socket_addr)->sin_addr));
assert(int_val == 1);
/* Socket port */
((struct sockaddr_in *)&socket_addr)->sin_port = htons(PORT);
/* Socket size */
socket_addr_size = sizeof(struct sockaddr_in);
sockfd = socket(socket_addr.ss_family, SOCK_STREAM, 0);
assert(sockfd >= 0);
int_val = bind(sockfd, (const struct sockaddr *)&socket_addr, socket_addr_size);
assert(int_val == 0);
int_val = listen(sockfd, 128);
assert(int_val == 0);
while (1)
{
printf("Server: accept()\n");
connfd = accept(sockfd, NULL, NULL);
assert(connfd >= 0);
/* Size of data to send/receive */
snd_size = rcv_size = SIZE;
/* Data copy loop */
while (snd_size != 0 && rcv_size != 0)
{
/* Send data */
io_size = (sizeof(snd_buf) <= snd_size) ? sizeof(snd_buf) : snd_size;
ssize_val = send(connfd, snd_buf, io_size, MSG_NOSIGNAL);
if (ssize_val < 0)
{
fprintf(stderr, "send() failed, error=%s\n", strerror(errno));
goto cleanup;
}
snd_size -= (size_t)ssize_val;
/* Receive data */
io_size = (sizeof(rcv_buf) <= rcv_size) ? sizeof(rcv_buf) : rcv_size;
ssize_val = recv(connfd, rcv_buf, io_size, MSG_NOSIGNAL | MSG_WAITALL);
if (ssize_val < 0)
{
fprintf(stderr, "recv() failed, error=%s\n", strerror(errno));
goto cleanup;
}
rcv_size -= (size_t)ssize_val;
}
/* Send server ID string to client */
ssize_val = send(connfd, SRV_ID, SRV_ID_LEN, MSG_NOSIGNAL);
if (ssize_val <= 0)
{
fprintf(stderr, "send() failed, error=%s\n", strerror(errno));
goto cleanup;
}
/* No more sending, do TCP half close */
printf("Server: shutdown()\n");
int_val = shutdown(connfd, SHUT_WR);
if (int_val != 0)
{
fprintf(stderr, "shutdown() failed, error=%s\n", strerror(errno));
goto cleanup;
}
/* Receive client ID string and verify it */
ssize_val = recv(connfd, rcv_buf, CLI_ID_LEN, MSG_NOSIGNAL | MSG_WAITALL);
if (ssize_val < 0)
{
fprintf(stderr, "recv() failed, error=%s\n", strerror(errno));
goto cleanup;
}
assert(ssize_val == CLI_ID_LEN);
assert(strncmp((char *)rcv_buf, CLI_ID, CLI_ID_LEN) == 0);
cleanup:
printf("Server: close()\n");
int_val = close(connfd);
if (int_val != 0)
{
/* We hit https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=146845 */
fprintf(stderr, "close() failed, error=%s\n", strerror(errno));
exit(EXIT_FAILURE);
}
}
}
static void client()
{
int int_val, connfd;
struct sockaddr_storage socket_addr = {0};
socklen_t socket_addr_size;
uint8_t snd_buf[1024], rcv_buf[1024];
size_t snd_size, rcv_size, io_size;
ssize_t ssize_val;
/* Socket family */
((struct sockaddr_in *)&socket_addr)->sin_family = AF_INET;
/* Socket address */
int_val = inet_pton(AF_INET, ADDR, &(((struct sockaddr_in *)&socket_addr)->sin_addr));
assert(int_val == 1);
/* Socket port */
((struct sockaddr_in *)&socket_addr)->sin_port = htons(PORT);
/* Socket size */
socket_addr_size = sizeof(struct sockaddr_in);
connfd = socket(socket_addr.ss_family, SOCK_STREAM, 0);
assert(connfd >= 0);
printf("Client: connect()\n");
int_val = connect(connfd, (const struct sockaddr *)&socket_addr, socket_addr_size);
assert(int_val == 0);
/* Size of data to send/receive */
snd_size = rcv_size = SIZE;
/* Data copy loop */
while (snd_size != 0 && rcv_size != 0)
{
/* Send data */
io_size = (sizeof(snd_buf) <= snd_size) ? sizeof(snd_buf) : snd_size;
ssize_val = send(connfd, snd_buf, io_size, MSG_NOSIGNAL);
assert(ssize_val >= 0);
snd_size -= (size_t)ssize_val;
/* Receive data */
io_size = (sizeof(rcv_buf) <= rcv_size) ? sizeof(rcv_buf) : rcv_size;
ssize_val = recv(connfd, rcv_buf, io_size, MSG_NOSIGNAL | MSG_WAITALL);
assert(ssize_val >= 0);
rcv_size -= (size_t)ssize_val;
}
/* Send client ID string to server */
ssize_val = send(connfd, CLI_ID, CLI_ID_LEN, MSG_NOSIGNAL);
assert(ssize_val >= 0);
/* No more sending, do TCP half close */
printf("Client: shutdown()\n");
int_val = shutdown(connfd, SHUT_WR);
assert(int_val == 0);
/* Receive server ID string and verify it */
ssize_val = recv(connfd, rcv_buf, SRV_ID_LEN, MSG_NOSIGNAL | MSG_WAITALL);
assert(ssize_val == SRV_ID_LEN);
assert(strncmp((char *)rcv_buf, SRV_ID, SRV_ID_LEN) == 0);
printf("Client: close()\n");
int_val = close(connfd);
if (int_val != 0)
{
/* We hit https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=146845 */
fprintf(stderr, "close() failed, error=%s\n", strerror(errno));
exit(EXIT_FAILURE);
}
}
int main(int argc, char *argv[])
{
if (argc != 2)
{
printf("Usage: econnreset client | server\n");
exit(EXIT_FAILURE);
}
if (strcmp(argv[1], "client") == 0)
{
client();
}
else if (strcmp(argv[1], "server") == 0)
{
server();
}
else
{
printf("Usage: econnreset client | server\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}