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);
^^^^^^^^^^^^^
Related
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 :-)
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.
I'm trying to use a mavlink sample developed by Parrot to control a Bebop2 using mavlink protocol (link here).
In order to send a message to the drone, they use the sendto function but I encounter an issue I cannot resolve : everytime I try to make the program works, I recieve an error and after a bit of investigation, I found that it's the 'sendto' used in this code (in the mavlink_comm_send_msg function) that returns an EINVAL error type (code located inside the mavlink_comm.c file in the src directory) :
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>
#include <sys/time.h>
#include <poll.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <netdb.h>
#include <mavlink.h>
#include "mavlink_comm.h"
#define MAVLINK_COMM_BUFSIZE 4096
struct mavlink_comm {
int sock;
struct sockaddr_in remote_addr;
unsigned char tx_buffer[MAVLINK_COMM_BUFSIZE];
unsigned int tx_bufidx;
unsigned char rx_buffer[MAVLINK_COMM_BUFSIZE];
unsigned int rx_bufidx;
void (*cb)(mavlink_message_t *msg, void *user_data);
void *user_data;
};
struct mavlink_comm *mavlink_comm_new(struct mavlink_comm_cfg *cfg)
{
struct sockaddr_in locAddr;
struct mavlink_comm *c;
struct hostent *server;
if (!cfg)
return NULL;
c = calloc(1, sizeof(*c));
if (!c)
return NULL;
c->cb = cfg->cb;
c->user_data = cfg->user_data;
c->sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
memset(&locAddr, 0, sizeof(locAddr));
locAddr.sin_family = AF_INET;
locAddr.sin_addr.s_addr = INADDR_ANY;
locAddr.sin_port = htons(cfg->local_port);
if (cfg->remote_addr && cfg->remote_addr[0] != '\0') {
server = gethostbyname(cfg->remote_addr);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host %s\n",
cfg->remote_addr);
exit(0);
}
bzero((char *) &c->remote_addr, sizeof(c->remote_addr));
c->remote_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&c->remote_addr.sin_addr.s_addr,
server->h_length);
c->remote_addr.sin_port = htons(cfg->remote_port);
}
/* Bind the socket to port 14551
* necessary to receive packets from qgroundcontrol */
if (-1 == bind(c->sock,(struct sockaddr *)&locAddr,
sizeof(struct sockaddr))) {
perror("error bind failed");
goto exit_close;
}
return c;
exit_close:
close(c->sock);
free(c);
return NULL;
}
static inline int mavlink_comm_send_data_internal(struct mavlink_comm *c)
{
int size = sizeof(struct sockaddr_in);
return sendto(c->sock, c->tx_buffer, c->tx_bufidx, 0,
(struct sockaddr*)&c->remote_addr,
sizeof(struct sockaddr_in));
}
int mavlink_comm_send_msg(struct mavlink_comm *c, mavlink_message_t *msg)
{
if (!c || !msg)
return -EINVAL;
int len = mavlink_msg_to_send_buffer(c->tx_buffer, msg);
return sendto(c->sock, c->tx_buffer, len, 0,
(struct sockaddr*)&c->remote_addr,
sizeof(struct sockaddr_in));
}
I looked at other related posts and tried several things to make it work but none of them were successful. The cast seems correct and I verified dest_len argument, so I'm quite lost.
Thanks a lot for your help.
(sorry by advance for my English...)
I am working on a network between a PC and a set of Zedboards connected by Ethernet. I use the UDP protocol, and here's my problem:
- All my cards and my PC are correctly connected to a switch and have their own IPs (192.168.1.15 for PC , and 11/12 for cards).
- When I try to send a simple packet with unix sockets with the code below, only the last message is received (but both seem to be send) (plus, no error or warning on compilation)
- If I switch the two sending, it is always the last which arrive at destination so it is not a card problem...
main.c
#include "send_tool.h"
static int PORT;
static char LOCAL_ADDRESS[50];
static SOCKADDR_IN client_list[NB_CLIENTS];
static int uc_sock;
int main(int argc, char **argv)
{
char buffer[BUF_SIZE];
int actual;int i;
memset(buffer,0, sizeof(buffer));
SOCKADDR_IN csin = { 0 };
if(argc < 3)
{
PORT = 2005;
sprintf(LOCAL_ADDRESS, "192.168.1.15");
printf("Valeurs de parametre forcees: %d\t%s\n", PORT, LOCAL_ADDRESS);
}
else
{
PORT = atoi(argv[1]);
sprintf(LOCAL_ADDRESS, argv[2]);
}
uc_sock = init_UC_connection(LOCAL_ADDRESS, PORT);
while(actual < NB_CLIENTS)
{
read_UDP(uc_sock, &csin, buffer);
if(!strncmp(buffer, "free:",5))
{
//add_client(client_list, csin);
client_list[actual].sin_addr.s_addr = csin.sin_addr.s_addr;
client_list[actual].sin_port = csin.sin_port;
client_list[actual].sin_family = csin.sin_family;
memcpy(client_list[actual].sin_zero, csin.sin_zero, 8);
//sprintf(buffer, "salut");
//write_UDP(uc_sock, &client_list[actual], buffer, strlen(buffer));
actual++;
}
}
printf("************************************\n");
printf("0: %s:%d/%d\n", inet_ntoa(client_list[0].sin_addr), ntohs(client_list[0].sin_port), client_list[0].sin_family);
printf("1: %s:%d/%d\n", inet_ntoa(client_list[1].sin_addr), ntohs(client_list[1].sin_port), client_list[1].sin_family);
printf("************************************\n");
sprintf(buffer, "au revoir");
write_UDP(uc_sock, &client_list[0], buffer, strlen(buffer));
write_UDP(uc_sock, &client_list[1], buffer, strlen(buffer));
memset(buffer, 0, sizeof(buffer));
while(read_UDP(uc_sock, &csin, buffer) > 0){}
close(uc_sock);
return 0;
}
send_tool.h
/* STD LIBS */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
//#include <sys/types.h>
//#include <sys/stat.h>
/* SOCKETS */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
/* TIME */
#include <sys/timerfd.h>
#include <sys/time.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
#define BUF_SIZE 60000
#define MULTICAST_ADDRESS "224.0.0.1"
#define NB_CLIENTS 2
typedef struct sockaddr_in SOCKADDR_IN;
typedef struct sockaddr SOCKADDR;
typedef struct in_addr IN_ADDR;
int init_UC_connection(char LOCAL_ADDRESS[], int port);
void write_UDP(int sock, SOCKADDR_IN *sin, const char *buffer, size_t buff_len);
int read_UDP(int sock, SOCKADDR_IN *sin, char *buffer);
send_tool.c
#include "send_tool.h"
int init_UC_connection(char LOCAL_ADDRESS[], int port)
{
/* UDP so SOCK_DGRAM */
int sock = socket(AF_INET, SOCK_DGRAM, 0);
SOCKADDR_IN sin = { 0 };
struct timeval tv;
tv.tv_sec = 3;
tv.tv_usec = 0;
if(sock == -1)
{
perror("socket()");
exit(errno);
}
sin.sin_addr.s_addr = inet_addr(LOCAL_ADDRESS);
sin.sin_port = htons(port);
sin.sin_family = AF_INET;
if(bind(sock,(SOCKADDR *)&sin, sizeof sin) == -1)
{
perror("bind()");
exit(errno);
}
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO,&tv,sizeof(tv)) < 0) {
perror("Error");
}
return sock;
}
void write_UDP(int sock, SOCKADDR_IN *sin, const char *buffer, size_t buff_len)
{
socklen_t sinsize = 16;//sizeof *sin;
printf("sinsize : %d\n", sinsize);
int n;
size_t i;
if((n=sendto(sock, buffer, buff_len, 0, (SOCKADDR *) sin, sinsize)) < 0)
{
perror("send()");
exit(errno);
}
printf("envoi (sinsize = %d; n = %d)|%s| a %s:%d/%d termine \n", sinsize, n, buffer, inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), sin->sin_family);
}
int read_UDP(int sock, SOCKADDR_IN *sin, char *buffer)
{
int n = 0;
socklen_t sinsize = sizeof *sin;
if((n = recvfrom(sock, buffer, BUF_SIZE - 1, 0, (SOCKADDR *) sin, &sinsize)) < 0)
{
perror("recvfrom()");
exit(errno);
}
printf("recu (sinsize = %d; n = %d)|%s| depuis %s:%d/%d termine \n", sinsize, n, buffer, inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), sin->sin_family);
return n;
}
client.c
#include "receive_tool.h"
static int PORT;
static char LOCAL_ADDRESS[50];
/* main function, launching the Server application*/
int main(int argc, char **argv)
{
int uc_sock;
char free_msg[30];
memset(free_msg, 0, sizeof(free_msg));
char buffer[60000];
memset(buffer, 0, sizeof(buffer));
if(argc < 3)
{
PORT = 2005;
sprintf(LOCAL_ADDRESS, "192.168.1.12");
//printf("Valeurs de parametre forcees: %d\t%s\n", PORT, LOCAL_ADDRESS);
}
else
{
PORT = atoi(argv[1]);
sprintf(LOCAL_ADDRESS, argv[2]);
}
sprintf(free_msg, "free:%s", LOCAL_ADDRESS);
SOCKADDR_IN server_sin = { 0 }, receive_sin = { 0 };
server_sin.sin_addr.s_addr = inet_addr(SERVER_ADDRESS); // addresse multicast vers les serveurs
server_sin.sin_family = AF_INET;
server_sin.sin_port = htons(PORT);
memset(&server_sin.sin_zero, 0, sizeof(server_sin.sin_zero));
uc_sock = init_UC_connection(LOCAL_ADDRESS, PORT);
write_UDP(uc_sock, &server_sin, free_msg);
time_t chrono_begin;
chrono_begin = time(NULL);
chrono_begin+= 5;
while(time(NULL) <= chrono_begin)
{
if(read_UDP(uc_sock, &receive_sin, buffer)<0) continue;
write_UDP(uc_sock, &server_sin, buffer);
}
close(uc_sock);
return 0;
}
Do you have any idea why this happen?
Thank you for your help.
The problem was caused by the Zedboards which used the same MAC address. Then, the switch sent only to the last IP address connected to the MAC address.
Bye
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.