TCP + UDP Socket in C using Daytime Protocol - c

My lecturer gave us his implemented Daytime Protocol in C using TCP and we have to extend it to UDP. It should only accept UDP or TCP using cmd flags -u / -t. I have done everything so far, but the sendto() function returns
"Server started, listening on 0.0.0.0:13 ...
Received message from 127.0.0.1:35585:
Successful incoming connection from 127.0.0.1:35585.
Error in function sending_datetime() line 319: ERROR IN SENDTO(): Socket operation on non-socket
"
I have tried everything but still cant fix it, please help me and thank you :) (iam a beginner)
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <getopt.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <arpa/inet.h>
#define TCP 1
#define UDP 2
#define PROTO TCP
#define PORT 13
#define BACKLOG 256
#define MSGLENGTH 27
#define XSTR(s) STR(s)
#define STR(s) #s
#define ERROR(str) (fprintf(stderr, "Error in function %s() line %d: ", __FUNCTION__, __LINE__),\
perror(str), kill(0, SIGTERM), exit(EXIT_FAILURE))
#define LOG(...) (printf(__VA_ARGS__), fflush(stdout))
volatile sig_atomic_t active = 1;
/*
* struct srvcfg is used to manage all necessary settings for a daytime service
*/
struct srvcfg {
int proto;
unsigned short port;
struct in_addr server_ip;
struct sockaddr_in server_addr;
int sockfd;
};
/*
* Declaration of functions
*/
void exiting(int sig);
void set_signal_handler(void);
void print_usage(void);
void parse_arguments(int argc, char *argv[], struct srvcfg *cfg);
void start_server(struct srvcfg *cfg);
int create_socket(int type, struct sockaddr_in *addr);
int sending_datetime(int fd, int port);
void destroy_server(struct srvcfg *cfg);
int main(int argc, char *argv[]) {
struct srvcfg *cfg;
struct sockaddr_in client;
int client_len = sizeof(struct sockaddr_in);
int fd;
if ((cfg = malloc(sizeof(struct srvcfg))) == NULL)
ERROR("Error allocating memory");
set_signal_handler();
parse_arguments(argc, argv, cfg);
start_server(cfg);
while (active) {
// Sockets are set to non-blocking, therefor errno needs to be checked
// for EWOULDBLOCK and/or EAGAIN
if (cfg->proto == TCP)
{
if (((fd = accept(cfg->sockfd, (struct sockaddr *)&client, &client_len)) == -1)
&& (errno != EWOULDBLOCK) && (errno != EAGAIN))
ERROR("Error incoming connection");
}
else if (cfg->proto == UDP)
{
char buffer[MSGLENGTH];
if ((fd = recvfrom(cfg->sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&client, &client_len)) == -1)
{
if (errno != EWOULDBLOCK && errno != EAGAIN)
ERROR("Error receiving message");
}
else
{
buffer[fd] = '\0';
printf("Received message from %s:%d: %s\n",
inet_ntoa(client.sin_addr), ntohs(client.sin_port), buffer);
}
}
// Only if we get a socket descriptor from the accept() call,
// we continue sending the daytime message
if (fd != -1) {
LOG("Successful incoming connection from %s:%d.\n", \
inet_ntoa(client.sin_addr),ntohs(client.sin_port));
if (cfg->proto == TCP)
{
if (sending_datetime(fd, TCP) == -1)
ERROR("Error sending datetime");
}
else if (cfg->proto == UDP)
{
if (sending_datetime(fd, UDP) == -1)
ERROR("Error sending datetime via UDP");
}
LOG("Successfully sent datetime to %s:%d.\n", \
inet_ntoa(client.sin_addr),ntohs(client.sin_port));
if (close(fd) == -1)
ERROR("Error on closing socket");
LOG("Successfully closing connection from %s:%d.\n", \
inet_ntoa(client.sin_addr),ntohs(client.sin_port));
}
}
destroy_server(cfg);
free(cfg);
exit(EXIT_SUCCESS);
}
* Starting the daytime services by using create_socket() until the socket is in
* listen state.
*/
void start_server(struct srvcfg *cfg)
{
assert(cfg != NULL);
bzero(&cfg->server_addr, sizeof(cfg->server_addr));
cfg->server_addr.sin_family = AF_INET;
cfg->server_addr.sin_port = htons(cfg->port);
cfg->server_addr.sin_addr.s_addr = cfg->server_ip.s_addr;
if (cfg->proto == TCP) {
cfg->sockfd = create_socket(SOCK_STREAM, &cfg->server_addr);
// We set the socket to non-blocking, which means an accept() call won't block
// until a client connection request is received
fcntl(cfg->sockfd, F_SETFL, fcntl(cfg->sockfd, F_GETFL, 0) | O_NONBLOCK);
}
else if (cfg->proto == UDP)
{
cfg->sockfd = create_socket(SOCK_DGRAM, &cfg->server_addr); // UDP Erweiterung
}
LOG("Server started, listening on %s:%d ...\n", inet_ntoa(cfg->server_ip), cfg->port);
}
/*
* Creating a socket in listen state for the daytime service.
*/
int create_socket(int type, struct sockaddr_in *addr)
{
assert(type == SOCK_STREAM || type == SOCK_DGRAM);
assert(addr != NULL);
int sockfd;
int reuseaddr = 1;
if ((sockfd = socket(PF_INET, type, 0)) == -1)
ERROR("Error creating socket");
if ((setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR,
&reuseaddr, sizeof(reuseaddr))) == -1)
ERROR("Error setting socket options");
if ((bind(sockfd, (struct sockaddr *)addr, sizeof(*addr))) == -1)
ERROR("Error binding socket");
if (type == SOCK_STREAM) {
if ((listen(sockfd, BACKLOG)) == -1)
ERROR("Error setting stream socket into listening mode");
}
return sockfd;
}
/*
* Sending a daytime message to the client.
*/
int sending_datetime(int fd, int port)
{
time_t curr_time;
char buffer[MSGLENGTH];
struct sockaddr_in client;
int client_len = sizeof(client);
/*
* A daytime message from this server is 26 bytes long, including a closing \r\n.
* Example: Thu Nov 26 11:29:54 2020\r\n
*/
curr_time = time(NULL);
snprintf(buffer, sizeof(buffer), "%.24s\r\n", ctime(&curr_time));
if (port == TCP)
{
return write(fd, buffer, strlen(buffer));
}
else if (port == UDP)
{
int n;
n = sendto(fd, buffer, strlen(buffer), 0, (struct sockaddr *)&client, client_len);
if (n < 0)
{
ERROR("ERROR IN SENDTO()"); // <---- HERE !!!!***
}
else
return n;
}
//sendto(fd, buffer, strlen(buffer), 0, (struct sockaddr *)&client, sizeof(client));
}
void destroy_server(struct srvcfg *cfg)
{
assert(cfg != NULL);
if ((close(cfg->sockfd)) == -1)
ERROR("Error closing socket");
}

else if (cfg->proto == UDP)
{
char buffer[MSGLENGTH];
if ((fd = recvfrom(cfg->sockfd, buffer, sizeof(buffer), 0, (struct sockaddr *)&client, &client_len)) == -1)
{
if (errno != EWOULDBLOCK && errno != EAGAIN)
From the man page:
Upon successful completion, recvfrom() shall return the length of the
message in bytes. If no messages are available to be received and the
peer has performed an orderly shutdown, recvfrom() shall return 0.
Otherwise, the function shall return -1 and set errno to indicate the
error.
recvfrom() doesn't return a file descriptor. It returns the length of the message in bytes.
A subsequent sendto() operation then tries to write to the return value of recvfrom(), which causes it to fail with EBADF:
The socket argument is not a valid file descriptor.
Fix:
You need to specify the socket descriptor the server is bound to instead of the return value of recvfrom().
The sendto() function shall send a message through a connection-mode
or connectionless-mode socket. If the socket is connectionless-mode,
the message shall be sent to the address specified by dest_addr.
Pass in the struct srvcfg to sending_daytime(), and replace fd with cfg->sock.
int sending_datetime(struct srvcfg *cfg, int fd, int port)
{
time_t curr_time;
char buffer[MSGLENGTH];
struct sockaddr_in client;
int client_len = sizeof(client);
/*
* A daytime message from this server is 26 bytes long, including a closing \r\n.
* Example: Thu Nov 26 11:29:54 2020\r\n
*/
curr_time = time(NULL);
snprintf(buffer, sizeof(buffer), "%.24s\r\n", ctime(&curr_time));
if (port == TCP)
{
return write(fd, buffer, strlen(buffer));
}
else if (port == UDP)
{
int n;
n = sendto(cfg->sockfd, buffer, strlen(buffer), 0, (struct sockaddr *)&client, client_len);
if (n < 0)
{
ERROR("ERROR IN SENDTO()");
}
else
return n;
}
}
NB that time() and ctime() can fail. Pedantic code must check their return values.

Related

No response from recv when using nonblocking socket

I am trying to create a portscanner in c. If the port is open, I want to get a response from the server. When I use regular blocking sockets, this works fine. For example, I know that for a certain address on my network, if I check port 80, it will return the html page to me when I call recv. I have tested this, and it works correctly every time.
However, I want to use nonblocking sockets, because sometimes certain servers will not respond and will cause the program to hang. I was able to get the nonblocking sockets to (kindof) work (the code is currently commented out below). I could see which ports were open, which were closed, and which timed out, but I was not able to get a response from the server (even though I know it should send one). What am I doing wrong?
tl;dr: When using nonblocking sockets (vs blocking), recv doesn't return any data.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <fcntl.h>
#define MAX_LEN 100000
int main(int argc, char **argv)
{
int sock, test_sock;
struct sockaddr_in server_addr;
struct hostent *hp;
char buf[MAX_LEN];
int num_bytes;
int err_code;
int START_PORT = 1;
int END_PORT = 100;
fd_set fdset;
struct timeval tv;
int opts;
// resolve server name for its IP address, etc.
hp = gethostbyname(argv[1]);
if (NULL == hp) {
perror("gethostbyname");
exit(2);
}
//printf("Here1\n");
// build remote server addr/port
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);
//server_addr.sin_port = htons(atoi(argv[2]));
test_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
for(int i=START_PORT; i<=END_PORT; i++) {
printf("Here2\n");
sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); //created the tcp socket
//opts = fcntl(sock, F_SETFL, O_NONBLOCK);
printf("Here3\n");
if (sock < 0)
{
perror("Socket()\n");
exit(1);
}
server_addr.sin_port = htons(i);
// connect to server
printf("Here4\n");
err_code = connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
printf("Here5\n");
/* ... */
if (err_code < 0) {
printf("Port %d: connection refused\n", i);
//exit(3);
} else {
printf("Port %d:\n", i);
memset(buf, 0, MAX_LEN);
// Create message to send
char message[256];
strcpy(message, "GET / HTTP/1.0\r\nHost: ");
strcat(message, argv[1]);
strcat(message, "\r\n\r\n");
unsigned total_bytes_sent = 0;
num_bytes = send(sock, message, strlen(message), 0);
if (num_bytes < 0) {
perror("send");
exit(4);
}
unsigned total_bytes_received = 0;
while(1) {
num_bytes = recv(sock, buf+total_bytes_received, MAX_LEN, 0);
if(num_bytes <= 0){
break;
}
total_bytes_received += num_bytes;
}
// display received ack message
//printf("Port %d:\n", i);
fflush(stdout);
write(1, buf, total_bytes_received);
printf("\n");
printf("Done...\n");
}
close(sock);
}
// close sock to release resource
close(sock);
return 0;
}
SOLUTION
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <errno.h>
#include <fcntl.h>
#define MAX_LEN 100000
int main(int argc, char **argv)
{
int sock, sock_test;
struct sockaddr_in server_addr;
struct hostent *hp;
char buf[MAX_LEN];
int num_bytes;
int err_code;
int START_PORT = 1;
int END_PORT = 100;
int valid = 1;
fd_set fdset;
struct timeval tv;
// resolve server name for its IP address, etc.
hp = gethostbyname(argv[1]);
if (NULL == hp) {
perror("gethostbyname");
exit(2);
}
// build remote server addr/port
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);
for(int i=START_PORT; i<=END_PORT; i++) {
sock_test = socket(AF_INET, SOCK_STREAM, 0);
if (sock_test < 0)
{
perror("Socket()\n");
exit(1);
}
fcntl(sock_test, F_SETFL, O_NONBLOCK);
server_addr.sin_port = htons(i);
connect(sock_test, (struct sockaddr *)&server_addr, sizeof(server_addr));
FD_ZERO(&fdset);
FD_SET(sock_test, &fdset);
tv.tv_sec = 3;
tv.tv_usec = 0;
if (select(sock_test + 1, NULL, &fdset, NULL, &tv) == 1)
{
int so_error;
socklen_t len = sizeof so_error;
getsockopt(sock_test, SOL_SOCKET, SO_ERROR, &so_error, &len);
if (so_error == 0) {
printf("%s:%d is open\n", argv[1], i);
memset(buf, 0, MAX_LEN);
// Create message to send
char message[256];
strcpy(message, "GET / HTTP/1.0\r\nHost: ");
strcat(message, argv[1]);
strcat(message, "\r\n\r\n");
printf("Here6\n");
unsigned total_bytes_sent = 0;
num_bytes = send(sock_test, message, strlen(message), 0);
printf("Here7\n");
int retry = 3;
unsigned total_bytes_received = 0;
while(retry) {
num_bytes = recv(sock_test, buf+total_bytes_received, MAX_LEN, 0);
if (0 == num_bytes)
{
/* socket has been closed by peer */
break;
}
else if(-1 == num_bytes)
{
if ((EAGAIN == errno) || (EWOULDBLOCK == errno))
{
/* no data to be read on socket */
retry--;
/* wait one second */
sleep(1);
}
else
{
/* other error */
perror("recv");
break;
}
}
else
{
total_bytes_received += num_bytes;
}
}
// display received ack message
//printf("Port %d:\n", i);
fflush(stdout);
write(1, buf, total_bytes_received);
printf("\n");
printf("Done...\n");
}
else
{
//printf("%s:%d is closed\n", argv[1], i);
}
} else {
printf("timed out\n");
valid = 0; //set the boolean flag to false
}
close(sock_test);
}
// close sock to release resource
close(sock_test);
return 0;
}
As pointed in comments, in non-blocking mode, you have to handle cases when
server is not ready to send data.
For man recv(3)
Return Value
Upon successful completion, recv() shall return the length of the message in bytes. If no messages are available to be received and the peer has performed an orderly shutdown, recv() shall return 0. Otherwise, -1 shall be returned and errno set to indicate the error.
Errors
The recv() function shall fail if:
EAGAIN or EWOULDBLOCK
The socket's file descriptor is marked O_NONBLOCK and no data is waiting to be received; or MSG_OOB is set and no out-of-band data is available and either the socket's file descriptor is marked O_NONBLOCK or the socket does not support blocking to await out-of-band data.
Since your client may try to read before the server send something, you must
adapt your code to wait:
/* maximum number of retry, one second per retry */
int retry = 10;
unsigned total_bytes_received = 0;
while(retry) {
num_bytes = recv(sock, buf+total_bytes_received, MAX_LEN, 0);
if (0 == num_bytes)
{
/* socket has been closed by peer */
break;
}
else if(-1 == num_bytes)
{
if ((EAGAIN == errno) || (EWOULDBLOCK == errno))
{
/* no data to be read on socket */
retry--;
/* wait one second */
sleep(1);
}
else
{
/* other error */
perror("recv");
break;
}
}
else
{
total_bytes_received += num_bytes;
}
}

Why my server and client appear TIME_WAIT at the same time?

I met a strange problem when I test my server and client.
The server and client appear TIME_WAIT at the same time!
Here is my server code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h> //bzero
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#define LISTEN_QUEUE 20
#define MAX_LINE 1024
#define MAXN 16384
int parse(char *buf)
{
int num;
sscanf(buf, "%d", &num);
return num;
}
int main()
{
int listenfd, connfd, r, n;
pid_t childpid;
socklen_t chilen;
struct sockaddr_in chiaddr, servaddr;
char buf[MAX_LINE];
char content[MAXN];
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if(listenfd < 0)
printf("listen socket get error\n"), exit(-1);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(9999);
r = bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
if(r < 0)
printf("bind socket error\n"), exit(-1);
r = listen(listenfd, LISTEN_QUEUE);
memset(content, 'A', MAXN);
while(1)
{
chilen = sizeof(chiaddr);
connfd = accept(listenfd, (struct sockaddr *)&chiaddr, &chilen);
memset(buf, 0, MAX_LINE);
r = read(connfd, buf, MAX_LINE);
n = parse(buf);
write(connfd, content, n);
close(connfd);
}
}
And here is my client code:
#include <unp.h>
#define MAXN 16384
static int readn(int fd, char *buf, int nbytes)
{
int nread, ntoread, n;
#ifdef __DEBUG__
char temp[1024];
#endif /* __DEBUG__ */
nread = 0;
ntoread = nbytes;
while(nread < nbytes)
{
n = read(fd, buf, ntoread);
if(n < 0)
printf("read nbytes error\n"), exit (-1);
else if(n == 0)
break;
else
{
#ifdef __DEBUG__
memcpy(temp, buf, n);
temp[n] = 0;
printf("%s\n", temp);
#endif /* _DEBUG__ */
buf += n;
nread += n;
ntoread = nbytes - nread;
}
}
return nread;
}
static int Tcp_connect(char *host, char *port)
{
struct hostent *h;
char str[16];
struct sockaddr_in addr;
h = gethostbyname(host);
addr.sin_family = h->h_addrtype;
addr.sin_port = htons(atoi(port));
addr.sin_addr = *(struct in_addr *)(h->h_addr);
#ifdef __DEBUG__
if(h->h_addrtype == AF_INET)
printf("AF_INET\n");
printf("server ip is :%s\n", inet_ntoa(addr.sin_addr));
#endif /* __DEBUG__ */
int fd = socket(h->h_addrtype, SOCK_STREAM, 0);
if(fd < 0)
printf("socket get failed\n"), exit(-1);
#ifdef __DEBUG__
printf("socket get success\n");
#endif /* __DEBUG__ */
if(connect(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0)
printf("connect failed\n"), exit(-1);
#ifdef __DEBUG__
printf("connected success\n");
#endif /* __DEBUG__ */
return fd;
}
int main(char argc, char *argv[])
{
int i, j, fd, nchilds, nloops, nbytes;
pid_t pid;
ssize_t n, nread;
char request[MAX_LINE], reply[MAXN];
if(argc != 6)
printf("usage : client <IP addr> <port> <#children>
<#loops/child> <#bytes/request>"),
exit(-1);
nchilds = atoi(argv[3]);
nloops = atoi(argv[4]);
nbytes = atoi(argv[5]);
snprintf(request, sizeof(request), "%d\n", nbytes);
for(i = 0; i < nchilds; i++)
{
if((pid = fork()) == 0) //son
{
nread = 0;
for(j = 0; j < nloops; j++)
{
// printf("begin connect\n");
fd = Tcp_connect(argv[1], argv[2]);
write(fd, request, strlen(request));
if((n = readn(fd, reply, nbytes)) != nbytes)
printf("server return %d bytes\n", (int)n), exit(-1);
else
nread += n;
close(fd);
}
printf("child %d done, read total %d bytes\n", i, (int)nread);
exit(0);
}
}
while(wait(NULL) > 0) ;
if(errno != ECHILD)
printf("wait error\n"), exit(-1);
}
I run server and client on my machine.
And the client cmd is
./client 127.0.0.1 9999 5 100 4000
and then I run
netstat -a | grep 9999 > log
when I run
grep -c TIME_WAIT log
the result is 501, but according to book, the result should be 500.
I open the log, then I find server and client in TIME_WAIT at the same time on one connection
tcp 0 0 localhost:54915 localhost:9999 TIME_WAIT
tcp 0 0 localhost:9999 localhost:54915 TIME_WAIT
It is so strange, I don't know why
You are establishing and closing connection between client and server in loop - client connects to server, sends some data, receives response from server and then closes the connection. Similarly the server also disconnects after this exchange. You are observing TIME_WAIT state as you are closing connections between client and server.
The TIME_WAIT state is a normal TCP connection state, as the TCP stack does not immediately wipe out the TCBs after FIN exchange. The TIME_WAIT state represents waiting for enough time to pass to be sure the remote TCP received the acknowledgment of its connection termination request. Please refer to TCP RFC 793 for more details on TCP state machine.
If you do a grep on port 9999, you will notice connection in ESTABLISHED state while your client and server are running.
Possibly you can avoid establishing and closing TCP connection in loop, that is, change your server and client programs to establish connection and then enter the loop for exchanging data.

C: epoll port forwarding proxy doesn't forward all data

I have got this code and I'm trying to use it as a part of my project. The details of the project are not important for now, but what I'm trying to do is to use this port forwarding proxy as proxy between the browser and the local http server.
So if I type http: //127.0.0.1:8999/ in my browser I want to get web page from 127.0.0.1:8888 back. This only works for small web pages (no images, small html files,...). When I try to do this on webpage with a few images, they don't get transmitted or are transmitted only partially.
I have also tested with telnet and a web site with a big html file. I have connected to the proxy and send the HEAD method request. I got all the meta-data, as expected. When I tried GET method I got only part of the web page's html file back.
Could anyone point me in the right direction? I'm really not sure what I am doing wrong.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <errno.h>
#include <signal.h>
#include <assert.h>
#define FALSE 0
#define TRUE 1
#define EPOLL_QUEUE_LEN 256
#define SERVER_PORT 7000
#define LISTEN_PORT 8667 // Proxy Server Listens for this port
#define FORWARD_PORT 8888 // Proxy Server forwards all port LISTEN_PORT data to this port
#define BUFLEN 1024
//Globals
int fd_server;
int sent = 0; // for how many requests were processed to client
int forwardSockets[EPOLL_QUEUE_LEN];
int internalSockets[EPOLL_QUEUE_LEN];
// Function prototypes
static void SystemFatal (const char* message);
static int forwardData (int fd);
void close_fd (int);
// This is the main function which handles the epoll loop and
// accepts connections
// Also handles the data processing back to the client.
int main (int argc, char* argv[])
{
int i, arg, src_port, forwardSD, dest_port;
int num_fds, fd_new, epoll_fd;
int linenum=0;
static struct epoll_event events[EPOLL_QUEUE_LEN], event;
struct sockaddr_in addr, remote_addr;
socklen_t addr_size = sizeof(struct sockaddr_in);
struct sigaction act;
struct hostent *hp;
struct sockaddr_in server_fwd;
char *host;
char line[256], ip[256];
FILE *fp;
fp=fopen("port_forward_config", "r");
//src_port = LISTEN_PORT; // Use the default listen port
while(fgets(line, 256, fp) != NULL)
{
linenum++;
if(line[0] == '#') continue;
sscanf(line, "%s %d %d", &ip, &src_port, &dest_port);
{
fprintf(stderr, "Syntax error, line %d\n", linenum);
continue;
}
}
printf("Reading Config File...\n");
printf("IP %s LPORT %d DPORT %d\n", ip, src_port, dest_port);
host = ip;
// set up the signal handler to close the server socket when CTRL-c is received
act.sa_handler = close_fd;
act.sa_flags = 0;
if ((sigemptyset (&act.sa_mask) == -1 || sigaction (SIGINT, &act, NULL) == -1))
{
perror ("Failed to set SIGINT handler");
exit (EXIT_FAILURE);
}
//--------------------------------------------------------------------------------------------
// Create the listening socket
fd_server = socket (AF_INET, SOCK_STREAM, 0);
if (fd_server == -1)
{
SystemFatal("socket");
}
// set SO_REUSEADDR so port can be resused imemediately after exit, i.e., after CTRL-c
arg = 1;
if (setsockopt (fd_server, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) == -1)
{
SystemFatal("setsockopt");
}
// Make the server listening socket non-blocking
if (fcntl (fd_server, F_SETFL, O_NONBLOCK | fcntl (fd_server, F_GETFL, 0)) == -1)
{
SystemFatal("fcntl");
}
// Bind to the specified listening port
memset (&addr, 0, sizeof (struct sockaddr_in));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(src_port);
if (bind (fd_server, (struct sockaddr*) &addr, sizeof(addr)) == -1)
{
SystemFatal("bind");
}
// Listen for fd_news; SOMAXCONN is 128 by default
if (listen (fd_server, SOMAXCONN) == -1)
{
SystemFatal("listen");
}
//---------------------------------------------------------------------------------------------
// Create the epoll file descriptor
epoll_fd = epoll_create(EPOLL_QUEUE_LEN);
if (epoll_fd == -1)
{
SystemFatal("epoll_create");
}
// Add the server socket to the epoll event loop
event.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLET;
event.data.fd = fd_server;
if (epoll_ctl (epoll_fd, EPOLL_CTL_ADD, fd_server, &event) == -1)
{
SystemFatal("epoll_ctl");
}
// Execute the epoll event loop
while (TRUE)
{
//struct epoll_event events[MAX_EVENTS];
num_fds = epoll_wait (epoll_fd, events, EPOLL_QUEUE_LEN, -1);
if (num_fds < 0)
{
SystemFatal ("Error in epoll_wait!");
}
for (i = 0; i < num_fds; i++)
{
// Case 1: Error condition
if (events[i].events & (EPOLLHUP | EPOLLERR))
{
fputs("epoll: EPOLLERR", stderr);
//close(events[i].data.fd);
continue;
}
assert (events[i].events & EPOLLIN);
//-----------------------------------------------------------------------------------------
// Case 2: Server is receiving a connection request
if (events[i].data.fd == fd_server)
{
//socklen_t addr_size = sizeof(remote_addr);
fd_new = accept (fd_server, (struct sockaddr*) &remote_addr, &addr_size);
if (fd_new == -1)
{
if (errno != EAGAIN && errno != EWOULDBLOCK)
{
perror("accept");
}
continue;
}
//------------------------------------------------------------------------------------------------
// Make the fd_new non-blocking
if (fcntl (fd_new, F_SETFL, O_NONBLOCK | fcntl(fd_new, F_GETFL, 0)) == -1)
{
SystemFatal("fcntl");
}
// Add the new socket descriptor to the epoll loop
event.data.fd = fd_new;
if (epoll_ctl (epoll_fd, EPOLL_CTL_ADD, fd_new, &event) == -1)
{
SystemFatal ("epoll_ctl");
}
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
printf(" Remote Address: %s\n", inet_ntoa(remote_addr.sin_addr));
//close(fd_new);
dest_port = FORWARD_PORT; // Used the default forward port
// create internal connection
printf("Trying to create forward socket\n");
if ((forwardSD = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("Cannot create forward socket.");
exit(1);
}
printf("Binding...\n");
bzero((char *)&server_fwd, sizeof(struct sockaddr_in));
server_fwd.sin_family = AF_INET;
server_fwd.sin_port = htons(dest_port);
//host = "192.168.0.10";
if ((hp = gethostbyname(host)) == NULL) {
printf("Failed to get host name");
}
bcopy(hp->h_addr, (char *)&server_fwd.sin_addr, hp->h_length);
printf("Connecting to interal machine.\n");
printf("Server Forward Port: %d\n", ntohs(server_fwd.sin_port));
printf("Server Forward IP: %s\n", inet_ntoa(server_fwd.sin_addr));
// Connecting to interal machine
if (connect (forwardSD, (struct sockaddr *)&server_fwd, sizeof(server_fwd)) == -1) {
perror("connect failed");
exit(1);
}
// Add the new socket descriptor to the epoll loop
event.data.fd = forwardSD;
if (epoll_ctl (epoll_fd, EPOLL_CTL_ADD, forwardSD, &event) == -1)
{
SystemFatal ("epoll_ctl");
}
printf ("Connected: Server: %s\n", hp->h_name);
forwardSockets[fd_new] = forwardSD;
internalSockets[forwardSD] = fd_new;
// end internal connection
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
continue;
}
// Case 3: One of the sockets has read data
if (!forwardData(events[i].data.fd))
{
// epoll will remove the fd from its set
// automatically when the fd is closed
close (events[i].data.fd);
}
}
}
close(fd_server);
exit (EXIT_SUCCESS);
}
/*
This function clears a socket if it has data waiting to be processed.
It takes the string sent from the client and parses it. After it has
done that it will send back to the client the amount of data it requested
for however many requests it defined as well.
*/
static int forwardData (int fd)
{
int n, bytes_to_read;
char *bp, buf[BUFLEN];
int forwardData;
printf ("Forwarding :\n");
//check if internal or external connection to send data back to.
if(forwardSockets[fd] != 0){
forwardData = forwardSockets[fd];
}
if(internalSockets[fd] != 0){
forwardData = internalSockets[fd];
}
bp = buf;
bytes_to_read = BUFLEN;
while ((n = recv (fd, bp, bytes_to_read, 0)) > 0) {
bp += n;
bytes_to_read -= n;
send (forwardData, buf, n, 0);
return TRUE;
}
return TRUE;
}
// Prints the error stored in errno and aborts the program.
static void SystemFatal(const char* message)
{
perror (message);
exit (EXIT_FAILURE);
}
// close fd
void close_fd (int signo)
{
close(fd_server);
exit (EXIT_SUCCESS);
}
It seems like you're using the EPOLLET (edge triggered) flag in all of your epoll events. This flag will cause epoll to only return events when a file descriptor changes from unavailable to available.
Furthermore, in your forwardData function, you receive BUFLEN bytes and then return TRUE. If there are more bytes available, you will never forward them, as your events are edge triggered and will not be resent.
Try modifying forwardData (perhaps remove the return TRUE in the loop) so that all readable data is forwarded before returning.

c socket prog - select() problems

I'm new to network programming. I have to write a simple client/server program in C. The server will listen for a connection and the client will connect to the server, send a message, and receive an echo back from the client. We have to update this using select() to handle connections to multiple clients at the same time from the server process. I tried to implement select() on the client side like instructed,but I think I'm having an infinite loop on the client side in if(FD_ISSET(clientSockfd, &readfds)) part.
//client1.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <resolv.h>
#include <arpa/inet.h>
const int BUF_SIZE = 512;
int main(int argc, char *argv[]) {
char buf[BUF_SIZE], buf2[BUF_SIZE];
char *msg;
struct sockaddr_in serverInfo;
int clientSockfd, errorCheck, readVal, numfd;
struct hostent *hostName;
fd_set readfds;
//make sure user entered correct arguments when starting client
if(argc != 3)
{
printf("error: must enter 'programName portNumber hostname'\n");
exit(errno);
}
//create socket and error check socket() call
clientSockfd=socket(AF_INET, SOCK_STREAM, 0);
if (clientSockfd == -1)
{
perror("error creating socket");
exit(errno);
}
//assign sockaddr_in info for RemoteAddr
bzero(&serverInfo, sizeof(serverInfo));
serverInfo.sin_family=AF_INET;
hostName=gethostbyname(argv[2]);
if(hostName == NULL)
{
herror("Error when calling gethostbyname()");
exit(errno);
}
memcpy((unsigned char *) &serverInfo.sin_addr, (unsigned char *)hostName->h_addr, hostName->h_length); //copy IP address to be used
serverInfo.sin_port=htons(atoi(argv[1])); //port number to be used, given in command line, must be converted to network byte order
//connect to server side
if(connect(clientSockfd, (struct sockaddr *)&serverInfo, sizeof(serverInfo)) == -1)
{
perror("error when connecting to server");
exit(errno);
}
while(1)
{
FD_ZERO(&readfds); //zero out set
FD_SET(fileno(stdin), &readfds);
FD_SET(clientSockfd, &readfds);
int maxfd = fileno(stdin);
if(maxfd < clientSockfd) maxfd = clientSockfd;
numfd = select(maxfd, &readfds, 0, 0, 0); //call select()
if(numfd > 0)
{
if(FD_ISSET(clientSockfd, &readfds))
{
//make sure buf is empty so it doesnt print extra chars
bzero(buf2, BUF_SIZE);
read(clientSockfd, buf2, BUF_SIZE);
}
if(FD_ISSET(fileno(stdin), &readfds))
{
bzero(buf, BUF_SIZE);
fgets(buf, BUF_SIZE-1, stdin);
printf("echo from server: %s\n", buf);
errorCheck = write(clientSockfd, buf, strlen(buf)+1);
if(errorCheck == -1)
{
perror("error writing");
}
}
}
else if(numfd == 0)
{
perror("Error using select()\n");
exit(0);
}
else
printf("no data\n");
}
//close connection to server
errorCheck = close(clientSockfd);
if(errorCheck == -1)
{
perror("Error closing connection.");
exit(errno);
}
return 0;
}
here is the server..
//server.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/socket.h>
#include <resolv.h>
#include <arpa/inet.h>
#include <errno.h>
const int ASSIGNED_PORT = 17000;
const int BUF_SIZE = 512;
int main() {
int serverfd, clientfd;
struct sockaddr_in serverSock; //NOTE: a pointer to sockaddr_in can be cast to a pointer to
// a struct sockaddr - useful for connect()
char buf[BUF_SIZE];
int errorCheck, msgLength;
//create socket with error checking (-1 ret on error)
serverfd = socket(AF_INET, SOCK_STREAM, 0);
if(serverfd < 0 )
{
perror("socket failed.");
exit(errno);
}
//assign sockaddr_in info for server
bzero(&serverSock, sizeof(serverSock)); //set to all 0's
serverSock.sin_family = AF_INET;
serverSock.sin_addr.s_addr = htonl(INADDR_ANY);
serverSock.sin_port = htons(ASSIGNED_PORT);
//bind a name to the socket with error checking (0 ret on success)
errorCheck = bind(serverfd, (struct sockaddr*)&serverSock, sizeof(serverSock));
if(errorCheck < 0 )
{
perror("bind failed.");
exit(errno);
}
//listen for connections with error checking (0 ret on success)
errorCheck = listen(serverfd, 10);
if(errorCheck < 0 )
{
perror("listen failed.");
exit(errno);
}
printf("Listening for connections. Enter CNTRL-c to kill server.\n");
//create infinite loop to accept, read, write, and close connections with error hecking
while(1)
{
//accept the connection from the client
clientfd = accept(serverfd, 0, 0);
if(clientfd == -1)
{
perror("error accepting connection.");
exit(errno);
}
//read data from the client
bzero(buf, BUF_SIZE);
msgLength = read(clientfd, buf, BUF_SIZE-1);
if(msgLength == -1)
{
perror("error reading from client");
close(clientfd);
close(serverfd);
exit(errno);
}
if(buf[0] '\0')
{
printf("connection closing");
exit(0);
}
//print what the client sent
printf("Message from client: %s\n", buf);
//echo back what the client sent
errorCheck = write(clientfd, buf, strlen(buf)+1);
if(errorCheck == -1 )
{
perror("error writing to client");
exit(errno);
}
//close the connection
errorCheck = close(clientfd);
if(errorCheck == -1)
{
perror("error closing connection");
exit(errno);
}
}
errorCheck = close(serverfd);
if(errorCheck==-1)
{
perror("error closing server, exiting program now");
sleep(6);
exit(errno);
}
return 0;
}
I think the problem (or at least, a problem) in your client-side code is that you are passing the magic number 32 to select(). That's incorrect. What you should be passing is the maximum of all of the socket numbers, plus one. For example, something like this:
int maxfd = fileno(stdin);
if (maxfd < clientSockFD) maxfd = clientSockFD;
// further maximizations for other sockets would go here, if you had any other sockets...
numfd = select(maxfd+1, &readfds, 0, 0, 0);
You need to use select() on the server side to handle multiple clients, not on the client side.

How to know if the client has terminated in sockets

Suppose, I have a connected socket after writing this code..
if ((sd = accept(socket_d, (struct sockaddr *)&client_addr, &alen)) < 0)
{
perror("accept failed\n");
exit(1);
}
How can I know at the server side that client has exited.
My whole program actually does the following..
Accepts a connection from client
Starts a new thread that reads messages from that particular client and then broadcast this message to all the connected clients.
If you want to see the whole code... In this whole code. I am also struggling with one more problem that whenever I kill a client with Ctrl+C, my server terminates abruptly.. It would be nice if anyone could suggest what the problem is..
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <pthread.h>
/*CONSTANTS*/
#define DEFAULT_PORT 10000
#define LISTEN_QUEUE_LIMIT 6
#define TOTAL_CLIENTS 10
#define CHAR_BUFFER 256
/*GLOBAL VARIABLE*/
int current_client = 0;
int connected_clients[TOTAL_CLIENTS];
extern int errno;
void *client_handler(void * socket_d);
int main(int argc, char *argv[])
{
struct sockaddr_in server_addr;/* structure to hold server's address*/
int socket_d; /* listening socket descriptor */
int port; /* protocol port number */
int option_value; /* needed for setsockopt */
pthread_t tid[TOTAL_CLIENTS];
port = (argc > 1)?atoi(argv[1]):DEFAULT_PORT;
/* Socket Server address structure */
memset((char *)&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET; /* set family to Internet */
server_addr.sin_addr.s_addr = INADDR_ANY; /* set the local IP address */
server_addr.sin_port = htons((u_short)port); /* Set port */
/* Create socket */
if ( (socket_d = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
fprintf(stderr, "socket creation failed\n");
exit(1);
}
/* Make listening socket's port reusable */
if (setsockopt(socket_d, SOL_SOCKET, SO_REUSEADDR, (char *)&option_value,
sizeof(option_value)) < 0) {
fprintf(stderr, "setsockopt failure\n");
exit(1);
}
/* Bind a local address to the socket */
if (bind(socket_d, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
fprintf(stderr, "bind failed\n");
exit(1);
}
/* Specify size of request queue */
if (listen(socket_d, LISTEN_QUEUE_LIMIT) < 0) {
fprintf(stderr, "listen failed\n");
exit(1);
}
memset(connected_clients,0,sizeof(int)*TOTAL_CLIENTS);
for (;;)
{
struct sockaddr_in client_addr; /* structure to hold client's address*/
int alen = sizeof(client_addr); /* length of address */
int sd; /* connected socket descriptor */
if ((sd = accept(socket_d, (struct sockaddr *)&client_addr, &alen)) < 0)
{
perror("accept failed\n");
exit(1);
}
else printf("\n I got a connection from (%s , %d)\n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));
if (pthread_create(&tid[current_client],NULL,(void *)client_handler,(void *)sd) != 0)
{
perror("pthread_create error");
continue;
}
connected_clients[current_client]=sd;
current_client++; /*Incrementing Client number*/
}
return 0;
}
void *client_handler(void *connected_socket)
{
int sd;
sd = (int)connected_socket;
for ( ; ; )
{
ssize_t n;
char buffer[CHAR_BUFFER];
for ( ; ; )
{
if (n = read(sd, buffer, sizeof(char)*CHAR_BUFFER) == -1)
{
perror("Error reading from client");
pthread_exit(1);
}
int i=0;
for (i=0;i<current_client;i++)
{
if (write(connected_clients[i],buffer,sizeof(char)*CHAR_BUFFER) == -1)
perror("Error sending messages to a client while multicasting");
}
}
}
}
My client side is this (Maye be irrelevant while answering my question)
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string.h>
#include <stdlib.h>
void error(char *msg)
{
perror(msg);
exit(0);
}
void *listen_for_message(void * fd)
{
int sockfd = (int)fd;
int n;
char buffer[256];
bzero(buffer,256);
printf("YOUR MESSAGE: ");
fflush(stdout);
while (1)
{
n = read(sockfd,buffer,256);
if (n < 0)
error("ERROR reading from socket");
if (n == 0) pthread_exit(1);
printf("\nMESSAGE BROADCAST: %sYOUR MESSAGE: ",buffer);
fflush(stdout);
}
}
int main(int argc, char *argv[])
{
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *server;
pthread_t read_message;
char buffer[256];
if (argc < 3) {
fprintf(stderr,"usage %s hostname port\n", argv[0]);
exit(0);
}
portno = atoi(argv[2]);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error("ERROR opening socket");
server = gethostbyname(argv[1]);
if (server == NULL) {
fprintf(stderr,"ERROR, no such host\n");
exit(0);
}
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
serv_addr.sin_port = htons(portno);
if (connect(sockfd,&serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
bzero(buffer,256);
if (pthread_create(&read_message,NULL,(void *)listen_for_message,(void *)sockfd) !=0 )
{
perror("error creating thread");
}
while (1)
{
fgets(buffer,255,stdin);
n = write(sockfd,buffer,256);
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,256);
}
return 0;
}
After accepting the connection, your recv() on the socket will return 0 or -1 in special cases.
Excerpt from recv(3) man page:
Upon successful completion, recv()
shall return the length of the message
in bytes. If no messages are available
to be received and the peer has
performed an orderly shutdown, recv()
shall return 0. Otherwise, -1 shall be
returned and errno set to indicate the
error.
So, if your client exited gracefully, you will get 0 from recv() at some point. If the connection was somehow lost, you may also get -1 and checking for appropriate errno would tell you if the connection was lost of some other error occured. See more details at recv(3) man page.
Edit:
I see that you are using read(). Still, the same rules as with recv() apply.
Your server can also fail when trying to write() to your clients. If your client disconnects write() will return -1 and the errno would probably be set to EPIPE. Also, SIGPIPE signal will be send to you process and kill him if you do not block/ignore this signal. And you don't as I see and this is why your server terminates when client presses Ctrl-C. Ctrl-C terminates client, therefore closes client socket and makes your server's write() fail.
See mark4o's answer for nice detailed explanation of what else might go wrong.
If the client program exits, then the OS on the client will close its end of the socket. When you call recv() it will return 0, or -1 with errno ECONNRESET if a TCP RST has been received (e.g. because you attempted to send data after the client had closed). If the whole client machine goes down, or the network becomes disconnected, then in that case you may not receive anything if the server is not trying to send anything; if that is important to detect, you can either send some data periodically, or set the SO_KEEPALIVE socket option using setsockopt() to force it to send a packet with no data after long periods (hours) of inactivity. When no acknowledgment is received, recv() will then return -1 with errno ETIMEDOUT or another error if more specific information is available.
In addition, if you attempt to send data on a socket that has been disconnected, by default the SIGPIPE signal will terminate your program. This can be avoided by setting the SIGPIPE signal action to SIG_IGN (ignore), or by using send() with the MSG_NOSIGNAL flag on systems that support it (Linux).

Resources