unexpected socket closure on server side - c

I am running a sample libevent based server, however, sometimes, this server will immediately send a TCP FIN immediately after TCP handshake according to wireshark capture. The server didn't crash. The OS is ubuntu 18.04, the terminal has ulimit -n being 100000.
Any idea why?
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
int printed = 0;
static void echo_read_cb(struct bufferevent *bev, void *ctx) {
/* This callback is invoked when there is data to read on bev. */
struct evbuffer *input = bufferevent_get_input(bev);
struct evbuffer *output = bufferevent_get_output(bev);
//evbuffer_add_buffer(output, input); //if I replace the next line with this, same issue of unexpected server side closure on some sockets
evbuffer_add(bufferevent_get_output(bev), "a", 1);
}
static void echo_event_cb(struct bufferevent *bev, short events, void *ctx) {
if (events & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
int fd = bufferevent_getfd(bev);
evutil_closesocket(fd);
bufferevent_free(bev);
return;
}
}
static void
accept_conn_cb(struct evconnlistener *listener,
evutil_socket_t fd, struct sockaddr *address, int socklen,
void *ctx)
{
/* We got a new connection! Set up a bufferevent for it. */
struct event_base *base = evconnlistener_get_base(listener);
struct bufferevent *bev = bufferevent_socket_new(
base, fd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, echo_read_cb, NULL, echo_event_cb, NULL);
bufferevent_enable(bev, EV_READ|EV_WRITE);
}
static void
accept_error_cb(struct evconnlistener *listener, void *ctx)
{
struct event_base *base = evconnlistener_get_base(listener);
int err = EVUTIL_SOCKET_ERROR();
fprintf(stderr, "Got an error %d (%s) on the listener. "
"Shutting down.\n", err, evutil_socket_error_to_string(err));
event_base_loopexit(base, NULL);
}
int
main(int argc, char **argv)
{
struct event_base *base;
struct evconnlistener *listener;
struct sockaddr_in sin;
int port = 9876;
if (argc > 1) {
port = atoi(argv[1]);
}
if (port<=0 || port>65535) {
puts("Invalid port");
return 1;
}
base = event_base_new();
if (!base) {
puts("Couldn't open event base");
return 1;
}
/* Clear the sockaddr before using it, in case there are extra
* platform-specific fields that can mess us up. */
memset(&sin, 0, sizeof(sin));
/* This is an INET address */
sin.sin_family = AF_INET;
/* Listen on 0.0.0.0 */
sin.sin_addr.s_addr = htonl(0);
/* Listen on the given port. */
sin.sin_port = htons(port);
listener = evconnlistener_new_bind(base, accept_conn_cb, NULL,
LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, 10000,
(struct sockaddr*)&sin, sizeof(sin));
if (!listener) {
perror("Couldn't create listener");
return 1;
}
evconnlistener_set_error_cb(listener, accept_error_cb);
event_base_dispatch(base);
return 0;
}

Turned out there is no need to have evutil_closesocket(fd) in the function echo_event_cb. It's wrong to have it since it will result in a double-free.
As to why the server program didn't crash, that's a miracle :-)

Related

libevent based echo server got stuck

Here is an libevent based on echoServer with a little bit of my tweak (very minor).
The question is, when running a client simulation again, it appears to be stuck. Here is how the client does: it open a TCP connection to this server, send a small data (about 20B) and wait for a reply, then it close the connection with TCP RST and then repeat.
After thousands (sometimes ~20000) iterations, the client stopped.
Wireshark listening on loopback interface showed that when it stopped, the last TCP session has data sent from client, but the server was not echoing/sending it back to client, causing the client to hang.
Any ideas? Thanks in advance!
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#include <netinet/tcp.h>
#include <event.h>
#include <event2/listener.h>
#include <event2/bufferevent_ssl.h>
struct event_base *base;
static void echo_read_cb(struct bufferevent *bev, void *ctx) {
/* This callback is invoked when there is data to read on bev. */
struct evbuffer *input = bufferevent_get_input(bev);
struct evbuffer *output = bufferevent_get_output(bev);
/* Copy all the data from the input buffer to the output buffer. */
int ret = evbuffer_add_buffer(output, input);
if (ret) {
printf("error happened when adding buf\n");
}
}
static void echo_event_cb(struct bufferevent *bev, short events, void *ctx) {
if (events & (BEV_EVENT_EOF|BEV_EVENT_ERROR)) {
int fd = bufferevent_getfd(bev);
evutil_closesocket(fd);
bufferevent_free(bev);
return;
}
}
static void accept_error_cb(struct evconnlistener *listener, void *ctx) {
struct event_base *base = evconnlistener_get_base(listener);
int err = EVUTIL_SOCKET_ERROR();
fprintf(stderr, "Got an error %d (%s) on the listener. "
"Shutting down.\n", err, evutil_socket_error_to_string(err));
}
static void accept_conn_cb(struct evconnlistener *serv, int sock, struct sockaddr *sa,
int sa_len, void *arg) {
struct bufferevent *bev = bufferevent_socket_new(
base, sock, BEV_OPT_CLOSE_ON_FREE);
int flag = 1;
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));
bufferevent_setcb(bev, echo_read_cb, NULL, echo_event_cb, NULL);
bufferevent_enable(bev, EV_READ|EV_WRITE);
}
int main(int argc, char **argv) {
struct evconnlistener *listener;
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(9999);
sin.sin_addr.s_addr = htonl(0x7f000001); /* 127.0.0.1 */
base = event_base_new();
listener = evconnlistener_new_bind(base, accept_conn_cb, NULL,
LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1,
(struct sockaddr*)&sin, sizeof(sin));
if (!listener) {
perror("Couldn't create listener");
return 1;
}
evconnlistener_set_error_cb(listener, accept_error_cb);
event_base_loop(base, 0);
evconnlistener_free(listener);
return 0;
}
Figured out the cause of the problem: in the function echo_event_cb, there is a call evutil_closesocket(fd); This is not only unnecessary, it's harmful! After the call to close socket is removed, it works perfect.
It's unnecessary because the bufferedevent was created with BEV_OPT_CLOSE_ON_FREE.

eventbuffer can only read 8 bytes on input?

I am working through some examples on libevent and am running into a problem with reading any string over 8 bytes from the input buffer. I'm going between two computers on my local network. Here is my code for the simple libevent server:
#include <sys/types.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <event2/listener.h>
#include <event2/bufferevent.h>
#include <event2/buffer.h>
#define PORTNUM 8080
static void echo_read_cb(struct bufferevent *bev, void *ctx)
{
struct evbuffer *input = bufferevent_get_input(bev);
struct evbuffer *output = bufferevent_get_output(bev);
size_t len = evbuffer_get_length(input);
char *data = malloc(len);
evbuffer_copyout(input, data, len);
printf("We got some data: %s\n", data);
free(data);
}
static void echo_event_cb(struct bufferevent *bev, short events, void *ctx)
{
if (events & BEV_EVENT_ERROR)
perror("Error from bufferevent.");
if (events & (BEV_EVENT_EOF | BEV_EVENT_ERROR)) {
bufferevent_free(bev);
}
}
static void accept_conn_cb(struct evconnlistener *listener,
evutil_socket_t fd, struct sockaddr *addr,
int socklen, void *ctx)
{
struct event_base *base = evconnlistener_get_base(listener);
struct bufferevent *bev = bufferevent_socket_new(base, fd, BEV_OPT_CLOSE_ON_FREE);
bufferevent_setcb(bev, echo_read_cb, NULL, echo_event_cb, NULL);
bufferevent_enable(bev, EV_READ|EV_WRITE);
}
static void accept_error_cb(struct evconnlistener *listener, void *ctx) {
struct event_base *base = evconnlistener_get_base(listener);
int err = EVUTIL_SOCKET_ERROR();
fprintf(stderr, "Got an error %d (%s) on the listener. "
"Shutting down.\n", err, evutil_socket_error_to_string(err));
event_base_loopexit(base, NULL);
}
int main(int argc, char **argv)
{
struct event_base *base;
struct evconnlistener *listener;
struct sockaddr_in sin;
base = event_base_new();
if (!base) {
puts("Couldn't open event base\n");
return 1;
}
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(PORTNUM);
sin.sin_addr.s_addr = htonl(INADDR_ANY);
listener = evconnlistener_new_bind(base, accept_conn_cb, NULL,
LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE,
-1, (struct sockaddr *)&sin,
sizeof(sin));
if (!listener) {
perror("Couldn't create listener.\n");
return 1;
}
evconnlistener_set_error_cb(listener, accept_error_cb);
event_base_dispatch(base);
return 0;
}
This program continues to run while on another computer on the local network I run a simple client program using regular networking:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
struct sockaddr_in serv;
memset(&serv, 0, sizeof(serv));
serv.sin_family = AF_INET;
serv.sin_port = htons(8080);
inet_aton("192.168.1.155", &(serv.sin_addr));
int SOCKET = socket(AF_INET, SOCK_STREAM, 0);
connect(SOCKET, (struct sockaddr *)&serv, sizeof(struct sockaddr_in));
const char *s = "Messagefromtheclient.";
int n = send(SOCKET, s, sizeof(s), 0);
close(SOCKET);
return EXIT_SUCCESS;
}
I run this simple client program, which should write to the simple server made using evbuffer and output the data to stdout. The server writes to stdout:
> We got some data: Messagef
So data is only 8 bytes, the rest of the string gets truncated. Can anyone tell me why?
You are passing the size of a pointer (which happens to be 8 on your 64-bit platform) here:
int n = send(SOCKET, s, sizeof(s), 0);
^^^^^^^^^
You need to instead pass the number of characters in the string (+ 1 to include the NUL terminator):
int n = send(SOCKET, s, strlen(s) + 1, 0);
^^^^^^^^^^^^^

Libuv UDP basic send

When I try to send some basic UDP message it looks like it doesn't send it.
I tried to run couple of examples that I found online.
I am using SocketTest v3.0.0 for testing server/client.
When I tested TCP sending everything worked as expected, but I UDP case it simply doesn't.
Can you provide some really basic UDP send code snippet ?
For example the receiver will be on localhost and the message will contain "test" and it will be fixed length ?
And I have one more small question. It I send the message on local host can the application(server) itself receive it and think that someone else has send it (causing looping) because it does not connect to some peer it just send the message and listen on same ip-port ?
Thanks :)
You have a lot of usefull examples here: https://nikhilm.github.io/uvbook/networking.html
uv_loop_t *loop;
uv_udp_t send_socket;
uv_udp_t recv_socket;
int main() {
loop = uv_default_loop();
uv_udp_init(loop, &recv_socket);
struct sockaddr_in recv_addr;
uv_ip4_addr("0.0.0.0", 68, &recv_addr);
uv_udp_bind(&recv_socket, (const struct sockaddr *)&recv_addr, UV_UDP_REUSEADDR);
uv_udp_recv_start(&recv_socket, alloc_buffer, on_read);
uv_udp_init(loop, &send_socket);
struct sockaddr_in broadcast_addr;
uv_ip4_addr("0.0.0.0", 0, &broadcast_addr);
uv_udp_bind(&send_socket, (const struct sockaddr *)&broadcast_addr, 0);
uv_udp_set_broadcast(&send_socket, 1);
uv_udp_send_t send_req;
uv_buf_t discover_msg = make_discover_msg();
struct sockaddr_in send_addr;
uv_ip4_addr("255.255.255.255", 67, &send_addr);
uv_udp_send(&send_req, &send_socket, &discover_msg, 1, (const struct sockaddr *)&send_addr, on_send);
return uv_run(loop, UV_RUN_DEFAULT);
}
Here is a basic example that shows sending UDP packets and receiving the response from the other side:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <uv.h>
uv_udp_t udp_socket;
void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
buf->base = malloc(suggested_size);
buf->len = suggested_size;
}
void on_read(uv_udp_t *socket, ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned flags){
if (nread < 0) {
fprintf(stderr, "read error %s\n", uv_err_name(nread));
uv_close((uv_handle_t*) socket, NULL);
} else if (nread > 0) {
char sender[17] = { 0 };
uv_ip4_name((const struct sockaddr_in*) addr, sender, 16);
printf("recv from %s:%.*s\n", sender, (int)buf->len, buf->base);
}
if (buf && buf->base) {
/* releases the buffer allocated on alloc_buffer() */
free(buf->base);
}
}
void on_send(uv_udp_send_t *req, int status) {
if (status) {
fprintf(stderr, "send error %s\n", uv_strerror(status));
}
/* releases the request allocated on send_msg() */
if (req) free(req);
}
void send_msg(char *msg){
uv_buf_t buf = uv_buf_init(msg, strlen(msg)+1);
struct sockaddr_in send_addr;
uv_ip4_addr("123.45.67.89", 2222, &send_addr);
uv_udp_send_t *send_req = malloc(sizeof(uv_udp_send_t));
uv_udp_send(send_req, &udp_socket, &buf, 1, (const struct sockaddr *)&send_addr, on_send);
}
int main() {
uv_loop_t *loop = uv_default_loop();
uv_udp_init(loop, &udp_socket);
struct sockaddr_in recv_addr;
uv_ip4_addr("0.0.0.0", 2345, &recv_addr);
uv_udp_bind(&udp_socket, (const struct sockaddr *)&recv_addr, UV_UDP_REUSEADDR);
uv_udp_recv_start(&udp_socket, alloc_buffer, on_read);
send_msg("hi there!");
send_msg("hello world");
return uv_run(loop, UV_RUN_DEFAULT);
}
You may try stream route handler, although is new, but it's reliable and had tested with heavy traffic data transaction.
Example
void read_data(srh_request_t *req);
void read_data(srh_request_t *req) {
char *a = "CAUSE ERROR FREE INVALID";
if (strncmp( (char*)req->in_buff->start, "ERROR", 5) == 0) {
free(a);
}
// printf("%d, %.*s\n", i++, (int) (req->in_buff->end - req->in_buff->start), req->in_buff->start);
srh_write_output_buffer_l(req, req->in_buff->start, (req->in_buff->end - req->in_buff->start));
// printf("%d, %.*s\n", i++, (int) (req->out_buff->end - req->out_buff->start), req->out_buff->start);
}
int main(void) {
srh_instance_t * instance = srh_create_routing_instance(24, NULL, NULL);
srh_add_udp_fd(instance, 12345, read_data, 1024, NULL);
srh_add_tcp_fd(instance, 3232, read_data, 64, NULL);
srh_start(instance, 1);
return 0;
}

Use libpcap and sockets to transmit a packet whenever you receive

I am trying to make a program, so that whenever I receive a packet in an interface from a specific MAC address I want to transmit another packet (the reply packets contained in a pcap file). The program below partially works.It has two problems.
There is no way to achieve synchronization, i.e. transmit whenever I receive.
Additionally, for some unknown reason,despite the replay pcap file that I want to use as a response to every packet that I receive,contains 65000 packets, only 800 are transmitted (unsynchronized again) and the program terminates with no errors...
Any thoughts? Thank you!
#include <time.h>
#include <pcap.h>
#include <libnet.h>
#include <signal.h>
#include <sys/wait.h>
#include <inttypes.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <net/ethernet.h>
#include <netpacket/packet.h>
#include <netinet/if_ether.h>
int make_raw_sock(const char *interface, struct sockaddr_ll *sa);
int main(int argc, char *argv[])
{
const char *srcmac;
char mac[]={0x28,0x63,0x36,0x91,0x3c,0x0e};
//SET UP FOR INJECTING SOCKET
//Buffer to send data.
char buf[1514];
struct sockaddr_ll sa;
const u_char *packetstored;
const u_char *packetrx;
struct pcap_pkthdr headerst;
struct pcap_pkthdr headerrx;
pcap_t *mypcapfile;
pcap_t *p;
//Create the socket for injection.
int ps = 0;
ps = make_raw_sock("lo", &sa);
if (ps<0)
{
fprintf(stderr,"Failure in socket creation\n");
exit(-1);
}
//PARAMETERS FOR LIBPCAP
//The maximum number of packets to be captured.
//uint32_t count = 250000;
//The snapshot lenght.
uint32_t snaplen = 1514;
//Buffer for errors.
char errbuf[PCAP_ERRBUF_SIZE];
//Set interface in promiscuous mode.
int promisc = 1;
//timeout, in milliseconds.
int to_ms = 1000;
fprintf(stdout,"Parent process id = %d\n",getpid());
if (!(p = pcap_open_live("lo", snaplen, promisc, to_ms, errbuf)))
{
fprintf(stderr, "Error opening interface %s: %s\n","lo", errbuf);
exit(EXIT_FAILURE);
}
mypcapfile = pcap_open_offline("replay3.pcap", errbuf);
if (mypcapfile == NULL)
{
printf("Error pcap_open_offline(): %s\n", errbuf);
exit(EXIT_FAILURE);
}
int counter;
while (1)
{
packetrx = pcap_next(p, &headerrx);
srcmac = packetrx;
if (packetrx == NULL)
{
break;
}
else
{
if (!strncmp(srcmac,mac,6))
{
packetstored = pcap_next(mypcapfile, &headerst);
memcpy(buf,packetstored,headerst.caplen);
sendto(ps, buf, headerst.caplen, 0, (struct sockaddr*)&sa, sizeof(sa));
counter = counter + 1;
}
}
}
fprintf(stdout,"Packets sent:%d\n",counter);
return (0);
}
int make_raw_sock(const char *interface, struct sockaddr_ll *sa)
{
int ps;
struct ifreq ifr;
ps = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
if (ps < 0)
{
return ps;
}
strcpy(ifr.ifr_name, interface);
if (ioctl(ps, SIOCGIFINDEX, &ifr) != 0)
{
close(ps);
return -1;
}
memset(sa, 0, sizeof(*sa));
sa->sll_family = AF_PACKET;
sa->sll_ifindex = ifr.ifr_ifindex;
sa->sll_halen = sizeof(struct ether_addr);
return ps;
}

Can't finish transferring a file when using a multi-threaded server in C

I have a multi-threaded client that can transfer a batch of files to a new directory that the client itself makes. My client used to use a single-threaded server.
For an assignment I'm supposed to transform my single-threaded server into a multi-threaded server that creates a new thread for each client request. I'm also supposed to time the whole operation and output it to the client (which I got to work when the server was single threaded). The code for both the multi-threaded client (which works with a single-thread server) and the multi-thread server (which does not work well) are below:
client.c
#include <sys/uio.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <netdb.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
#include <string.h>
#include <strings.h>
#include "Timer.h"
#define N_THREADS 10
char * files[] = {
"/usr/share/dict/words",
"/usr/include/sqlite3.h",
"/usr/include/tclDecls.h",
"/usr/include/bfd.h",
"/usr/include/libmng.h",
"/usr/include/elf.h",
"/usr/include/gmpxx.h",
"/usr/include/tkDecls.h",
"/usr/include/H5overflow.h",
"/usr/include/tcl.h",
"/usr/include/gmp-x86_64.h",
"/usr/include/curses.h",
"/usr/include/lcms.h",
"/usr/include/netapi.h",
"/usr/include/gcrypt.h",
"/usr/include/zlib.h",
"/usr/include/ldap.h",
"/usr/include/geos_c.h",
"/usr/include/kdb.h",
"/usr/include/tk.h",
"/usr/include/yaml.h"
};
#define files_length() (sizeof files / sizeof files[0])
void error(char *msg)
{
perror(msg);
exit(-1);
}
struct sockaddr_in make_server_addr(char *host, short port)
{
struct sockaddr_in addr;
bzero(&addr, sizeof addr);
struct hostent *hp = gethostbyname(host);
if ( hp == 0 )
error(host);
addr.sin_family = AF_INET;
bcopy(hp->h_addr_list[0], &addr.sin_addr, hp->h_length);
addr.sin_port = htons(port);
return addr;
}
int connect_socket(char *host, short port)
{
int status;
int tries = 3;
struct sockaddr_in addr = make_server_addr(host, port);
int s = socket(AF_INET, SOCK_STREAM, 0);
if ( s == -1 )
error("socket()");
status = connect(s, (struct sockaddr*)&addr, sizeof addr);
if ( status < 0 )
error("connect refused");
return s;
}
void request_file_from_server(int server_socket, char *file)
{
int len = strlen(file);
int n = write(server_socket, file, len);
if ( n != len )
error("short write");
}
void read_file_from_server(int server_socket, char *file)
{
char buf[BUFSIZ];
int n;
mode_t mode = 0666;
int ofd = open(file, O_WRONLY | O_CREAT, mode);
if ( ofd == -1 )
perror("open()");
while ( (n = read(server_socket, buf, BUFSIZ)) > 0 )
write(ofd, buf, n);
close(ofd);
}
struct Thread_data
{
int id;
pthread_t thread_id;
char * host;
short port;
char path[BUFSIZ];
};
void make_file_name(char *local_name, char *dir, char *original_path)
{
char *p = rindex(original_path, '/');
if ( !p )
error("rindex()");
sprintf(local_name, "%s/%s", dir, p+1);
}
int remote_copy(struct Thread_data * data, char * file)
{
int server_socket = connect_socket(data->host, data->port);
request_file_from_server(server_socket, file);
char local_name[BUFSIZ];
make_file_name(local_name, data->path, file);
read_file_from_server(server_socket, local_name);
close(server_socket);
}
void make_empty_dir_for_copies(struct Thread_data * data)
{
mode_t mode = 0777;
sprintf(data->path, "./Thread_%d", (data->id + 1));
mkdir(data->path, mode);
}
#define N_FILES_TO_COPY files_length() // copy them all
void *thread_work(void *arg)
{
struct Thread_data * data = (struct Thread_data *)arg;
make_empty_dir_for_copies(data);
for ( int i=0; i < N_FILES_TO_COPY; ++i )
remote_copy(data, files[i]);
pthread_exit(0);
}
void start_threads(char *host, short port, struct Thread_data thread_args[])
{
for ( int i = 0; i < N_THREADS; ++i )
{
struct Thread_data * t = &thread_args[i];
t->id = i;
t->host = host;
t->port = port;
pthread_create(&t->thread_id, NULL, thread_work, t);
}
}
void join_threads(struct Thread_data thread_args[], double *eTime)
{
for ( int i=0; i < N_THREADS; i++ )
pthread_join(thread_args[i].thread_id, NULL);
Timer_elapsedUserTime(eTime);
printf("Elapsed time for transferring all files: %lf\n", *eTime);
pthread_exit(0);
}
int main(int argc, char *argv[])
{
if ( argc != 3 )
{
fprintf(stderr, "Usage: %s host port\n", argv[0]);
exit(-1);
}
struct Thread_data thread_args[N_THREADS];
char *host = argv[1];
short port = atoi(argv[2]);
double eTime;
Timer_start();
start_threads(host,port,thread_args);
join_threads(thread_args, &eTime);
}
server.c
#include <sys/types.h>
#include <signal.h>
#include <sys/uio.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <pthread.h>
#include <string.h>
#include "Timer.h"
#define BACKLOG 200
// more than this in the queue, and client connect will fail
#define NUM_THREADS 200
void error(char *msg)
{
fprintf(stderr, "%s\n", msg);
exit(-1);
}
struct sockaddr_in make_server_addr(short port)
{
struct sockaddr_in addr;
memset(&addr, 0, sizeof addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
return addr;
}
int create_server_socket(short port)
{
int s = socket(AF_INET, SOCK_STREAM, 0);
int optval = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof optval);
struct sockaddr_in my_addr = make_server_addr(port);
if ( s == -1 )
error("socket()");
bind(s, (struct sockaddr*)&my_addr, sizeof my_addr);
listen(s, BACKLOG);
return s;
}
void get_file_request(int socket, char *fileName)
{
char buf[BUFSIZ];
int n = read(socket, buf, BUFSIZ);
if ( n < 0 )
error("read from socket");
buf[n] = '\0';
strcpy(fileName, buf);
printf("Server got file name of '%s'\n", fileName);
}
void write_file_to_client_socket(char *file, int socket)
{
char buf[BUFSIZ];
int n;
int ifd = open(file, O_RDONLY);
if ( ifd == -1 )
error("open()");
while ( (n = read(ifd, buf, BUFSIZ)) > 0 )
write(socket, buf, n);
close(ifd);
}
void * handle_request(void * c_socket)
{
int *client_socket = (int*)c_socket;
char fileName[BUFSIZ];
get_file_request(*client_socket, fileName);
write_file_to_client_socket(fileName, *client_socket);
close(*client_socket);
pthread_exit(0);
return NULL;
}
void time_out(int arg)
{
fprintf(stderr, "Server timed out\n");
exit(0);
}
void set_time_out(int seconds)
{
struct itimerval value = {0};
// bzero(&value, sizeof value);
/* timerclear(&value.it_interval); timerclear(&value.it_value); */
value.it_value.tv_sec = seconds;
setitimer(ITIMER_REAL, &value, NULL);
signal(SIGALRM, time_out);
}
void accept_client_requests(int server_socket)
{
pthread_t threads;
int client_socket;
struct sockaddr_in client_addr;
socklen_t sin_size = sizeof client_addr;
set_time_out(10);
while ( (client_socket =
accept(server_socket, (struct sockaddr*)&client_addr, &sin_size)) )
{
set_time_out(10);
pthread_create(&threads,0, handle_request,&client_socket);
}
}
int main(int argc, char *argv[])
{
if ( argc != 2 )
error("usage: server port");
short port = atoi(argv[1]);
int server_socket = create_server_socket(port);
accept_client_requests(server_socket);
shutdown(server_socket, 2);
return 0;
}
The issue happens when using handle_request when I am using accept and creating a new pthread. It gets to whatever the last file is (in this case /usr/include/yaml.h), hangs and then times out. Without a timeout it would hang indefinitely.
I don't really know much about multi-threading with pthreads, so I'm just going off of my professors instructions which basically said to create the thread and handle the request like you would in a single threaded server. In my single threaded server, handle_request was passed in an int (which now gets converted).
Does anyone know why my server would hang on the last transferred file until it times out?
There's a flaw in the accept_client_requests function. You have a variable
int client_socket;
The address of that variable is passed to pthread_create
pthread_create(&threads,0, handle_request,&client_socket);
pthread_create passes the pointer to handle_request which stores it as a local pointer
int *client_socket = (int *)c_socket;
The problem is that the pointer is still pointing to the client_socket variable in the accept_client_requests function. So when accept_client_requests gets another connection, the client_socket is changed, and every thread currently running has its client_socket changed, which should cause all sorts of chaos.
The solution is to malloc an int to hold the client_socket, and then pass that address to the thread.
int *temp = malloc( sizeof(int) );
*temp = client_socket;
pthread_create(&threads, 0, handle_request, temp);
pthread_detach(threads);
When the thread is finished, it should free the memory.
The accept_client_requests function should also call pthread_detach on every thread that it creates, so that resources can be reclaimed when the thread finishes.
Without the pthread_detach the system will expect to see a pthread_join before cleaning up the thread.

Resources