I've been playing a bit with the sockets API to get a feel for how it works.
I wrote two little programs:
The server listens on 8080 for a stream connection. It sends anyone that connects to it a simple message.
The client connects to 127.0.0.1:8080, and dumps what it receives to stdout. It repeats this 20000 times in sequence.
The messages flow blazingly fast up to ~16370 times, then it pauses for tens of seconds before it starts moving really fast again to finish out the 20000 connections.
I've repeated this experiment a few times, and I've caught it at 16370, 16371 and 16372. It's surprisingly consistent on repeat experiments.
My question is: why does it need to pause after ~16370 iterations? What's the bottleneck here?
FWIW, I'm on macOS Sierra.
I run the server code like this:
clang -Wall -Werror -Wpedantic server.c -o server.out && ./server.out
and the client code like this:
clang -Wall -Werror -Wpedantic client.c -o client.out && time ./client.out
Here are the two programs:
server.c
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define PORT 8080
#define MAXMSG 512
int make_socket(int port) {
int sock;
struct sockaddr_in name;
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket");
exit(1);
}
name.sin_family = AF_INET;
name.sin_port = htons(port);
name.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sock, (struct sockaddr*) &name, sizeof(name)) < 0) {
perror("bind");
exit(1);
}
return sock;
}
int main(int argc, char** argv) {
const char hello[] = "Hello visitor ";
char buffer[MAXMSG];
int sk;
unsigned long count = 0;
strcpy(buffer, hello);
sk = make_socket(PORT);
listen(sk, 10);
printf("ready\n");
for (;;) {
count++;
sprintf(buffer + strlen(hello), "%lu", count);
int s = accept(sk, NULL, NULL);
if (send(s, buffer, strlen(buffer) + 1, 0) < 0) {
perror("send");
exit(1);
}
close(s);
printf("data socket (%d) message sent (%s)\n", s, buffer);
}
}
client.c
#include <arpa/inet.h>
#include <errno.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define PORT 8080
#define MAXMSG 512
int make_socket() {
int sock;
sock = socket(PF_INET, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket");
exit(1);
}
return sock;
}
int main(int argc, char** argv) {
char buffer[MAXMSG];
int sk;
size_t i;
struct sockaddr_in addr;
strcpy(buffer, "Hello world!");
for (i = 0; i < 20000; i++) {
sk = make_socket();
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
connect(sk, (struct sockaddr*) &addr, sizeof(addr));
recv(sk, buffer, strlen(buffer) + 1, 0);
close(sk);
printf("socket (%d) message = %s\n", sk, buffer);
}
}
This is some of the last stdout I get client side:
socket (3) message = Hello visitor 16369
socket (3) message = Hello visitor 16370
socket (3) message = Hello visitor 16371
socket (3) message = Hello visitor 16372
Most likely you are hitting a limit called the Ephemeral Port Range on your operating system. The same principle applies on all IP based operating systems.
Whenever a socket connection is made a port is allocated for the request and associated with the interface that the connection is made on. Once this socket is closed the port is put into a state called TIME_WAIT. Effectively putting the port on the bench for a period of time to ensure that is it not reused too soon. This is to prevent latent packets in the internet from coming in late and causing issues.
The ephermal port range is specified on Linux as /proc/sys/net/ipv4/ip_local_port_range.
You can display these on MacOS using:
sysctl net.inet.ip.portrange.first net.inet.ip.portrange.last
net.inet.ip.portrange.first: 49152
net.inet.ip.portrange.last: 65535
Which is 16,383 available ports in the ephemeral range.
To see all network parameters you can execute:
sysctl net.inet.tcp
You can change the TIME_WAIT values but for your high stress application it would simply lower the threshold at which your slowdown waits.
You can see the number of open connections using netstat -an. Sockets may be stuck in TIME_WAIT state if you are opening and closing a lot of connections. In some places this is unavoidable, but you may need to consider if you need a pool of connection if this is the case.
If TIME_WAIT is the issue there are system settings you can adjust. you can set net.ipv4.tcp_tw_reuse / net.ipv4.tcp_tw_recycle to speed up connection turnover.
One quick test would be to switch to another interface and try again. If you were using localhost and then experienced the slowdown you could then switch to your external IP on another interface and you should run like gangbusters until you hit the limit again.
This is not an issue with the language your using but an issue in socket based network usage.
You are probably dealing with the TIME_WAIT state of the sockets. After actively closing each connection socket in your server, the socket remains in that state for a long (tens of seconds) period. So, as #Blaze suggests, your program is reaching a resource limit and has to wait for this sockets to be definitively closed.
This feature is there to prevent two cases, the first is the chance that a delayed packet sent in one connection to be interpreted as part of a later connection. The second is the chance that the last ACK of the connection close is not received by the passive closing side of the connection, causing it to retransmit the FIN/ACK. If this happens and the active closing side has already closed the socket it will answer with a RST, causing the other side to receive an error in spite that all the information was correctly sent.
Take a look at this: http://www.serverframework.com/asynchronousevents/2011/01/time-wait-and-its-design-implications-for-protocols-and-scalable-servers.html
If you really want to completely close the socket (having the risk of the previous events to happen) you have to tune the SO_LINGER option before trying to close it.
struct linger immediate_linger;
immediate_linger.l_onoff = 1; /* Do linger on closing */
immediate_linger.l_linger = 0; /* Wait 0 seconds to linger after closing */
if (setsockopt(comm_socket, SOL_SOCKET, SO_LINGER, &immediate_linger, sizeof (immediate_linger))) {
err = errno;
printf("Error setting immediate linger for socket %d: %s.", comm_socket, strerror(err));
}
else {
printf("Set immediate linger after close for the socket %d.", comm_socket);
}
Related
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <pthread.h>
#include <arpa/inet.h>
struct sockaddr_in server, acc;
int sock;
int stuff(void) {
char buffer[2000];
recv(sock, buffer, sizeof(buffer), 0);
send(sock, "HTTP/1.1 200 OK\nContent-Type: text/html\n\nHello", sizeof("HTTP/1.1 200 OK\nContent-Type: text/html\n\nHello"), 0);
shutdown(sock, SHUT_RDWR);
return 0;
}
int main(void) {
int size;
sock = socket(AF_INET, SOCK_STREAM, 0);
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(1111);
if (bind(sock, (struct sockaddr*)&server, sizeof(server)) == -1) {
perror("Error:");
return 1;
}
while (1) {
listen(sock, 5);
sock = accept(sock, (struct sockaddr*)&acc, &size);
printf("accepted\n");
stuff();
close(sock);
}
}
This is meant to be an extremely basic HTTP server.
I don't know what's going wrong, but the connection seems to not end with the client.
Opening this in the browser, it doesn't stop loading and the console is flooded with "accepted" (line 42). No new connections can be accepted.
After your program accepts a connection, it forgets about the socket that listens for new connections, does remember the newly connected socket, does some stuff with that socket, closes it, then tries to accept another connection from the connected socket that it just closed.
There are two problems here:
It's the wrong socket
Even if it was the right socket, it's closed
You create socket number 3 (for example) to wait for new connections. You wait for a connection on socket 3 and the new connection is socket number 4 (for example). Then you do the stuff, then you close socket 4, then you wait for a new connection on socket 4. Do you see the mistake here? Socket 3 is the one that accepts connections.
Since you are not new to programming, you should be able to figure out how to make your program call accept with the right socket.
This is my first time socket programming and have a question about send and recv. I'm sending a file from server to client which works fine. But, when I want to continue my program with more send() and recv() calls (the commented code at the end of client and server), the client receives this data and writes it to newfile.txt.
So, my question is, how do I call send() from the server to only send data from test.txt, and then recv() on the client to only receive and write data from test.txt to newfile.txt? After this, I would want to continue with my program with more send() and recv() calls which dont get mixed up with the file transfer code.
Client:
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <arpa/inet.h>
int main(int argc, char *argv[]){
int port = 8001;
int client_socket;
struct sockaddr_in server_addr;
int connect_status;
client_socket = socket(AF_INET, SOCK_STREAM, 0); //SOCK_STREAM for TCP
if (client_socket < 0)
{
printf("Error creating socket\n");
exit(1);
}
printf("Socket created\n");
//address for socket to connect to
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port); //8001 arbitrary port
server_addr.sin_addr.s_addr = INADDR_ANY;
//connect to address of socket
connect_status = connect(client_socket, (struct sockaddr *) &server_addr, sizeof(server_addr));
if(connect_status == -1)
{
printf("Error connecting to server\n");
exit(1);
}
printf("Connected to Server\n");
FILE* fp = fopen( "newfile.txt", "w");
char data[512];
int b;
while((b=recv(client_socket, data, 512,0))>0){
fwrite(data, 1, b, fp);
}
fclose(fp);
//recv(client_socket, data, 512,0);
//printf("client buffer: %s\n", data);
close(client_socket);
return 0;
}
Server:
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
int main(int argc, char *argv[]){
int port = 8001;
int server_socket;
struct sockaddr_in server_addr;
int client_socket, client_addr;
int bind_status, listen_status;
server_socket = socket(AF_INET, SOCK_STREAM, 0); //SOCK_STREAM for TCP
if (server_socket < 0)
{
printf("Error creating socket\n");
exit(1);
}
printf("Socket created\n");
//address for socket to connect to
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
server_addr.sin_addr.s_addr = INADDR_ANY;
//bind socket to the IP and port
bind_status = bind(server_socket, (struct sockaddr *) &server_addr, sizeof(server_addr));
if (bind_status < 0)
{
printf("Error binding to socket\n");
exit(1);
}
printf("Socket binded\n");
//listen for client connections
listen_status = listen(server_socket, 10);
if (listen_status == -1)
{
printf("Error listening to socket\n");
exit(1);
}
printf("Listening\n");
//accept client connection
socklen_t addr_len;
addr_len = sizeof(client_socket);
client_socket = accept(server_socket, (struct sockaddr *) &client_addr, &addr_len);
FILE *fp = fopen("test.txt", "r");
char data[512];
int b;
while((b = fread(data, 1, 512, fp))>0){
send(client_socket, data, b, 0);
}
fclose(fp);
// strcpy(data,"test message");
//printf("server buffer: %s\n", data);
//send(client_socket, data, 512, 0);
close(server_socket);
return 0;
}
You need some way to indicate the server that you have finished sending a file, and now you want to send another thing.
While the socket abstraction seems to show you that the recv and send calls are somehow synchronized, this means that the data you send from the client in one call to send is recv'd in the server with exactly one recv, that is not true, due fundamentally to how the sockets are implemented (the client tcp can decide to split your transfer in several packets, and the unattending of the server can make all those packets to buffer n the receiver before the server receives part of them in one call to recve and others in the text call.
The only thing sure is that a byte that has been sent before, is receive before, and no repeated or missing bytes in between. But the number of bytes received at some recve call is dictated only by the amount of buffered data that one side of the connection has.
This means that, for telling the server that you are finished with your file and some other thing is to be sent, you must do something that allows the server to recognize that the data has ended and more data on a different thing is about to come.
There are several approaches here... you can send an inband sequence (some control sequence) that, wen read in the other side, will be recognized as the end of a block of data and the beginning of the next. Some systems use a packet encoding that simply prepends each block with a number of bytes to follow, and an empty packet (e.g. a single number 0, meaning 0 bytes to follow) can represent this sequence. Another way, can be to use a specific sequence you know is improbable to occur in the data (e.g. \n.\m, two newlines with one dot interspersed ---this has been used many times in unix's ed editor, for example, or the post office protocol uses it.) and if it is the case that such a sequence happens to be in the file, just double the dot, to indicate that it is not the separator sequence. And both ends must agree on this (so called) protocol.
Other mechanisms are valid, you can close the connection and use another socket for a new data exchange.... for example, FTP protocol uses this approach by using one control connection for FTP commands, while using other connections for data transfers.
I have not included code because there are plenty of examples in the literature. Try to get access to the book "Unix Network Programming" of Richard W. Stevens. That's a very good reference to get initiated on socket protocols and how to deal with all these things.
So I have a device connected to my network card and it sends data to port 11678 and address 192.168.121.1 using IPv4 and UDP. I have checked that the device does actually send to that port and address using IPv4 and UDP by calling tcpdump. However my C socket does not receive any packets. Below I have a minimum non-working example that just runs an infinite loop until one packet is received. It does not receive any packets even though tcpdump does, so I assume something is wrong with my code.
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#define DEST_ADDR "192.168.121.1"
#define DEST_PORT 11678
#define PACKET_MAXSIZE 1024
int main(int argc, char **argv) {
struct sockaddr_in dest_addr;
bzero(&dest_addr, sizeof(dest_addr));
dest_addr.sin_family = AF_INET;
/* create socket */
int fd = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0); // use SOCK_NONBLOCK?
if (fd < 0) {
perror("Could not create socket.");
}
/* bind port and address to socket */
dest_addr.sin_port = htons(DEST_PORT);
inet_aton(DEST_ADDR, &dest_addr.sin_addr);
int rc = bind(fd, (struct sockaddr*) &dest_addr, sizeof(dest_addr));
if (rc != 0) {
perror("Could not bind socket to local address");
}
/* read packets */
void* buf;
posix_memalign(&buf, 4096, 1024);
while (true) {
ssize_t read_size = read(fd, buf, PACKET_MAXSIZE);
printf("%d\n", read_size);
if (read_size > 0) {
break;
}
}
return 0;
}
The read just returns -1 and sets errno to 11 (EAGAIN) in every iteration. Any help is appreciated. Thanks in advance.
If you're on a system that uses iptables, check that you aren't dropping packets. tcpdump will show packets that are incoming before they get to iptables.
Another thing is that you should be using epoll or select to read from the socket in a more controlled way. EAGAIN isn't neccessarily wrong: it just means there's no data. But you're whizzing round that while loop without waiting, so I'd expect lots of EAGAIN's until something actually arrives at the port.
I wrote a simple application which connects to a given server on a given port. When the port is open, everything is ok, I got the message about the established connection. However, when the port is closed, nothing happens, my program does not show me the information about it.
I test my program using my remote server accessible via the Internet. How can I improve this?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
int main(int argc, char **argv) {
char * ip_addr;
int port;
int sockfd;
struct sockaddr_in server_addr;
if (argc != 3)
{
fprintf(stderr,"Usage: ./canconnect ip port\n");
exit(1);
}
ip_addr = argv[1];
port = atoi(argv[2]);
if (port <= 0)
{
fprintf(stderr,"error: invalid port\n");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket");
exit(1);
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(port);
inet_aton(ip_addr, &server_addr.sin_addr);
if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1)
{
perror("connect");
printf("Port %d is closed on server %s.\n", port, ip_addr);
exit(1);
}
else
{
printf("Connection established. Port %d is open on server %s.\n", port, ip_addr);
}
close(sockfd);
return 0; }
Given that the program actually prints some data after some time, it's most certainly has to do with timeout.
In order to finally decide that the host or the port is unreadable or the connection couldn't be established for any other reason, connect performs several attempts to connect and returns an error after a certain amount of time - the timeout.
The value of timeout can be changed to any value you want using setsockopt:
struct timeval timeout;
timeout.tv_sec = 3; // wait for three seconds
timeout.tv_usec = 0;
// set up receive timeout
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
sizeof(timeout)) < 0)
printf("[!] setsockopt failed\n");
If the remote server is reachable, but neither accepts nor refuses the connection, then connect() will block until the network connection attempt times out. As the Linux manual page for connect(2) puts it:
Note that for IP sockets the timeout may be very long when syncookies are enabled on the server.
Indeed, it is a well-known defense against port scans to attempt to elicit that behavior intentionally. You might be able to get quicker failures by using setsockopt() to set a receive timeout, but the docs are inconsistent on whether that will work for connect().
I've been approaching network programming these days, and I wrote two simple routines to check if I got it right. So I built the server on the desktop and started it, then I built the client on the laptop and I ran it, and everything went as expected. When I tried to run them the second time and on, the server kept looping and the client after two seconds gave "Error connecting!". If I try again after fifteen minutes it works, but then I have to wait again. Where am I wrong? The computers are both connected to my LAN, 79.13.199.165 is the IP of my modem/router, which forwards every incoming connection on port 53124 to the desktop. This problem doesn't occur when running both server and client on the same PC.
server.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
int main () {
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = htons(53124);
sa.sin_addr.s_addr = htonl(INADDR_ANY);
memset(sa.sin_zero, '\0', sizeof(sa.sin_zero));
int mysocket = socket(PF_INET, SOCK_STREAM, 0);
bind(mysocket, (struct sockaddr*)&sa, sizeof(sa));
listen(mysocket, 5);
int inc;
struct sockaddr_in inc_addr;
socklen_t inc_addr_size = sizeof(inc_addr);
inc = accept(mysocket, (struct sockaddr*)&inc_addr, &inc_addr_size);
if (inc != -1) {
printf("accepting client\n");
}
send(inc, "ciao", sizeof("ciao"), 0);
close(inc);
return 0;
}
client.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
int main () {
struct sockaddr_in sa;
sa.sin_family = AF_INET;
sa.sin_port = htons(53124);
sa.sin_addr.s_addr = inet_addr("79.13.199.165");
memset(sa.sin_zero, '\0', sizeof(sa.sin_zero));
int mysocket = socket(PF_INET, SOCK_STREAM, 0);
if (mysocket == -1) {
printf("Could not create socket!\n");
}
if (connect(mysocket, (struct sockaddr*)&sa, sizeof(sa)) == -1) {
printf("Error connecting!\n");
}
char message[5];
memset(message, '\0', sizeof(message));
recv(mysocket, message, 5, 0);
printf("%s\n", message);
return 0;
}
When the server closes the socket, a couple of ACK packets get sent backwards and forwards across the connection. There's no way to tell if the last ACK gets delivered successfully, so the connection goes into the TIME_WAIT state for a bit. This basically gives the TCP stack time to wait for any lost packets and throw them away.
It's possible to ignore this and reuse the socket straight away by setting SO_REUSEADDR using setsockopt(). There is a small danger that subsequent connects might get data they weren't supposed to but it shouldn't be a problem for your little test application.
Edit
By the way, one reason why you were probably getting confused by this is that you don't do any error checking on socket() bind() or listen(). The bind() call would certainly return an error and set errno, EINVAL on Linux.