I am writing a TCP client and server protocol for a school project. The client sends a "GET \r\n" message and the server has to transfer "+OK\r\n", size of the file and the file, in case it exists in the server directory. I'm blocked in the file transfer
I tried to solve it at small steps at a time. I set up the connection, sent the request from the client and received the "OK" message from the server.
Now I opened the file in the server and tried to send it 128 bytes at a time to the client. The reading of the file works and apparently also the sending of the buffers but the client is not receiving anything...
Here's my server.c
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "../errlib.h"
#include "../sockwrap.h"
#define BUFLEN 128 /* Buffer length */
#define TIMEOUT 15 /* TIMEOUT */
/* FUNCTION PROTOTYPES */
void service(int s);
/* GLOBAL VARIABLES */
char *prog_name;
int main(int argc, char *argv[])
{
int conn_request_skt; /* passive socket */
uint16_t lport_n, lport_h; /* port used by server (net/host ord.) */
int bklog = 2; /* listen backlog */
int s; /* connected socket */
fd_set cset; // waiting for connection
struct timeval tval; // timeout
size_t n;
socklen_t addrlen;
struct sockaddr_in saddr, caddr; /* server and client addresses */
prog_name = argv[0];
if (argc != 2) {
printf("Usage: %s <port number>\n", prog_name);
exit(1);
}
/* get server port number */
if (sscanf(argv[1], "%" SCNu16, &lport_h)!=1)
err_sys("Invalid port number");
lport_n = htons(lport_h);
/* create the socket */
printf("creating socket...\n");
s = Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
printf("done, socket number %u\n",s);
/* bind the socket to any local IP address */
bzero(&saddr, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = lport_n;
saddr.sin_addr.s_addr = INADDR_ANY;
showAddr("Binding to address", &saddr);
Bind(s, (struct sockaddr *) &saddr, sizeof(saddr));
printf("done.\n");
/* listen */
printf ("Listening at socket %d with backlog = %d \n",s,bklog);
Listen(s, bklog);
printf("done.\n");
conn_request_skt = s;
/* main server loop */
for ( ; ; )
{
printf("waiting for connection...\n");
/* accept next connection */
FD_ZERO(&cset);
FD_SET(conn_request_skt, &cset);
tval.tv_sec = TIMEOUT;
tval.tv_usec = 0;
n = Select(FD_SETSIZE, &cset, NULL, NULL, &tval);
if ( n > 0 ){
addrlen = sizeof(struct sockaddr_in);
s = Accept(conn_request_skt, (struct sockaddr *) &caddr, &addrlen);
showAddr("Accepted connection from", &caddr);
printf("new socket: %u\n",s);
/* serve the client on socket s */
service(s);
} else {
printf("No connection request after %d seconds\n",TIMEOUT);
}
}
}
void service(int s) {
char buf[BUFLEN]; /* reception buffer */
char filename[BUFLEN];
int n;
long filesize;
uint32_t fsize;
FILE *fp;
for ( ; ; )
{
n = recv(s, buf, BUFLEN, 0);
if (n < 0) {
printf("Read error\n");
close(s);
printf("Socket %d closed\n", s);
break;
} else if (n == 0) {
printf("Connection closed by party on socket %d\n",s);
close(s);
break;
} else {
printf("Received request from socket %03d :\n", s);
sscanf(buf, "GET %s\r\n", filename);
strcpy(buf, "+OK\r\n");
printf("%s",buf);
if(writen(s, buf, strlen(buf)) != strlen(buf))
printf("Write error while sending +OK\n");
// open file
fp = fopen(filename, "r");
if( fp == NULL){
//TODO close connection
}
// calculating dim of file
fseek(fp, 0L, SEEK_END);
filesize = ftell(fp);
rewind(fp); // go back at beginning of file
fsize = htonl(filesize); // size file in network byte order
// sending file size
if(writen(s, &fsize, 4) != 4)
printf("Write error while sending file size\n");
while(fread(buf, 1, BUFLEN - 1, fp) == BUFLEN - 1){
printf("%s", buf);
if(writen(s, buf, strlen(buf)) != strlen(buf))
printf("Write error while buf\n");
}
printf("%s", buf);
printf("I am here\n");
}
}
}
While here is my client.c
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "../errlib.h"
#include "../sockwrap.h"
#define BUFLEN 128 /* BUFFER LENGTH */
#define TIMEOUT 15 /* TIMEOUT*/
/* GLOBAL VARIABLES */
char *prog_name;
int main(int argc, char *argv[])
{
char request[BUFLEN]; /* request buffer */
char rbuf[BUFLEN]; /* reception buffer */
uint32_t taddr_n; /* server IP addr. (net/host ord) */
uint16_t tport_n, tport_h; /* server port number (net/host ord) */
int s, len;
int result;
struct sockaddr_in saddr; /* server address structure */
struct in_addr sIPaddr; /* server IP addr. structure */
fd_set cset; // variables for timeout
struct timeval tval;
size_t n;
prog_name = argv[0];
if(argc < 4)
err_sys("Wrong number of parameters!\n");
// read address from first argument
taddr_n = inet_addr(argv[1]);
if (taddr_n == INADDR_NONE)
err_sys("Invalid address");
// read port number from second argument
if (sscanf(argv[2], "%" SCNu16, &tport_h)!=1)
err_sys("Invalid port number");
tport_n = htons(tport_h);
/* create the socket */
printf("Creating socket\n");
s = Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
printf("done. Socket fd number: %d\n",s);
/* prepare address structure */
bzero(&saddr, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = tport_n;
saddr.sin_addr = sIPaddr;
printf("trying to connect to the server...\n");
/* connect */
showAddr("Connecting to target address", &saddr);
Connect(s, (struct sockaddr *) &saddr, sizeof(saddr));
printf("done.\n");
// loop to request files
for (int i = 3 ; i < argc ; i++ ){ // i = 4 because the first file is the fourth argument
// check if file name is too big
if(strlen(argv[i]) >= BUFLEN - 6)
err_sys("The file name is too big for the buffer request!\n");
// create the string of bytes for the request
strcpy(request, "GET ");
strcat(request, argv[i]);
strcat(request, "\r\n");
len = strlen(request);
if(writen(s, request, len) != len){
printf("Write error\n");
break;
}
printf("waiting for response...\n");
// receive file from server
n = recv(s, rbuf, BUFLEN, 0);
if (n < 0) {
printf("Read error\n");
close(s);
printf("Socket %d closed\n", s);
break;
} else if (n == 0) {
printf("Connection closed by party on socket %d\n",s);
close(s);
break;
} else {
printf("Received reply from server\n");
uint32_t fsize;
printf("%s",rbuf);
if(strcmp(rbuf, "+OK\r\n") == 0){
n = recv(s, &fsize, 4, 0);
if (n < 0) {
printf("Read error\n");
close(s);
printf("Socket %d closed\n", s);
break;
} else if (n == 0) {
printf("Connection closed by party on socket %d\n",s);
close(s);
break;
} else {
// received file dimension
fsize = ntohl(fsize);
}
while(fsize > 0){
printf("I am here1n\n");
// receive file
n = recv(s, rbuf, BUFLEN-1, 0);
if (n < 0) {
printf("Read error\n");
close(s);
printf("Socket %d closed\n", s);
break;
} else if (n == 0) {
printf("Connection closed by party on socket %d\n",s);
close(s);
break;
} else {
printf("I am here");
fsize -= n;
}
}
}
}
}
printf("===========================================================\n");
close(s);
exit(0);
}
The recv in the client where I am supposed to receive the file just blocks without receiving anything. I don't understand what I am missing...
The issue here is a common one: You're not being careful with message boundaries.
In your client, you do a recv and check whether the number of bytes is greater than 0. But then you don't do more length checking. You next do a strcmp on a particular string you're expecting to receive (+OK\r\n). But you might have received 3 bytes (+OK) or you might have received 10: (+OK\r\nXXXXX) or more [aside: also, recv doesn't guarantee your byte string is null-terminated]. There is nothing stopping the kernel on the far side from batching the preamble plus subsequent bytes into a single TCP packet. Likewise, there is nothing preventing the local side from aggregating multiple TCP packets into a single buffer.
You must provide message boundaries. If you're expecting your next message to be 5 bytes, then you should receive exactly 5 bytes (and retry if you get fewer -- being careful to check for EOF too in case the other side aborted early). Or, alternatively stick a buffering layer in front of your receive logic so that it will receive up to some large amount, return to you the number of bytes you want, and then save whatever is in excess for a subsequent "receive" call.
To restate this in a different way: Your server sends +OK\r\n, then it sends a four-byte length, then it starts sending the file. But that means your first recv on the client side could be receiving the preamble, plus the length, plus the first N bytes of the file all in one system call.
TCP does not respect, provide or enforce message boundaries.
Related
I have A TCP Server-Client where I enter a number with the number of times that I want to send the word to the server. So I send correctly because I print the information on a loop(in client) but in the server shows infinitely because I put while(1) but if I don't use just print it one time. I don't want to send the number to the server.
If I enter the number 4 I want to print the word "hello" four times
This is the server, now it's printing infinitely obviously.
#define MAXPENDING 5 /* Maximum number of simultaneous connections */
#define BUFFSIZE 255 /* Size of message to be received */
void err_sys(char *mess) { perror(mess); exit(1); }
void handle_client(int sock) {
char buffer[BUFFSIZE];
int received = -1;
while (1) {
/* Read from socket */
read(sock, &buffer[0], BUFFSIZE);
printf("Message from client: %s\n", buffer);
/* Write to socket */
write(sock, buffer, strlen(buffer) + 1);
/* Close socket */
close(sock);
}
}
int main(int argc, char *argv[]) {
struct sockaddr_in echoserver, echoclient;
int serversock, clientsock;
int result;
/* Check input arguments */
if (argc != 2) {
fprintf(stderr, "Usage: %s <port>\n", argv[0]);
exit(1);
}
/* Create TCP socket */
serversock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serversock < 0) {
err_sys("Error socket");
}
/* Set information for sockaddr_in structure */
memset(&echoserver, 0, sizeof(echoserver)); /* we reset memory */
echoserver.sin_family = AF_INET; /* Internet/IP */
echoserver.sin_addr.s_addr = htonl(INADDR_ANY); /* ANY address */
echoserver.sin_port = htons(atoi(argv[1])); /* server port */
/* Bind socket */
result = bind(serversock, (struct sockaddr *) &echoserver, sizeof(echoserver));
if (result < 0) {
err_sys("Error bind");
}
/* Listen socket */
result = listen(serversock, MAXPENDING);
if (result < 0) {
err_sys("Error listen");
}
while (1) {
unsigned int clientlen = sizeof(echoclient);
/* Wait for a connection from a client */
clientsock = accept(serversock, (struct sockaddr *) &echoclient, &clientlen);
if (clientsock < 0) {
err_sys("Error accept");
}
fprintf(stdout, "Client: %s\n", inet_ntoa(echoclient.sin_addr));
/* Call function to handle socket */
handle_client(clientsock);
}
}
This is the client
printf("Enter a number between 0 to 9[0 to exit]: ");
fgets(number, 100, stdin);
while(strtol(number,&pEnd,10)!=0){
while (!((strtol(number,&pEnd,10) <= 9) && (strtol(number,&pEnd,10) >= 0))) {
printf("[ERROR] The number you entered is out of range\n");
printf("Enter a number between 0 to 9[0 to exit]: ");
fgets(number, 100, stdin);
}
if(strtol(number,&pEnd,10)!=0){
printf("Enter a word: ");
fgets(buffer, 100, stdin);
for(int i=0;i<strtol(number,&pEnd,10);i++){
write(sock, buffer, strlen(buffer) + 1);
fprintf(stdout, " sent \n");
read(sock, buffer, BUFFSIZE);
fprintf(stdout, " %s ...done \n", buffer);
}
}
printf(" Enter a number between 0 to 9[0 to exit]: ");
fgets(number, 100, stdin);
}
There are multiple problems in you code:
you do not test if read() succeeds. You should break from the loop if it fails.
you unconditionally close the socket in the body of the loop: the next read will fail and return -1 immediately, you will print a bogus message and iterate at nauseam.
you call strlen() and printf() with a buffer that might not be null terminated, potentially causing undefined behavior.
Here is a modified version:
void handle_client(int sock) {
char buffer[BUFFSIZE];
int received = -1;
if (sock < 0)
return;
for (;;) {
/* Read from socket */
received = read(sock, buffer, sizeof(buffer) - 1);
if (received <= 0)
break;
buffer[received] = '\0';
printf("Message from client: %s\n", buffer);
/* Write to socket */
write(sock, buffer, received);
}
/* Close socket */
close(sock);
}
You have an infinite loop in your hands normally that is not recommended but if you need to do it then add a if statement such:
while(1){
if(clientsock < 0){/*statement that is met so that your code stops running the if */
break;
}
}
I have written a proxy that accepts connections from host1 and forwards the connection toward server2.
I accept a connection and then I start two threads to handle the connections... One thread to receive from host 1 and send to server2, and another thread to receive form server2 and send to host1.
My program compiles with no errors, and when I run it and connect host 1 my program fails at the function WRITE after some short time with no error.
The connection is OK, it just fails after short period of time, before write.
Also you can find the written function that I am using.
The code is failing at the CONTROLLER thread, when the controller is writing to the socket.
Why does the program fail with no error message at all?
/*
* File Server0.c
* ECHO TCP SERVER with the following features:
* - Gets port from keyboard
* - SEQUENTIAL: serves one client at a time
*/
#define _POSIX_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "mysocket.h"
#include <arpa/inet.h>
#include <fcntl.h> // for open
#include <unistd.h> // for close
#include <sys/types.h>
#include <signal.h>
#include <sys/wait.h>
#include <pthread.h> /* POSIX threads */
#define RBUFLEN 16384
/* FUNCTION PROTOTYPES */
int mygetline(char * line, size_t maxline, char *prompt);
int writen(SOCKET, char *, size_t);
void *mininet(void *);
void *controller(void *);
SOCKET s,s1[100],d[100];
int y=0,z=0;
int main()
{
uint16_t lport_n, lport_h;
int result;
socklen_t addrlen;
struct sockaddr_in saddr, caddr;
//fd_set readfds;
SOCKET conn_request_skt;
int bklog = 2,t=0;
pthread_t tid1[100];
pthread_t tid[100];
/* Initialize socket API if needed */
SockStartup();
/* input server port number */
lport_h=8888;
lport_n = htons(lport_h);
/* create the MININET socket */
printf("Creating first socketn");
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET)
err_fatal("socket() failed");
printf("done, socket number %un",s);
/* bind the MININET socket to any local IP address */
saddr.sin_family = AF_INET;
saddr.sin_port = lport_n;
saddr.sin_addr.s_addr = INADDR_ANY;
showAddr("Binding to address first socket", &saddr);
result = bind(s, (struct sockaddr *) &saddr, sizeof(saddr));
if (result == -1)
err_fatal("bind() failed");
printf("done.n");
/* listen on MININET socket */
printf ("Listening at socket %d with backlog = %d n",s,bklog);
result = listen(s, bklog);
if (result == -1)
err_fatal("listen() failed");
printf("done.n");
conn_request_skt=s;
/* accept next connection from mininet */
for(;;)
{
addrlen = sizeof(struct sockaddr_in);
z++;
y++;
t++;
d[t] = accept(conn_request_skt, (struct sockaddr *) &caddr, &addrlen);
if (d[t] == INVALID_SOCKET)
err_fatal("accept() failed");
showAddr("Accepted connection from", &caddr);
printf("new accepted connection socket: %un",d[t]);
/* serve the client on socket s */
pthread_create(&tid[t], NULL, mininet, &t);
pthread_create(&tid1[t], NULL, controller, &t);
sleep(1);
}
exit(0);
}
void *mininet (void *arg)
{
char buf[RBUFLEN]; /* reception buffer */
int n,t,k;
t=y;
k=z;
printf("MININET: m is: %d, c is: %d n ", t,k);
/* main server loop */
for (;;)
{
printf("MININET receiving on socket: %un",d[t]);
n=recv(d[t], buf, RBUFLEN-1, 0);
if (n < 0)
{
printf("MININET Read errorn");
closesocket(d[t]);
printf("MININET Socket %d closedn", d[t]);
return NULL;
}
else if (n==0)
{
printf("Connection closed by MININET on socket %dn",d[t]);
closesocket(d[t]);
break;
}
else
{
printf("Received line from MININET socket %03d :n", d[t]);
buf[n]=0;
printf("[%s]n",buf);
printf("MINET WRITING TO: %un",s1[k]);
if(writen(s1[k], buf, n) != n)
printf("Write error while replying TO CONTROLERn");
else
printf("Reply sent towards CONTROLERn");
}
}
return NULL;
}
void *controller (void *arg)
{
char buf[RBUFLEN]; /* reception buffer */
uint16_t lport_n1, lport_h1; /* port where the server listens (net/host byte ord resp.) */
int result, n,t,k;
struct sockaddr_in saddr1; /* server and client address structures */
t=y;
k=z;
printf("CONTROLLER: m is: %d, c is: %d n ", t,k);
/* Initialize socket API if needed */
SockStartup();
lport_h1=6633;
lport_n1 = htons(lport_h1);
printf("Creating CONTROLLER socketn");
s1[k] = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s1[k] == INVALID_SOCKET)
err_fatal("socket() failed");
printf("done, socket number %un",s1[k]);
/* bind the socket to any local IP address */
saddr1.sin_family = AF_INET;
saddr1.sin_port = lport_n1;
saddr1.sin_addr.s_addr = INADDR_ANY;
/* connect */
showAddr("Connecting to target CONTROLER address", &saddr1);
result = connect(s1[k], (struct sockaddr *) &saddr1, sizeof(saddr1));
if (result == -1)
err_fatal("connect() failed");
printf("done.n");
/* main server loop */
for (;;)
{
printf("CONTROLER receiving on socket: %un",s1[k]);
n=recv(s1[k], buf, RBUFLEN-1, 0);
if (n < 0)
{
printf("CONTROLER Read errorn");
closesocket(s1[k]);
printf("Socket %d closedn", s1[k]);
return NULL;
}
else if (n==0)
{
printf("Connection closed by CONTROLER on socket %dn",s1[k]);
closesocket(s1[k]);
return NULL;
}
else
{
printf("Received line from CONTROLER socket %03d :n", s1[k]);
buf[n]=0;
printf("[%s]n",buf);
printf("CONTROLER writing on socket: %un",d[t]);
if(writen(d[t], buf, n) != n)
printf("Write error while replying TO MININETn");
else
printf("Reply sent towards MININETn");
}
}
return NULL;
}
/* Gets a line of text from standard input after having printed a prompt string Substitutes end of line with ''
Empties standard input buffer but stores at most maxline-1 characters in the passed buffer
*/
int mygetline(char *line, size_t maxline, char *prompt)
{
char ch;
size_t i;
printf("%s", prompt);
for (i=0; i< maxline-1 && (ch = getchar()) != 'n' && ch != EOF; i++)
*line++ = ch;
*line = '';
while (ch != 'n' && ch != EOF)
ch = getchar();
if (ch == EOF)
return(EOF);
else
return(1);
}
/* Writes nbytes from buffer ptr to stream socket s */
int writen(SOCKET s, char *ptr, size_t nbytes)
{
size_t nleft;
ssize_t nwritten;
for (nleft=nbytes; nleft > 0; )
{
nwritten = send(s, ptr, nleft, 0);
if (nwritten <=0)
return (nwritten);
else
{
nleft -= nwritten;
ptr += nwritten;
}
}
return (nbytes - nleft);
}
I have a client which is working fine, but whenever I run a new client, sometimes I don't receive the sent message on the other client already running, while using telnet it works flawlessly, the message "broadcasts" to all connected clients, and I want whenever a message is received to one of the clients to show even if I didn't already send a message.
Should I use select on clients ? and what should be changed ?
client.c:
#include <stdio.h> //printf
#include <string.h> //strlen
#include <sys/socket.h> //socket
#include <arpa/inet.h> //inet_addr
#include <unistd.h>
int main(int argc , char *argv[]){
int sock;
struct sockaddr_in server;
char message[256] , server_reply[256];
//Create socket
sock = socket(AF_INET , SOCK_STREAM , 0);
if (sock == -1)
{
printf("Could not create socket");
}
puts("Socket created");
server.sin_addr.s_addr = inet_addr("127.0.0.1");
server.sin_family = AF_INET;
server.sin_port = htons( 9034 );
//Connect to remote server
if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0){
perror("connect failed. Error");
return 1;
}
puts("Connected\n");
//keep communicating with server
for(;;){
printf("Enter message: ");
memset(message, 0, 256);
fgets(message, 256,stdin);
// scanf("%s" , message);
//Send some data
if( send(sock , message , strlen(message) , 0) < 0)
{
puts("Send failed");
return 1;
}
//Receive a reply from the server
if( recv(sock , server_reply , 256 , 0) < 0)
{
puts("recv failed");
break;
}
printf("Server Reply: %s\n", server_reply);
server_reply[0]='\0';
}
close(sock);
return 0;
}
server.c:
/*
** selectserver.c -- a cheezy multiperson chat server
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define PORT "9034" // port we're listening on
// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
}
return &(((struct sockaddr_in6*)sa)->sin6_addr);
}
int main(void){
fd_set master; // master file descriptor list
fd_set read_fds; // temp file descriptor list for select()
int fdmax; // maximum file descriptor number
int listener; // listening socket descriptor
int newfd; // newly accept()ed socket descriptor
struct sockaddr_storage remoteaddr; // client address
socklen_t addrlen;
char buf[256]; // buffer for client data
int nbytes;
char remoteIP[INET6_ADDRSTRLEN];
int yes=1; // for setsockopt() SO_REUSEADDR, below
int i, j, rv;
struct addrinfo hints, *ai, *p;
FD_ZERO(&master); // clear the master and temp sets
FD_ZERO(&read_fds);
// get us a socket and bind it
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((rv = getaddrinfo(NULL, PORT, &hints, &ai)) != 0) {
fprintf(stderr, "selectserver: %s\n", gai_strerror(rv));
exit(1);
}
for(p = ai; p != NULL; p = p->ai_next) {
listener = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (listener < 0) {
continue;
}
// lose the pesky "address already in use" error message
setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
if (bind(listener, p->ai_addr, p->ai_addrlen) < 0) {
close(listener);
continue;
}
break;
}
// if we got here, it means we didn't get bound
if (p == NULL) {
fprintf(stderr, "selectserver: failed to bind\n");
exit(2);
}
freeaddrinfo(ai); // all done with this
// listen
if (listen(listener, 10) == -1) {
perror("listen");
exit(3);
}
// add the listener to the master set
FD_SET(listener, &master);
// keep track of the biggest file descriptor
fdmax = listener; // so far, it's this one
// main loop
for(;;) {
read_fds = master; // copy it
if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
perror("select");
exit(4);
}
// run through the existing connections looking for data to read
for(i = 0; i <= fdmax; i++) {
if (FD_ISSET(i, &read_fds)) { // we got one!!
if (i == listener) {
// handle new connections
addrlen = sizeof remoteaddr;
newfd = accept(listener,
(struct sockaddr *)&remoteaddr,
&addrlen);
if (newfd == -1) {
perror("accept");
} else {
FD_SET(newfd, &master); // add to master set
if (newfd > fdmax) { // keep track of the max
fdmax = newfd;
}
printf("selectserver: new connection from %s on "
"socket %d\n",
inet_ntop(remoteaddr.ss_family,
get_in_addr((struct sockaddr*)&remoteaddr),
remoteIP, INET6_ADDRSTRLEN),
newfd);
}
} else {
// handle data from a client
memset(buf, 0, 256);
if ((nbytes = recv(i, buf, sizeof buf, 0)) <= 0) {
// got error or connection closed by client
if (nbytes == 0) {
// connection closed
printf("selectserver: socket %d hung up\n", i);
} else {
perror("recv");
}
close(i); // bye!
FD_CLR(i, &master); // remove from master set
} else {
// we got some data from a client
for(j = 0; j <= fdmax; j++) {
// send to everyone!
if (FD_ISSET(j, &master)) {
// except the listener and ourselves
if (j != listener && j != i) {
if (send(j, buf, nbytes, 0) == -1) {
perror("send");
}
}
}
}
}
} // END handle data from client
} // END got new incoming connection
} // END looping through file descriptors
} // END for(;;)--and you thought it would never end!
return 0;
}
The reason a client can't receive a message until they send one is because.
fgets(message, 256,stdin);
Will keep "reading" (and will therefore block) until an EOF or a newline character has been read from the input stream
Also, note that
if( recv(sock , server_reply , 256 , 0) < 0)
blocks if there is nothing to read, which will prevent that user from sending more messages to the server until there is something new to read from the server. Assuming that you've played online games before, I hope that you can see that such a setup would be rather annoying!
So, we have to find someway of checking to see if we can read from STDIN and the server socket without incurring a block. Using select() will prevent us blocking on the sever socket, but it wouldn't work for STDIN whilst using fgets() to read input from the user. This is because, as mentioned above, fgets() blocks until an EOF or newline is detected.
The main solution I have in mind is to replace fgets with a method buffer_message() that will only read from STDIN when it won't block on read (we'll use select() to implement this). We'll then place what is read into a buffer. If there is a full message, this message will then be written to the server. Otherwise, we'll let the control keep going through the program until there is something to read or write.
This is code from a recent university assignment I did and so a small portion of the code isn't mine
Declarations:
//directives are above (e.g. #include ...)
//message buffer related delcartions/macros
int buffer_message(char * message);
int find_network_newline(char * message, int inbuf);
#define COMPLETE 0
#define BUF_SIZE 256
static int inbuf; // how many bytes are currently in the buffer?
static int room; // how much room left in buffer?
static char *after; // pointer to position after the received characters
//main starts below
Main:
//insert the code below into main, after you've connected to the server
puts("Connected\n");
//set up variables for select()
fd_set all_set, r_set;
int maxfd = sock + 1;
FD_ZERO(&all_set);
FD_SET(STDIN_FILENO, &all_set); FD_SET(sock, &all_set);
r_set = all_set;
struct timeval tv; tv.tv_sec = 2; tv.tv_usec = 0;
//set the initial position of after
after = message;
puts("Enter message: ");
//keep communicating with server
for(;;){
r_set = all_set;
//check to see if we can read from STDIN or sock
select(maxfd, &r_set, NULL, NULL, &tv);
if(FD_ISSET(STDIN_FILENO, &r_set)){
if(buffer_message(message) == COMPLETE){
//Send some data
if(send(sock, message, strlen(message) + 1, 0) < 0)//NOTE: we have to do strlen(message) + 1 because we MUST include '\0'
{
puts("Send failed");
return 1;
}
puts("Enter message:");
}
}
if(FD_ISSET(sock, &r_set)){
//Receive a reply from the server
if( recv(sock , server_reply , 256 , 0) < 0)
{
puts("recv failed");
break;
}
printf("\nServer Reply: %s\n", server_reply);
server_reply[0]='\0';
}
}
close(sock);
return 0;
//end of main
Buffer functions:
int buffer_message(char * message){
int bytes_read = read(STDIN_FILENO, after, 256 - inbuf);
short flag = -1; // indicates if returned_data has been set
inbuf += bytes_read;
int where; // location of network newline
// Step 1: call findeol, store result in where
where = find_network_newline(message, inbuf);
if (where >= 0) { // OK. we have a full line
// Step 2: place a null terminator at the end of the string
char * null_c = {'\0'};
memcpy(message + where, &null_c, 1);
// Step 3: update inbuf and remove the full line from the clients's buffer
memmove(message, message + where + 1, inbuf - (where + 1));
inbuf -= (where+1);
flag = 0;
}
// Step 4: update room and after, in preparation for the next read
room = sizeof(message) - inbuf;
after = message + inbuf;
return flag;
}
int find_network_newline(char * message, int bytes_inbuf){
int i;
for(i = 0; i<inbuf; i++){
if( *(message + i) == '\n')
return i;
}
return -1;
}
P.S.
if( send(sock , message , strlen(message) , 0) < 0)
The above can also block if there's no space to write to the server, but there's no need to worry about that here. Also, I'd like to point out a few things you should implement for your client and your server:
Whenever you send data over a network, the standard newline is \r\n, or carriage return / newline, or simply the network newline. All messages sent between the client and the server should have this appended at the end.
You should be buffering all data sent between the server and the client. Why? Because you're not guaranteed to receive all packets in a message in a single read of a socket. I don't have time to find a source, but when using TCP/IP, packets for a message/file don't have to arrive together, meaning that if you do read, you may not be reading all of the data you intend to read. I'm not well versed in this, so please investigate this more. Open to having this edited / corrected
I have a tcp echo server that creates a pthread for each client that connects to it. For each connection, I have a variable nbOfClients that increments.
When a client closes its connection, I detect it and decrease the number of clients. However the server keeps thinking that the client it alive and keeps on trying to read/write from the socket. I guessed that it was because of the thread that created the client and I tries to kill the thread with pthread_cancel all to non avail.
I want to kill the pthread associated to a certain client that closes its connection.
How can I go about it?
Here's my code :
static int nbOfClients = 0;
static pthread_t tid;
int main (int argc, char *argv[]) {
int bytes_to_read, arg, listen_sd, new_conn, sockfd, client_len, port;
struct sockaddr_in server, client_addr;
char *bp, buf[BUFLEN];
ssize_t n;
sockfd = 0;
switch(argc) {
case 1:
port = SERVER_TCP_PORT; // Use the default port
break;
case 2:
port = atoi(argv[1]); // Get user specified port
break;
default:
fprintf(stderr, "Usage: %s [port]\n", argv[0]);
exit(1);
}
// Create a stream socket
if ((listen_sd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
error("Cannot Create Socket!");
// set SO_REUSEADDR so port can be resused imemediately after exit, i.e., after CTRL-c
arg = 1;
if (setsockopt (listen_sd, SOL_SOCKET, SO_REUSEADDR, &arg, sizeof(arg)) == -1)
error("setsockopt");
// Bind an address to the socket
bzero((char *)&server, sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY); // Accept connections from any client
if (bind(listen_sd, (struct sockaddr *)&server, sizeof(server)) == -1)
error("bind error");
listen(listen_sd, MAX_CONNECTIONS); ///put a define constant indicating the maximum number of clients #define NB_CLIENTS 3
while (TRUE) {
client_len = sizeof(client_addr);
if ((new_conn = accept(listen_sd, (struct sockaddr *) &client_addr, (socklen_t *)&client_len)) == -1)
error("accept error");
if(new_conn > 0) {
if(nbOfClients < MAX_CONNECTIONS) {
printf("just here\n");
printf(">> Initializing remote address: %s\n", inet_ntoa(client_addr.sin_addr));
nbOfClients++;
fclose(fp);
printf("Connections to date: %u \n",nbOfClients);
printf("make thread\n");
pthread_create(&tid,NULL,&echo, (void *)new_conn);
printf("had thread\n");
}
else {
printf("connection limit reached\n");
if(send(new_conn, "Server full!\n", 13, 0) == -1)
perror("send");
close(new_conn);
}
}
}
return(0);
}
void * echo(void *arg) {
char buf[BUFSIZE]; /* message buffer */
int n, i = 0;
bzero(buf, BUFSIZE);
if(send((int)arg, "Welcome!!\n", 20, 0) == -1)
perror("send");
detect_closed_connection(arg);
while(TRUE) {
n = read((int)arg, buf, BUFSIZE);
/**read: read input string from the client*/
if(n < 0) {
perror("error reading from socket");
}
printf("Server received from client, %d bytes: %s\n", n, buf);
/**write: echo the input string in UPPERCASE back to the client*/
int len = strlen(buf);
for(i = 0; buf[i]; i++)
buf[i] = toupper(buf[i]);
n = write((int)arg, buf, len);
if(n < 0) {
error("ERROR writing to socket");
}
}
}
void detect_closed_connection(void * listenSocket) {
struct pollfd pfd;
pfd.fd = (int)listenSocket;
pfd.events = POLLIN | POLLHUP | POLLRDNORM;
pfd.revents = 0;
while(pfd.revents == 0) {
if(poll(&pfd, 1, 100) > 0) {
// if result > 0, this means that there is either data available on the
// socket, or the socket has been closed
char buffer[32];
if (recv((int)listenSocket, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0) {
// if recv returns zero, that means the connection has been closed:
nbOfClients--;
pthread_cancel(tid);
}
}
}
}
Thanks.
You should check read() for returning 0 in the thread servering the client, as read() returns 0 in case the peer (client here) closed the connection.
After this line
n = read((int)arg, buf, BUFSIZE);
add
if (0 == n)
{
fprintf(stderr, "The client closed the connection.\n");
break;
}
Just before the thread function leave you could add the statement to decrement the number of running threads.
Also be aware that nbOfClients is accessed concurently by all the "client"-threads as well as by the main thread, so accessing it shall be protected, for example by using a mutex.
There is another issues, as the call to strlen() on the buffer read expects the buffer to be 0-terminate, which does not necessarily needs ot be the case, even if you sent 0-terminated "strings". read() might very well return the "string" the client sent in more then one part. So loop around read() until the 0-terminator had been received.
Do not make the thread end itself by calling pthread_cancel(), use pthread_exit() instead.
Basically i use a recv in a while in order to read the data sent from my client. Client finishes reading the file and sending it but server remains in the while. How can i notify the server that the file sending is done meaning feof on clients side?
SERVER
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/types.h> /*For Sockets*/
#include <sys/socket.h> /*For Sockets*/
#include <netdb.h> /*For gethostbyaddr()*/
#include <netinet/in.h> /*For internet sockets*/
#include <dirent.h>
/*Function for creating the lof file of Server*/
void log_event (char *message,char *filename)
{
FILE *file;
char *log_this;
time_t system_time; //Get the system time
time(&system_time);
log_this=strcat(ctime(&system_time),message); //Create the message to log
/*Check for filename and log as appropiate*/
if (filename!=NULL)
{
file = fopen(filename,"a+");
fprintf(file,"%s",log_this); /*writes the message*/
fclose(file); /*done!*/
}
else
{
file = fopen("ftp_tracelog.txt","a+");
fprintf(file,"%s",log_this); /*writes the message*/
fclose(file); /*done!*/
}
}
int main (int argc,char *argv[])
{
/*DECLERATIONS*/
char *filename;
char message [1024];
char *temp;
char temp_2[1024];
char buf[1024];
char *p=buf;
ssize_t bytesRemaining = 1024;
ssize_t bytesRemaining2 = 50;
char request[50];
char command[5];
char c[50];
char copy[1024]="COPY_OF_";
FILE *fp;
DIR *dp;
char list[1024];
int port,sock,newsock,serverlen,clientlen,fname_len,recvMsgSize,i,len,count;
struct sockaddr_in server,client;
struct sockaddr *serverptr, *clientptr;
struct hostent *rem;
struct dirent *ep;
/*END OF DECLERATIONS*/
/*Check for required arguments and get them as appropiate*/
if (argc < 2) {
/* Check if server's port number is given */
printf("Please give the port number!!!\n");
exit(1);
}
/*if server's port number is given and filename for log is given*/
if(argc>2){
filename=argv[1];
port=atoi(argv[2]);
}
/*If only port is given*/
if (argc==2){
port=atoi(argv[1]);
filename=NULL;
}
temp="--Server is Starting!!--";
sprintf(message,"%s\n",temp);
log_event(message,filename);
/* Create socket */
if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{perror("socket"); exit(1); }
server.sin_family = PF_INET; /* Internet domain */
server.sin_addr.s_addr = htonl(INADDR_ANY); /* My Internet address */
server.sin_port = htons(port); /* The given port */
serverptr = (struct sockaddr *) &server;
serverlen = sizeof (server);
/* Bind socket to address */
if (bind(sock, serverptr, serverlen) < 0) {
perror("bind"); exit(1); }
/* Listen for connections */
if (listen(sock, 40) < 0) { /* 5 max. requests in queue */
perror("listen"); exit(1); }
temp="---Listening for connections to port";
sprintf(temp_2,"%d----",port);
sprintf(message,"%s:%s\n",temp,temp_2);
log_event(message,filename);
/*Accepting Connecttion*/
while(1) {
clientptr = (struct sockaddr *) &client;
clientlen = sizeof(client);
/* Accept connection */
if ((newsock = accept(sock, clientptr, &clientlen)) < 0){
perror("accept"); exit(1);}
/* Find client's address */
if ((rem = gethostbyaddr((char *) &client.sin_addr.s_addr,
sizeof (client.sin_addr.s_addr), client.sin_family)) == NULL) {
perror("gethostbyaddr"); exit(1);}
temp="----Accepted connection from ";
sprintf(temp_2,"%s----", rem -> h_name);
sprintf(message,"%s:%s\n",temp,temp_2);
log_event(message,filename);
/* Create child for serving the client */
switch (fork()) {
case -1:
perror("fork"); exit(1);
case 0: /* Child process */
do{
/* Receive message from client */
if ((recvMsgSize = recv(newsock,request,sizeof(request),0))< 0)
perror("recv() failed");
printf("%s\n",request);
//printf("%s\n",command);
/*IF YOU ARE GOING TO EXECUTE AN LS COMMAND*/
if (strcmp(request,"ls")==0)
{
dp = opendir ("./");
if (dp != NULL)
{ /*LOG LS REQUEST*/
temp="--Client ";
sprintf(temp_2,"%s requested ls -------",rem -> h_name);
sprintf(message,"%s:%s\n",temp,temp_2);
log_event(message,filename);
/*SEND ALL DIRECTORY LISTING*/
while (ep = readdir (dp))
{
strcpy(list,ep->d_name);
//printf("sending:%s\n",list);
if (send(newsock,list,sizeof(list), 0)!= sizeof(list))
perror("send() sent a different number of bytes than expected");
}
//IF DIRECORY IS FINISHED SEND A LAST MESSAGE FOR ENDING
(void) closedir (dp);
if (send(newsock,"end",sizeof("end"), 0)!= sizeof("end"))
perror("send() sent a different number of bytes than expected");
}
else
perror ("Couldn't open the directory");
}
/*IF THE COMMAND IS PUT*/
if (strcmp(request,"put")==0)
{
sprintf(buf,"");
printf("execute put!!\n");
do{
ssize_t recvd;
while (bytesRemaining) {
if (( recvd =recv(newsock,p,bytesRemaining,0))< 0){
perror("recv() failed");}
bytesRemaining -= recvd; // keep track of bytes left
p += recvd;
}
}while (buf=="");
strcat(copy,buf);
fp=fopen(copy,"w+");
count=0;
while (c!="")
{
bzero(c,sizeof(c));
if ((recvMsgSize = recv(newsock,c,sizeof(c),0))< 0)
perror("recv() failed");
fprintf(fp,"%s",c);
}
fclose(fp);
printf("!!DONE!!!!\n");
//printf("%s",request);
}
}while (strcmp(request,"end")!=0); //run until client sents end request
/*LOG EXIT OF CLIENT*/
temp="--Client";
sprintf(temp_2,"%s is disconnected---",rem -> h_name);
sprintf(message,"%s:%s\n",temp,temp_2);
log_event(message,filename);
close(newsock); /* Close socket */
exit(0);
} /* end of switch */
}/*end of while*/
}
CLIENT
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/types.h>
int main (int argc,char *argv[])
{
int port, sock, serverlen,recvMsgSize,was_read;
int fname_len,msg_len,request_len;
char buf[256];
char *fname;
char request[50];
char list[1024];
char msg[512];
char op[1000];
char temp[5];
char *temp3;
char read;
FILE *fp;
char b[50];
struct sockaddr_in server;
struct sockaddr *serverptr;
struct hostent *rem;
temp3="put";
/* Are server's host name and port number given? */
if (argc < 3) {
printf("Please give host name and port number\n"); exit(1);
}
/* Create socket */
if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {
perror("socket");
exit(1);
}
/* Find server address */
if ((rem = gethostbyname(argv[1])) == NULL) {
herror("gethostbyname"); exit(1);
}
/* Convert port number to integer */
port = atoi(argv[2]);
/* Internet domain */
server.sin_family = PF_INET;
bcopy((char *) rem -> h_addr, (char *) &server.sin_addr,
rem -> h_length);
/*Server's Internet address and port*/
server.sin_port = htons(port);
serverptr = (struct sockaddr *) &server;
serverlen = sizeof(server);
if (connect(sock, serverptr, serverlen) < 0) { /* Request connection */
perror("connect");
exit(1); }
printf("Requested connection to host %s port %d\n", argv[1], port);
do{
printf("Please enter request\n:");
scanf("%s",request);
/* Send the string to the server */
if (send(sock,request,sizeof(request), 0)!= sizeof(request))
perror("send() sent a different number of bytes than expected");
if(strcmp(request,"ls")==0)
{
sprintf(list,"");
/*Recieve from server*/
while(strcmp(list,"end")!=0){
if ((recvMsgSize = recv(sock,list,sizeof(list),0))< 0)
perror("recv() failed");
if(strcmp(list,"end")!=0){
printf("%s\n",list);
}
}
bzero(request,sizeof(request));
}
/*Command for put*/
if(strcmp(request,"put")==0)
{ bzero(request,sizeof(request));
bzero(list,sizeof(list));
printf("Please enter filename:\n");
scanf("%s",list);
//printf("%s",list);
if (send(sock,list,sizeof(list), 0)!= sizeof(list))
perror("send() sent a different number of bytes than expected");
fp=fopen(list,"r");
if(fp==NULL)
{
puts ( "Cannot open target file" ) ;
fclose (fp) ;
exit(0);
}
while(!feof(fp))
{
bzero(b,sizeof(b));
int was_read = fread(b, sizeof(char),50,fp);
if (send(sock,b,(sizeof (char) * was_read), 0)!= (sizeof (char) * was_read))
perror("send() sent a different number of bytes than expected");
}
zero(b,sizeof(b));
if (send(sock,b,sizeof(b), 0)!= sizeof(b))
perror("send() sent a different number of bytes than expected");
}
}while (strcmp(request,"end")!=0);
close(sock); /* Close socket */
exit(0);
}
One method is to have the client close the socket at the end of the file. The server will successfully receive 0 bytes from recv(), which means the other end cleanly closed the socket. I notice that you do not currently detect this return value in your server code, but you should.
I notice that your protocol has the ability to request more than one file. In this case, you will need to do something like send the number of bytes in the file before sending the actual file, then the server will know how many bytes to expect. Or, you can do like FTP does and open a second socket connection for the file data (one file at a time).
As a stylistic note, the indenting in your code is terrible. It's hard to read to find out where the blocks start and end. I would reject this code in a code review purely on reasons of formatting.