Related
The following code (in the end) represents thread function which takes in ls command from remote client and send current working directory to that client.
It successfully sends but there is one issue:
When it stops sending completely, I want it to start listening again. At line:
printf("Enter 1 if you want to exit or 0 if you don't: ");
fgets(exit_status,MAX_SIZE,stdin);
It gets stuck and it is terminated (and starts another thread) when I press Enter
Which I don't understand why? and while I was debugging I saw above print statement executes after pressing Enter despite the debugger being at the end of function (means it passed this print statement).
I want it to start listening again automatically when it finish sending data.
If anyone wants to look at my full code here is the link:https://pastebin.com/9UmTkPge
void *server_socket_ls(void *arg) {
int* exit_status = (int*)malloc(sizeof(int));
*exit_status = 0;
while (*exit_status == 0) {
//socket code is here
//code for ls
char buffer[BUFFSIZE];
int received = -1;
char data[MAX];
memset(data,0,MAX);
// this will make server wait for another command to run until it receives exit
data[0] = '\0';
if((received = recv(new_socket, buffer,BUFFSIZE,0))<0){
perror("Failed");
}
buffer[received] = '\0';
strcat (data, buffer);
if (strcmp(data, "exit")==0) // this will force the code to exit
exit(0);
puts (data);
char *args[100];
setup(data,args,0);
int pipefd[2],lenght;
if(pipe(pipefd))
perror("Failed to create pipe");
pid_t pid = fork();
char path[MAX];
if(pid==0)
{
close(1); // close the original stdout
dup2(pipefd[1],1); // duplicate pipfd[1] to stdout
close(pipefd[0]); // close the readonly side of the pipe
close(pipefd[1]); // close the original write side of the pipe
execvp(args[0],args); // finally execute the command
}
else
if(pid>0)
{
close(pipefd[1]);
memset(path,0,MAX);
while(lenght=read(pipefd[0],path,MAX-1)){
printf("Data read so far %s\n", path);
if(send(new_socket,path,strlen(path),0) != strlen(path) ){
perror("Failed");
}
//fflush(NULL);
printf("Data sent so far %s\n", path);
memset(path,0,MAX);
}
close(pipefd[0]);
//removed so server will not terminate
}
else
{
printf("Error !\n");
exit(0);
}
printf("Enter 1 if you want to exit or 0 if you don't: ");
fgets(exit_status,MAX_SIZE,stdin);
}
}
There are many bugs:
In terminal_thread, input_command is allocated on each loop iteration -- a memory leak
Code to strip newline is broken
With .l, not specifying an IP address causes a segfault because token is NULL
The port number in terminal_thread for .l is 5126 which does not match the 9191 in the corresponding server code
After connecting, server_socket_file does not do anything.
In server_socket_ls, it loops on socket, bind, listen, and accept. The loop should start after the listen (i.e. only do accept in the loop and reuse the listening socket).
Other bugs marked in the code
I had to refactor the code and add some debug. It is annotated with the bugs. I use cpp conditionals to denote old vs. new code:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
Here is the code. I got minimal .l (remote ls) working:
Edit: Because of the update below running over SO space limits, I've elided the first code block I posted here.
Here is the debug.txt output:
term term: PROMPT
term term: FGETS
ls ls: ENTER
ls ls: SOCKET
file file: ENTER
ls ls: BIND prtNum=9191
file file: BIND portNum=6123
ls ls: LISTEN
term term: COMMAND '.l'
term term: port=9191
ls ls: ACCEPTED
term term: PROMPT
This program is exiting as soon as its stops sending data at exit(0) and so doesn't ask for exit_status. Is there a way somehow to make it not stop and instead the terminal prompt reappears along with servers listening at the back? –
Dragut
Because I sensed the urgency, I erred on the side of a partial solution now is better than a perfect solution too late.
I may have introduced a bug with an extraneous exit call in the ls server parent process (now fixed).
But, there are other issues ...
The main issue is that the server (for ls) is prompting the user whether to continue or not (on stdout/stdin). This doesn't work too well.
It's the client (i.e. terminal_thread) that should prompt the user. Or, as I've done it, the client will see exit at the command prompt, then send a packet with "exit" in it to the server, and terminate. Then, the server will see this command and terminate.
I refactored as much as I could without completely redoing everything.
I split off some code into functions. Some of the can/could be reused to implement the "file" server.
But, I'd put both functions into a single server thread. I'd have the server look at the "command" it gets and do either of the actions based on the command. Since there's no code for actually doing something in the "file" server [yet] it's difficult to rework.
One thing to fix [which I did not have time for]:
The .l command is of the form: .l [ip_address]. The default for ip_address is 127.0.0.1. But, this should be split into two commands (e.g.):
attach [ip_address]
ls [ls arguments]
Anyway, here's the updated code. I had to move a bit quickly, so it's not quite as clean as I'd like.
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdbool.h>
#include <stdarg.h>
#if 1
#include <time.h>
#endif
#define BACKLOG 10
#define MAX_SIZE 200
#define BACKLOG 10
#define BUFFSIZE 2048
#define MAXPENDING 5
#define MAX 2048
__thread char *tid;
__thread char dbgstrbuf[1000];
FILE *xfdbg;
double tsczero = 0.0;
typedef struct server_arg {
int portNum;
} server_arg;
typedef struct server_arg1 {
int portNum;
} server_arg1;
double
tscgetf(void)
{
struct timespec ts;
double sec;
clock_gettime(CLOCK_MONOTONIC,&ts);
sec = ts.tv_nsec;
sec /= 1e9;
sec += ts.tv_sec;
sec -= tsczero;
return sec;
}
void
dbgprt(const char *fmt,...)
{
va_list ap;
char msg[1000];
char *bp = msg;
bp += sprintf(bp,"[%.9f/%4s] ",tscgetf(),tid);
va_start(ap,fmt);
bp += vsprintf(bp,fmt,ap);
va_end(ap);
fputs(msg,xfdbg);
}
const char *
dbgstr(const char *str,int len)
{
char *bp = dbgstrbuf;
if (len < 0)
len = strlen(str);
bp += sprintf(bp,"'");
for (int i = 0; i < len; ++i) {
int chr = str[i];
if ((chr > 0x20) && (chr <= 0x7E))
bp += sprintf(bp,"%c",chr);
else
bp += sprintf(bp,"{%2.2X}",chr);
}
bp += sprintf(bp,"'");
return dbgstrbuf;
}
void
setup(char inputBuffer[], char *args[], int *background)
{
const char s[4] = " \t\n";
char *token;
token = strtok(inputBuffer, s);
int i = 0;
while (token != NULL) {
args[i] = token;
i++;
// printf("%s\n", token);
token = strtok(NULL, s);
}
args[i] = NULL;
}
int
open_remote(const char *ip,unsigned short port)
{
int sock;
struct sockaddr_in echoserver;
dbgprt("open_remote: ENTER ip=%s port=%u\n",dbgstr(ip,-1),port);
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("Failed to create socket");
exit(1);
}
int enable = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &enable,
sizeof(int)) < 0) {
perror("error");
}
memset(&echoserver, 0, sizeof(echoserver));
echoserver.sin_family = AF_INET;
echoserver.sin_addr.s_addr = inet_addr(ip);
// NOTE/BUG: this port number does _not_ match any server port
#if 0
echoserver.sin_port = htons(5126);
#else
dbgprt("term: port=%u\n",port);
echoserver.sin_port = htons(port);
#endif
if (connect(sock, (struct sockaddr *) &echoserver,
sizeof(echoserver)) < 0) {
perror("Failed to connect with server");
exit(1);
}
dbgprt("open_remote: EXIT sock=%d\n",sock);
return sock;
}
void *
terminal_thread(void *arg)
{
// NOTE/FIX: do this _once_
#if 1
char *input_command = malloc(MAX_SIZE);
#endif
tid = "term";
char buffer[BUFFSIZE];
int sock_ls = -1;
while (1) {
dbgprt("term: PROMPT\n");
printf(">> ");
//memset(input_command,0,strlen(str));
// NOTE/BUG: this is a memory leak
#if 0
char *input_command = malloc(MAX_SIZE);
#endif
dbgprt("term: FGETS\n");
fgets(input_command, MAX_SIZE, stdin);
// NOTE/BUG: code is broken to strip newline
#if 0
if ((strlen(input_command) > 0) &&
(input_command[strlen(input_command) - 1] == '\n'))
input_command[strlen(input_command) - 1] = '\0';
#else
input_command[strcspn(input_command,"\n")] = 0;
#endif
dbgprt("term: COMMAND %s\n",dbgstr(input_command,-1));
char list[] = "ls";
char cp[] = "cp";
#if 0
char s[100];
printf("%s\n", getcwd(s,100));
chdir("Desktop");
printf("%s\n", getcwd(s,100));
#endif
// exit program (and exit server)
if (strcmp(input_command,"exit") == 0) {
if (sock_ls >= 0) {
dbgprt("term: SENDEXIT\n");
if (send(sock_ls,"exit",4,0) < 0) {
perror("send/exit");
exit(1);
}
break;
}
}
if (strcmp(input_command, list) == 0) {
// ls code will run here
}
if ((input_command[0] == '.') && (input_command[1] == 'l')) {
printf("remote ls\n");
char ip[20];
const char c[2] = " ";
// strcpy(str,input_command);
char *token;
// get the first token
token = strtok(input_command, c);
// walk through other tokens
int i = 0;
while (token != NULL && i != -1) {
token = strtok(NULL, c);
i--;
}
#if 1
if (token == NULL) {
token = "127.0.0.1";
printf("no IP address found -- using %s\n",token);
}
#endif
if (sock_ls < 0)
sock_ls = open_remote(token,9191);
char s[100];
strcpy(s, "ls");
// NOTE/BUG: this blows away the "s" in "ls" because s is _set_ with strcpy
#if 0
s[strlen(s) - 1] = '\0'; // fgets doesn't automatically discard '\n'
#endif
unsigned int echolen;
echolen = strlen(s);
int received = 0;
/* send() from client; */
if (send(sock_ls, s, echolen, 0) != echolen) {
perror("Mismatch in number of sent bytes");
}
fprintf(stdout, "Message from server: ");
int bytes = 0;
/* recv() from server; */
if ((bytes = recv(sock_ls, buffer, echolen, 0)) < 1) {
perror("Failed to receive bytes from server");
}
received += bytes;
buffer[bytes] = '\0';
/* Assure null terminated string */
fprintf(stdout, buffer);
bytes = 0;
// this d {...} while block will receive the buffer sent by server
do {
buffer[bytes] = '\0';
printf("%s\n", buffer);
} while ((bytes = recv(sock_ls, buffer, BUFFSIZE - 1, 0)) >= BUFFSIZE - 1);
buffer[bytes] = '\0';
printf("%s\n", buffer);
printf("\n");
continue;
}
}
dbgprt("term: EXIT\n");
return (void *) 0;
}
int
ls_loop(int new_socket)
{
dbgprt("ls_loop: ENTER new_socket=%d\n",new_socket);
//code for ls
char buffer[BUFFSIZE];
int received = -1;
char data[MAX];
int stop = 0;
while (1) {
memset(data, 0, MAX);
// this will make server wait for another command to run until it
// receives exit
data[0] = '\0';
if ((received = recv(new_socket, buffer, BUFFSIZE, 0)) < 0) {
perror("Failed");
}
buffer[received] = '\0';
strcpy(data, buffer);
dbgprt("ls_loop: COMMAND %s\n",dbgstr(data,-1));
// this will force the code to exit
#if 0
if (strcmp(data, "exit") == 0)
exit(0);
puts(data);
#else
if (strncmp(data, "exit", 4) == 0) {
dbgprt("ls_loop: EXIT/COMMAND\n");
stop = 1;
break;
}
#endif
char *args[100];
setup(data, args, 0);
int pipefd[2], length;
if (pipe(pipefd))
perror("Failed to create pipe");
pid_t pid = fork();
char path[MAX];
if (pid == 0) {
// NOTE/BUG: no need to close before dup2
#if 0
close(1); // close the original stdout
#endif
dup2(pipefd[1], 1); // duplicate pipfd[1] to stdout
close(pipefd[0]); // close the readonly side of the pipe
close(pipefd[1]); // close the original write side of the pipe
execvp(args[0], args); // finally execute the command
exit(1);
}
if (pid < 0) {
perror("fork");
exit(1);
}
dbgprt("ls_loop: PARENT\n");
close(pipefd[1]);
while (length = read(pipefd[0], path, MAX - 1)) {
dbgprt("ls_loop: DATAREAD %s\n",dbgstr(path,length));
if (send(new_socket, path, length, 0) != length) {
perror("Failed");
}
memset(path, 0, MAX);
}
close(pipefd[0]);
}
dbgprt("ls_loop: EXIT stop=%d\n",stop);
}
void *
server_socket_ls(void *arg)
{
tid = "ls";
dbgprt("lsmain: ENTER\n");
do {
server_arg *s = (server_arg *) arg;
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
dbgprt("lsmain: SOCKET\n");
// Creating socket file descriptor
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
int enable = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &enable,
sizeof(int)) < 0) {
perror("error");
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(s->portNum);
dbgprt("lsmain: BIND prtNum=%u\n",s->portNum);
if (bind(server_fd, (struct sockaddr *) &address, sizeof(address))
< 0) {
perror("bind failed");
}
dbgprt("lsmain: LISTEN\n");
if (listen(server_fd, 3) < 0) {
perror("listen");
}
while (1) {
if ((new_socket = accept(server_fd, (struct sockaddr *) &address,
(socklen_t *) & addrlen)) < 0) {
perror("accept");
}
dbgprt("lsmain: ACCEPTED\n");
int stop = ls_loop(new_socket);
close(new_socket);
if (stop) {
dbgprt("lsmain: STOP\n");
break;
}
}
} while (0);
dbgprt("lsmain: EXIT\n");
return (void *) 0;
}
void *
server_socket_file(void *arg)
{
tid = "file";
dbgprt("file: ENTER\n");
server_arg1 *s1 = (server_arg1 *) arg;
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);
// Creating socket file descriptor
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
int enable = 1;
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int))
< 0) {
perror("error");
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(s1->portNum);
dbgprt("file: BIND portNum=%u\n",s1->portNum);
if (bind(server_fd, (struct sockaddr *) &address, sizeof(address)) < 0) {
perror("bind failed");
}
if (listen(server_fd, 3) < 0) {
perror("listen");
}
if ((new_socket = accept(server_fd, (struct sockaddr *) &address,
(socklen_t *) & addrlen)) < 0) {
perror("accept");
}
printf("Server Connected\n");
}
int
main(int argc, char const *argv[])
{
tid = "main";
tsczero = tscgetf();
server_arg *s = (server_arg *) malloc(sizeof(server_arg));
server_arg1 *s1 = (server_arg1 *) malloc(sizeof(server_arg1));
pthread_t id_1;
pthread_t id_2;
pthread_t id_3;
xfdbg = fopen("debug.txt","w");
setlinebuf(xfdbg);
if (pthread_create(&id_3, NULL, terminal_thread, NULL) != 0) {
perror("pthread_create");
}
// NOTE/BUG: this port (or the one below) doesn't match the client code
// port of 5126
s->portNum = 9191;
pthread_create(&id_1, NULL, server_socket_ls, s);
s1->portNum = 6123;
if (0)
pthread_create(&id_2, NULL, server_socket_file, s1);
pthread_join(id_1, NULL);
if (0)
pthread_join(id_2, NULL);
pthread_join(id_3, NULL);
// NOTE/BUG: pthread_exit in main thread is wrong
#if 0
pthread_exit(0);
#else
fclose(xfdbg);
return 0;
#endif
}
UPDATE:
Feedback 2: the program does make terminal thread to reappear, but it doesn't listen anymore. When I tried to send ls command again from remote pc, it just blocks (and debugging shows it is because it gets stuck at blocking receive function). –
Dragut
I tried to avoid too much refactoring, but now, I've added more changes. This version is almost a complete rearchitecting:
pthread_create is okay when testing, but isn't general enough if the server is on a different system.
Usually, the client and server are separate programs (e.g. we start the server in a different window or from systemd).
The server usually creates a subprocess/subthread to transfer the request (Below, I've done a fork but the server could do pthread_create).
This child process handles everything after the accept, so the server main process is free to loop on accept and have multiple simultaneous clients.
Because we're using stream sockets (e.g. TCP), each side needs to know when to stop reading. The usual is to create a struct that is a descriptor of the data to follow (e.g. xmsg_t below) that has a "type" and a "payload length".
Every bit of payload data that is sent/received is prefixed by such a descriptor.
In other words, we need a simple "protocol"
Now, we need two windows (they can be on different systems):
To start server: ./myprogram -s
To start client: ./myprogram
Here's the refactored code. It is annotated:
#include <netinet/in.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdbool.h>
#include <stdarg.h>
#if 1
#include <errno.h>
#include <time.h>
#include <sys/file.h>
#include <sys/wait.h>
#endif
#define MAXBUFF 2048 // max buffer size
#define MAXPENDING 5 // max number of connections (listen)
#define MAXARG 100 // max number of args
#define PORTNO 9191 // default port number
#if 0
#define STOP_SIGNO SIGTERM // stop signal to use
#else
#define STOP_SIGNO SIGHUP // stop signal to use
#endif
#define CLOSEME(_fd) \
do { \
dbgprt("CLOSEME fd=%d (" #_fd ")\n",_fd); \
if (_fd >= 0) \
close(_fd); \
_fd = -1; \
} while (0)
int opt_h; // 1=send HELO message
int opt_s; // 1=doserver, 0=doclient
int opt_n; // 1=run server command in foreground
char ipaddr[100] = { "127.0.0.1" };
unsigned short portno = PORTNO;
pid_t server_pid; // pid of server main process
volatile int server_signo; // signal received by server main
__thread char *tid;
__thread char dbgstrbuf[MAXBUFF + 1];
int dbgfd = -1;
double tsczero = 0.0;
typedef struct {
int xmsg_type;
int xmsg_paylen;
} xmsg_t;
enum {
XMSG_NOP,
XMSG_CMD,
XMSG_DATA,
XMSG_EOF,
};
double
tscgetf(void)
{
struct timespec ts;
double sec;
clock_gettime(CLOCK_MONOTONIC,&ts);
sec = ts.tv_nsec;
sec /= 1e9;
sec += ts.tv_sec;
sec -= tsczero;
return sec;
}
#if _USE_ZPRT_
#ifndef DEBUG
#define DEBUG 1
#endif
#endif
#if DEBUG
#define dbgprt(_fmt...) \
xdbgprt(__FUNCTION__,_fmt)
#else
#define dbgprt(_fmt...) \
do { } while (0)
#endif
void
xdbgprt(const char *fnc,const char *fmt,...)
{
va_list ap;
char msg[MAXBUFF * 4];
char *bp = msg;
int sverr = errno;
bp += sprintf(bp,"[%.9f/%4s] %s: ",tscgetf(),tid,fnc);
va_start(ap,fmt);
bp += vsprintf(bp,fmt,ap);
va_end(ap);
// when doing forks, we have to lock the stream to guarantee atomic,
// non-interspersed messages that are sequential
flock(dbgfd,LOCK_EX);
lseek(dbgfd,0,2);
ssize_t remlen = bp - msg;
ssize_t curlen;
for (bp = msg; remlen > 0; remlen -= curlen, bp += curlen) {
curlen = write(dbgfd,bp,remlen);
if (curlen < 0) {
perror("xdbgprt");
break;
}
}
flock(dbgfd,LOCK_UN);
errno = sverr;
}
const char *
dbgstr(const char *str,int len)
{
char *bp = dbgstrbuf;
if (len < 0)
len = strlen(str);
bp += sprintf(bp,"'");
for (int i = 0; i < len; ++i) {
int chr = str[i];
if ((chr > 0x20) && (chr <= 0x7E))
bp += sprintf(bp,"%c",chr);
else
bp += sprintf(bp,"{%2.2X}",chr);
}
bp += sprintf(bp,"'");
return dbgstrbuf;
}
// tokenize -- convert buffer to tokens
int
tokenize(char **argv,const char *cmdbuf)
{
static char tokbuf[MAXBUFF];
char **av = argv;
strcpy(tokbuf,cmdbuf);
char *token = strtok(tokbuf," ");
while (token != NULL) {
*av++ = token;
token = strtok(NULL," ");
}
*av = NULL;
return (av - argv);
}
// xsend -- send buffer (guaranteed delivery)
ssize_t
xsend(int sock,const void *vp,size_t buflen,int flags)
{
const char *buf = vp;
ssize_t curlen;
ssize_t totlen = 0;
dbgprt("ENTER buflen=%zu flags=%8.8X\n",buflen,flags);
for (; totlen < buflen; totlen += curlen) {
dbgprt("LOOP totlen=%zd\n",totlen);
curlen = send(sock,&buf[totlen],buflen - totlen,flags);
if (curlen <= 0)
break;
}
dbgprt("EXIT totlen=%zd\n",totlen);
return totlen;
}
// xrecv -- receive buffer (guaranteed delivery)
ssize_t
xrecv(int sock,void *vp,size_t buflen,int flags)
{
char *buf = vp;
ssize_t curlen;
ssize_t totlen = 0;
dbgprt("ENTER buflen=%zu flags=%8.8X\n",buflen,flags);
for (; totlen < buflen; totlen += curlen) {
dbgprt("LOOP totlen=%zu\n",totlen);
curlen = recv(sock,&buf[totlen],buflen - totlen,flags);
if (curlen <= 0)
break;
}
dbgprt("EXIT totlen=%zd\n",totlen);
return totlen;
}
// open_remote -- client open connection to server
int
open_remote(const char *ip,unsigned short port)
{
int sock;
struct sockaddr_in echoserver;
dbgprt("ENTER ip=%s port=%u\n",dbgstr(ip,-1),port);
if ((sock = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP)) < 0) {
perror("Failed to create socket");
exit(1);
}
// NOTE/BUG: only server (who does bind) needs to do this
#if 0
int enable = 1;
if (setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&enable,sizeof(enable)) < 0) {
perror("error");
}
#endif
memset(&echoserver,0,sizeof(echoserver));
echoserver.sin_family = AF_INET;
echoserver.sin_addr.s_addr = inet_addr(ip);
echoserver.sin_port = htons(port);
if (connect(sock,(struct sockaddr *) &echoserver,sizeof(echoserver)) < 0) {
perror("Failed to connect with server");
exit(1);
}
dbgprt("EXIT sock=%d\n",sock);
return sock;
}
// send_cmd -- client send command to server and process reply
void
send_cmd(int type,const char *cmd,int paylen)
{
int sock;
xmsg_t xmsg;
char buffer[MAXBUFF];
dbgprt("ENTER type=%d\n",type);
// open socket to remote server
sock = open_remote(ipaddr,portno);
// send command descriptor
xmsg.xmsg_type = type;
if (paylen < 0)
paylen = strlen(cmd);
xmsg.xmsg_paylen = paylen;
xsend(sock,&xmsg,sizeof(xmsg),0);
// send command payload
xsend(sock,cmd,xmsg.xmsg_paylen,0);
fprintf(stdout,"Message from server:\n");
int received = 0;
int bytes;
// get all data that the server sends back
while (1) {
dbgprt("LOOP\n");
// get descriptor for next chunk
xrecv(sock,&xmsg,sizeof(xmsg),0);
// handle EOF from server
if (xmsg.xmsg_paylen <= 0)
break;
// get payload
bytes = recv(sock,buffer,xmsg.xmsg_paylen,0);
dbgprt("RCVD bytes=%d\n",bytes);
#if 0
if (bytes == 0)
break;
#endif
/* recv() from server; */
if (bytes < 0) {
perror("Failed to receive bytes from server");
break;
}
received += bytes;
dbgprt("PAYLOAD %s\n",dbgstr(buffer,bytes));
// send payload to terminal
fwrite(buffer,1,bytes,stdout);
}
close(sock);
dbgprt("EXIT\n");
}
void
doclient(void)
{
char cmdbuf[MAXBUFF];
char *argv[MAXARG];
tid = "clnt";
while (1) {
dbgprt("PROMPT\n");
printf(">> ");
fflush(stdout);
dbgprt("FGETS\n");
fgets(cmdbuf,sizeof(cmdbuf),stdin);
cmdbuf[strcspn(cmdbuf,"\n")] = 0;
dbgprt("COMMAND %s\n",dbgstr(cmdbuf,-1));
// tokenize the line
int argc = tokenize(argv,cmdbuf);
if (argc <= 0)
continue;
// set/display remote server IP address
if (strcmp(argv[0],"remote") == 0) {
if (argc >= 2)
strcpy(ipaddr,argv[1]);
if (ipaddr[0] != 0)
printf("REMOTE: %s\n",ipaddr);
continue;
}
// stop server
if (strcmp(argv[0],"stop") == 0) {
if (ipaddr[0] != 0) {
dbgprt("STOP/SERVER\n");
send_cmd(XMSG_CMD,cmdbuf,-1);
}
ipaddr[0] = 0;
continue;
}
// exit client program
if (strcmp(argv[0],"exit") == 0) {
dbgprt("STOP/CLIENT\n");
break;
}
// send command and echo response to terminal
send_cmd(XMSG_CMD,cmdbuf,-1);
}
dbgprt("EXIT\n");
}
// server_cmd -- process command on server
void
server_cmd(int new_socket)
{
xmsg_t xmsg;
char cmdbuf[MAXBUFF];
char *argv[MAXARG];
dbgprt("ENTER new_socket=%d\n",new_socket);
do {
// get command descriptor
xrecv(new_socket,&xmsg,sizeof(xmsg),0);
// get command text
xrecv(new_socket,cmdbuf,xmsg.xmsg_paylen,0);
cmdbuf[xmsg.xmsg_paylen] = 0;
dbgprt("COMMAND %s\n",dbgstr(cmdbuf,-1));
// tokenize the command
int argc = tokenize(argv,cmdbuf);
if (argc <= 0)
break;
// stop the server
if (strcmp(argv[0],"stop") == 0) {
dbgprt("KILL server_pid=%d\n",server_pid);
// FIXME -- we could send a "stopping server" message here
// send EOF to client
xmsg.xmsg_type = XMSG_EOF;
xmsg.xmsg_paylen = 0;
xsend(new_socket,&xmsg,sizeof(xmsg),0);
// signal the server main process to stop (cleanly)
if (opt_s)
server_signo = STOP_SIGNO;
else
kill(server_pid,STOP_SIGNO);
break;
}
int pipefd[2];
int length;
if (pipe(pipefd))
perror("Failed to create pipe");
pid_t pid = fork();
dbgprt("FORK pid=%d\n",pid);
// invoke the target program (under a pipe)
if (pid == 0) {
tid = "exec";
dbgprt("DUP2\n");
fflush(stdout);
int err = dup2(pipefd[1],1); // duplicate pipefd[1] to stdout
if (err < 0)
perror("dup2");
CLOSEME(pipefd[0]); // close the readonly side of the pipe
CLOSEME(pipefd[1]); // close the write side of the pipe
dbgprt("EXECVP\n");
CLOSEME(dbgfd);
if (opt_h) {
int len = sprintf(cmdbuf,"HELO\n");
write(1,cmdbuf,len);
}
execvp(argv[0],argv); // finally execute the command
perror("execvp");
exit(1);
}
// fork error
if (pid < 0) {
perror("fork");
exit(1);
}
dbgprt("PARENT\n");
CLOSEME(pipefd[1]);
// grab all output from the target program and send in packets to
// client
while (1) {
dbgprt("READBEG\n");
length = read(pipefd[0],cmdbuf,sizeof(cmdbuf));
dbgprt("READEND length=%d\n",length);
if (length < 0) {
perror("readpipe");
break;
}
if (length == 0)
break;
dbgprt("READBUF %s\n",dbgstr(cmdbuf,length));
// send descriptor for this chunk
xmsg.xmsg_type = XMSG_DATA;
xmsg.xmsg_paylen = length;
xsend(new_socket,&xmsg,sizeof(xmsg),0);
// send the payload
if (xsend(new_socket,cmdbuf,length,0) != length) {
perror("Failed");
}
}
CLOSEME(pipefd[0]);
// tell client we have no more data
xmsg.xmsg_paylen = 0;
xmsg.xmsg_type = XMSG_EOF;
xsend(new_socket,&xmsg,sizeof(xmsg),0);
} while (0);
CLOSEME(new_socket);
dbgprt("EXIT\n");
}
void
sighdr(int signo)
{
server_signo = signo;
}
void
doserver(void)
{
int server_fd,new_socket;
struct sockaddr_in address;
pid_t pid;
tid = "serv";
dbgprt("ENTER\n");
server_pid = getpid();
#if 0
signal(STOP_SIGNO,(void *) sighdr);
#else
struct sigaction act;
sigaction(STOP_SIGNO,NULL,&act);
act.sa_sigaction = (void *) sighdr;
sigaction(STOP_SIGNO,&act,NULL);
sigset_t set;
sigemptyset(&set);
sigaddset(&set,STOP_SIGNO);
sigprocmask(SIG_UNBLOCK,&set,NULL);
#endif
#if 0
int addrlen = sizeof(address);
#else
socklen_t addrlen = sizeof(address);
#endif
dbgprt("SOCKET\n");
// Creating socket file descriptor
if ((server_fd = socket(AF_INET,SOCK_STREAM,0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
int enable = 1;
if (setsockopt(server_fd,SOL_SOCKET,SO_REUSEADDR,&enable,sizeof(int)) < 0) {
perror("error");
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(portno);
dbgprt("BIND portno=%u\n",portno);
if (bind(server_fd,(struct sockaddr *) &address,sizeof(address)) < 0) {
perror("bind failed");
}
dbgprt("LISTEN\n");
if (listen(server_fd,MAXPENDING) < 0) {
perror("listen");
}
int pending = 0;
int status;
while (1) {
dbgprt("LOOP\n");
// reap all finished children
while (1) {
pid = waitpid(-1,&status,WNOHANG);
if (pid <= 0)
break;
dbgprt("REAP pid=%d pending=%d\n",pid,pending);
--pending;
}
// one of the children was given a stop command and it signaled us
if (server_signo) {
dbgprt("SIGNO server_signo=%d\n",server_signo);
break;
}
// wait for new connection from a client
// FIXME -- sending us a signal to stop cleanly is _broken_ because
// we do _not_ get an early return here (e.g. EINTR) -- we may need
// select with timeout
dbgprt("WAITACCEPT\n");
new_socket = accept(server_fd,(struct sockaddr *) &address,
(socklen_t *) &addrlen);
// stop cleanly
if (server_signo) {
dbgprt("SIGNO server_signo=%d\n",server_signo);
break;
}
if (new_socket < 0) {
if (errno == EINTR)
break;
perror("accept");
}
dbgprt("ACCEPTED\n");
// do command execution in main process (i.e. debug)
if (opt_n) {
server_cmd(new_socket);
continue;
}
pid = fork();
if (pid < 0) {
CLOSEME(new_socket);
continue;
}
// process the command in the child
if (pid == 0) {
server_cmd(new_socket);
exit(0);
}
++pending;
dbgprt("CHILD pid=%d\n",pid);
// server main doesn't need this after fork
#if 1
CLOSEME(new_socket);
#endif
}
// reap all children
while (pending > 0) {
pid = waitpid(-1,&status,0);
if (pid <= 0)
break;
dbgprt("REAP pid=%d pending=%d\n",pid,pending);
--pending;
}
dbgprt("EXIT\n");
}
int
main(int argc,char **argv)
{
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
char *cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'h':
opt_h = ! opt_h;
break;
case 'n': // do _not_ fork server
opt_n = ! opt_n;
break;
case 'p':
portno = (*cp != 0) ? atoi(cp) : PORTNO;
break;
case 's': // invoke server
opt_s = ! opt_s;
break;
}
}
tsczero = tscgetf();
#if DEBUG
int flags = O_WRONLY | O_APPEND;
if (opt_s)
flags |= O_TRUNC | O_CREAT;
dbgfd = open("debug.txt",flags,0644);
if (dbgfd < 0) {
perror("debug.txt");
exit(1);
}
#endif
if (opt_s)
doserver();
else
doclient();
#if DEBUG
if (dbgfd >= 0)
close(dbgfd);
#endif
return 0;
}
I have a client program and a server program. There could be multiple servers and multiple
clients that can connect to multiple servers of there choice
The client program lists a menu
connect 4000 // connects to server on port 4000
bid 1000 4000 // send a bid value of 1000 to the server at port 4000
Now a server may recieve bids from several clients connected to it and keeps track of the highest
bid till now. Whenever a new bid is placed the server sends a broadcast to each client connected
to it one by one like - write(users[i].sock_fd, msg, size).
How do I listen to this message on the client side ?
There are two things here
The client needs to listen to the message sent by server.
The client is also reading the text or menu items (connect and bid) from command line from the user.
I have coded the part 2) But confused how to code 1) into client and simultaneously make the 2) also working
Client code :
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#define BUF_SIZE 128
#define MAX_AUCTIONS 5
#ifndef VERBOSE
#define VERBOSE 0
#endif
#define ADD 0
#define SHOW 1
#define BID 2
#define QUIT 3
/* Auction struct - this is different than the struct in the server program
*/
typedef struct auction_data
{
int sock_fd;
char item[BUF_SIZE];
int current_bid;
} auction_data;
auction_data *auction_data_ptr;
/* Displays the command options available for the user.
* The user will type these commands on stdin.
*/
void print_menu()
{
printf("The following operations are available:\n");
printf(" show\n");
printf(" add <server address> <port number>\n");
printf(" bid <item index> <bid value>\n");
printf(" quit\n");
}
/* Prompt the user for the next command
*/
void print_prompt()
{
printf("Enter new command: ");
fflush(stdout);
}
/* Unpack buf which contains the input entered by the user.
* Return the command that is found as the first word in the line, or -1
* for an invalid command.
* If the command has arguments (add and bid), then copy these values to
* arg1 and arg2.
*/
int parse_command(char *buf, int size, char *arg1, char *arg2)
{
int result = -1;
char *ptr = NULL;
if (strncmp(buf, "show", strlen("show")) == 0)
{
return SHOW;
}
else if (strncmp(buf, "quit", strlen("quit")) == 0)
{
return QUIT;
}
else if (strncmp(buf, "add", strlen("add")) == 0)
{
result = ADD;
}
else if (strncmp(buf, "bid", strlen("bid")) == 0)
{
result = BID;
}
ptr = strtok(buf, " "); // first word in buf
ptr = strtok(NULL, " "); // second word in buf
if (ptr != NULL)
{
strncpy(arg1, ptr, BUF_SIZE);
}
else
{
return -1;
}
ptr = strtok(NULL, " "); // third word in buf
if (ptr != NULL)
{
strncpy(arg2, ptr, BUF_SIZE);
return result;
}
else
{
return -1;
}
return -1;
}
/* Connect to a server given a hostname and port number.
* Return the socket for this server
*/
int add_server(char *hostname, int port)
{
// Create the socket FD.
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd < 0)
{
perror("client: socket");
exit(1);
}
// Set the IP and port of the server to connect to.
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
struct addrinfo *ai;
/* this call declares memory and populates ailist */
if (getaddrinfo(hostname, NULL, NULL, &ai) != 0)
{
close(sock_fd);
return -1;
}
/* we only make use of the first element in the list */
server.sin_addr = ((struct sockaddr_in *)ai->ai_addr)->sin_addr;
// free the memory that was allocated by getaddrinfo for this list
freeaddrinfo(ai);
// Connect to the server.
if (connect(sock_fd, (struct sockaddr *)&server, sizeof(server)) == -1)
{
perror("client: connect");
close(sock_fd);
return -1;
}
if (VERBOSE)
{
fprintf(stderr, "\nDebug: New server connected on socket %d. Awaiting item\n", sock_fd);
}
return sock_fd;
}
/* ========================= Add helper functions below ========================
* Please add helper functions below to make it easier for the TAs to find the
* work that you have done. Helper functions that you need to complete are also
* given below.
*/
/* Print to standard output information about the auction
*/
void print_auctions(struct auction_data *a, int size)
{
printf("Current Auctions:\n");
for (int i = 0; i < size; i++)
{
struct auction_data auction_data = a[i];
printf("(%d) %s bid = %d\n", i, auction_data.item, auction_data.current_bid);
}
/* TODO Print the auction data for each currently connected
* server. Use the follosing format string:
* "(%d) %s bid = %d\n", index, item, current bid
* The array may have some elements where the auction has closed and
* should not be printed.
*/
}
/* Process the input that was sent from the auction server at a[index].
* If it is the first message from the server, then copy the item name
* to the item field. (Note that an item cannot have a space character in it.)
*/
void update_auction(char *buf, int size, struct auction_data *a, int index)
{
// TODO: Complete this function
// fprintf(stderr, "ERROR malformed bid: %s", buf);
// printf("\nNew bid for %s [%d] is %d (%d seconds left)\n", );
}
int main(void)
{
char name[BUF_SIZE];
int size = 0;
// Declare and initialize necessary variables
// TODO
// Get the user to provide a name.
printf("Please enter a username: ");
fflush(stdout);
int num_read = read(STDIN_FILENO, name, BUF_SIZE);
printf("%s-name\n", name);
if (num_read <= 0)
{
fprintf(stderr, "ERROR: read from stdin failed\n");
exit(1);
}
print_menu();
// TODO
char server_reply[2000];
while (1)
{
print_prompt();
char *command;
scanf("%m[^\n]s", &command);
getchar();
char arg1[100];
char arg2[100];
int commandNumber = parse_command(command, 1000, arg1, arg2);
char dest[100] = "";
strcpy(dest, name);
dest[strlen(dest) - 1] = '\0';
if (commandNumber == ADD)
{
printf("%s-name4\n", dest);
int port = atoi(arg2);
int sock_fd = add_server(arg1, port);
printf("%s-server\n", server_reply);
write(sock_fd, dest, strlen(dest));
auction_data_ptr = (auction_data *)realloc(auction_data_ptr, (size + 1) * sizeof(auction_data_ptr));
auction_data_ptr[size].sock_fd = sock_fd;
size++;
}
else if (commandNumber == SHOW)
{
print_auctions(auction_data_ptr, size);
}
else if (commandNumber == BID)
{
int itemIndex = atoi(arg1);
int bidValue = atoi(arg2);
printf("%d-test\n", auction_data_ptr[itemIndex].sock_fd);
send(auction_data_ptr[itemIndex].sock_fd, arg2, strlen(arg2), 0);
}
else if (commandNumber == QUIT)
{
}
// TODO
}
return 0; // Shoud never get here
}
Server Code :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifndef PORT
#define PORT 30000
#endif
#define MAX_BACKLOG 5
#define MAX_CONNECTIONS 20
#define BUF_SIZE 128
#define MAX_NAME 56
int verbose = 0;
struct user
{
int sock_fd;
char name[MAX_NAME];
int bid;
};
typedef struct
{
char *item;
int highest_bid; // value of the highest bid so far
int client; // index into the users array of the top bidder
} Auction;
/*
* Accept a connection. Note that a new file descriptor is created for
* communication with the client. The initial socket descriptor is used
* to accept connections, but the new socket is used to communicate.
* Return the new client's file descriptor or -1 on error.
*/
int accept_connection(int fd, struct user *users)
{
int user_index = 0;
while (user_index < MAX_CONNECTIONS && users[user_index].sock_fd != -1)
{
user_index++;
}
if (user_index == MAX_CONNECTIONS)
{
fprintf(stderr, "server: max concurrent connections\n");
return -1;
}
int client_fd = accept(fd, NULL, NULL);
if (client_fd < 0)
{
perror("server: accept");
close(fd);
exit(1);
}
users[user_index].sock_fd = client_fd;
users[user_index].name[0] = '\0';
return client_fd;
}
/* Remove \r\n from str if the characters are at the end of the string.
* Defensively assuming that \r could be the last or second last character.
*/
void strip_newline(char *str)
{
if (str[strlen(str) - 1] == '\n' || str[strlen(str) - 1] == '\r')
{
if (str[strlen(str) - 2] == '\r')
{
str[strlen(str) - 2] = '\0';
}
else
{
str[strlen(str) - 1] = '\0';
}
}
}
/*
* Read a name from a client and store in users.
* Return the fd if it has been closed or 0 otherwise.
*/
int read_name(int client_index, struct user *users)
{
int fd = users[client_index].sock_fd;
/* Note: This is not the best way to do this. We are counting
* on the client not to send more than BUF_SIZE bytes for the
* name.
*/
int num_read = read(fd, users[client_index].name, MAX_NAME);
if (num_read == 0)
{
users[client_index].sock_fd = -1;
return fd;
}
users[client_index].name[num_read] = '\0';
strip_newline(users[client_index].name);
if (verbose)
{
fprintf(stderr, "[%d] Name: %s\n", fd, users[client_index].name);
}
/*
if (num_read == 0 || write(fd, buf, strlen(buf)) != strlen(buf)) {
users[client_index].sock_fd = -1;
return fd;
}
*/
return 0;
}
/* Read a bid from a client and store it in bid.
* If the client does not send a number, bid will be set to -1
* Return fd if the socket is closed, or 0 otherwise.
*/
int read_bid(int client_index, struct user *users, int *bid)
{
printf("inside bid\n");
int fd = users[client_index].sock_fd;
char buf[BUF_SIZE];
char *endptr;
int num_read = read(fd, buf, BUF_SIZE);
if (num_read == 0)
{
return fd;
}
buf[num_read] = '\0';
if (verbose)
{
fprintf(stderr, "[%d] bid: %s", fd, buf);
}
// Check if the client sent a valid number
// (We are not checking for a good bid here.)
errno = 0;
*bid = strtol(buf, &endptr, 10);
if (errno != 0 || endptr == buf)
{
*bid = -1;
}
return 0;
}
void broadcast(struct user *users, char *msg, int size)
{
for (int i = 0; i < MAX_CONNECTIONS; i++)
{
if (users[i].sock_fd != -1)
{
if (write(users[i].sock_fd, msg, size) == -1)
{
// Design flaw: can't remove this socket from select set
close(users[i].sock_fd);
users[i].sock_fd = -1;
}
}
}
}
int prep_bid(char *buf, Auction *a, struct timeval *t)
{
// send item, current bid, time left in seconds
printf("robin2-%s-%d\n", a->item, a->highest_bid);
printf("robin-%ld\n", t->tv_sec);
sprintf(buf, "%s %d %ld", a->item, a->highest_bid, t->tv_sec);
printf("robin-bid2\n");
return 0;
}
/* Update auction if new_bid is higher than current bid.
* Write to the client who made the bid if it is lower
* Broadcast to all clients if the bid is higher
*/
int update_bids(int client_index, struct user *users,
int new_bid, Auction *auction, struct timeval *t)
{
char buf[BUF_SIZE];
if (new_bid > auction->highest_bid)
{
auction->highest_bid = new_bid;
auction->client = client_index;
prep_bid(buf, auction, t);
if (verbose)
{
fprintf(stderr, "[%d] Sending to %d:\n %s\n",
getpid(), users[client_index].sock_fd, buf);
}
broadcast(users, buf, strlen(buf) + 1);
}
else
{
fprintf(stderr, "Client %d sent bid that was too low. Ignored\n",
client_index);
}
return 0;
}
int main(int argc, char **argv)
{
argc = 7;
argv[1] = "-v";
argv[2] = "-t";
argv[3] = "5";
argv[4] = "-p";
argv[5] = "4000";
argv[6] = "robin";
Auction auction;
int opt;
int port = PORT;
struct timeval timeout;
struct timeval *time_ptr = NULL;
int minutes = 0;
while ((opt = getopt(argc, argv, "vt:p:")) != -1)
{
switch (opt)
{
case 'v':
verbose = 1;
break;
case 't':
minutes = atoi(optarg);
timeout.tv_sec = minutes * 60;
timeout.tv_usec = 0;
time_ptr = &timeout;
break;
case 'p':
port = atoi(optarg);
break;
default:
fprintf(stderr, "Usage: auction_server [-v] [-t timeout] [-p port] item\n");
exit(1);
}
}
if (optind >= argc)
{
fprintf(stderr, "Expected argument after options\n");
exit(1);
}
auction.item = argv[optind];
auction.client = -1;
auction.highest_bid = -1;
struct user users[MAX_CONNECTIONS];
for (int index = 0; index < MAX_CONNECTIONS; index++)
{
users[index].sock_fd = -1;
users[index].name[0] = '\0';
}
// Create the socket FD.
int sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd < 0)
{
perror("server: socket");
exit(1);
}
// Set information about the port (and IP) we want to be connected to.
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = INADDR_ANY;
// This sets an option on the socket so that its port can be reused right
// away. Since you are likely to run, stop, edit, compile and rerun your
// server fairly quickly, this will mean you can reuse the same port.
int on = 1;
int status = setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
(const char *)&on, sizeof(on));
if (status == -1)
{
perror("setsockopt -- REUSEADDR");
}
// This should always be zero. On some systems, it won't error if you
// forget, but on others, you'll get mysterious errors. So zero it.
memset(&server.sin_zero, 0, 8);
// Bind the selected port to the socket.
if (bind(sock_fd, (struct sockaddr *)&server, sizeof(server)) < 0)
{
perror("server: bind");
close(sock_fd);
exit(1);
}
// Announce willingness to accept connections on this socket.
if (listen(sock_fd, MAX_BACKLOG) < 0)
{
perror("server: listen");
close(sock_fd);
exit(1);
}
if (verbose)
{
fprintf(stderr, "[%d] Ready to accept connections on %d\n",
getpid(), port);
}
// The client accept - message accept loop. First, we prepare to listen
// to multiple file descriptors by initializing a set of file descriptors.
int max_fd = sock_fd;
fd_set all_fds;
FD_ZERO(&all_fds);
FD_SET(sock_fd, &all_fds);
while (1)
{
// select updates the fd_set it receives, so we always use a copy
// and retain the original.
fd_set listen_fds = all_fds;
int nready;
if ((nready = select(max_fd + 1, &listen_fds, NULL, NULL, time_ptr)) == -1)
{
perror("server: select");
exit(1);
}
if (nready == 0)
{
char buf[BUF_SIZE];
sprintf(buf, "Auction closed: %s wins with a bid of %d\r\n",
users[auction.client].name, auction.highest_bid);
printf("%s", buf);
broadcast(users, buf, BUF_SIZE);
exit(0);
}
// Is it the original socket? Create a new connection ...
if (FD_ISSET(sock_fd, &listen_fds))
{
int client_fd = accept_connection(sock_fd, users);
if (client_fd != -1)
{
if (client_fd > max_fd)
{
max_fd = client_fd;
}
FD_SET(client_fd, &all_fds);
if (verbose)
{
fprintf(stderr, "[%d] Accepted connection on %d\n",
getpid(), client_fd);
}
}
}
// Next, check the clients.
for (int index = 0; index < MAX_CONNECTIONS; index++)
{
if (users[index].sock_fd > -1 && FD_ISSET(users[index].sock_fd, &listen_fds))
{
int client_closed = 0;
int new_bid = 0;
if (users[index].name[0] == '\0')
{
client_closed = read_name(index, users);
if (client_closed == 0)
{
char buf[BUF_SIZE];
prep_bid(buf, &auction, time_ptr);
if (verbose)
{
fprintf(stderr, "[%d] Sending to %d:\n %s\n",
getpid(), users[index].sock_fd, buf);
}
if (write(users[index].sock_fd, buf, strlen(buf) + 1) == -1)
{
fprintf(stderr, "Write to %d failed\n", sock_fd);
close(sock_fd);
}
}
}
else
{ // read a bid
client_closed = read_bid(index, users, &new_bid);
if (client_closed == 0)
{
update_bids(index, users, new_bid, &auction, time_ptr);
}
}
if (client_closed > 0)
{
FD_CLR(client_closed, &all_fds);
printf("Client %d disconnected\n", client_closed);
}
}
}
}
// Should never get here.
return 1;
}
Caveat: Because you've only posted partial code for server and client, this will be some suggestions.
Your client can attach/connect to multiple bid servers simultaneously. As such, it must be able to keep track of the multiple connections in a manner similar to a server.
Your main [stated] issue is that you're blocking the client on a user prompt (e.g. from stdin via scanf et. al.). Presently, this means that the client is "stuck" at user input prompt and can not field messages from the servers it is connected to. More on how to fix this below.
So, you'll have a bunch of code from the server that needs to be in the client with some minor differences. You may wish to generalize some of the server code a bit, so it can work both in server and client (e.g. you may want to move it to common.c).
You already have code in the server to handle multiple connections. The server needs a select mask that is the OR of the listen fd and all active client fds.
Likewise, your client needs a select mask that is the OR of the fd for user input (e.g. 0) and all active server connections.
Doing select on fd 0 and using stdio.h streams won't work too well. So, replace access to stdin with (e.g.) read(0,line_buffer,sizeof(line_buffer)). You do this if fd 0 is set in the select mask. The role is very similar to what your server does for the accept on sock_fd.
You'll need to allow for partial reads and append to the buffer until you see a newline. So, you'll have to do the work that fgets would normally do in assembling a whole line. Then, you can call parse_command.
Because read doesn't understand newline demarcations, the user could enter more than one line before you can do a read.
So, for user input of:
connect 4000\n
bid 100 4000\n
connect 5000\n
You may get partial reads of:
conn
ect
4000\nbid 100 4000
\nconnect
5000\n
You may also need to use the FIONREAD ioctl on the fd 0 to prevent blocking. And, you may need to set the kernel TTY layer into raw mode via termios calls.
The client now becomes very similar to your server code. It will handle [asynchronously] actions by any connected servers and user input.
A tip: Under the DRY principle ["don't repeat yourself"] ...
You already have a struct user in the server. The client will need something similar/identical, such as struct server. When generalizing the code, rather than having two distinct structs that do essentially the same thing, consider renaming the existing struct to (e.g.) struct connection
I have looked at the similar threads but can't seem to find anything that could solve my problem.
I am programming a server that could send an image(jpg) file from the path sent to it from the client. I am using send/recv functions in C for that.
I read files one chunk of data at a time and send it to the client that receives the contents and writes them at some location to build the file.
Problem is 'recv' doesn't receive the number of bytes sent by the 'send'.
As part of debugging, I have tried different buffer sizes and '128' buffer size doesn't give me any problem, and the file is successfully transferred and built.
However, for '32' and '64' bit buffers, 'recv' receives '32' bit or '64' bit data at the last chunk, even though the data sent by the server is less than either '32' bit or '64' bit. And, for '256', '512', '1024' so on, 'recv' returns ONLY '128' bits at EXACTLY one of the responses, even though the server sends complete chunk, i.e. '256' or'512', depending on the buffer size.
I'll appreciate any advise for debugging. Following code is for the relevant parts only, but I can provide more, should someone requires it.
//Client Code
#define BUFFER_SIZE 4096
#define HEADER_LEN 512
const char * const scheme = "GETFILE";
const char* const method = "GET";
const char * const end_marker = "\\r\\n\\r\\n";
struct gfcrequest_t
{
int filelen;
char cport[12];
char servIP[50];
gfstatus_t ret_status;
char spath[1024];
int tot_bytes;
char filecontent[BUFFER_SIZE];
void (*fl_handler)(void *, size_t, void *);
void * fDesc;
void (*head_handler)(void *, size_t, void *);
void * headarg;
};
static pthread_mutex_t counter_mutex;
gfcrequest_t *gfc;
// 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);
}
static char *stringFromError(gfstatus_t stat)
{
static const char *strings[] = {"GF_OK", "GF_FILE_NOT_FOUND", "GF_ERROR", "GF_INVALID"};
return strings[stat];
}
int getFileRequestHeader(char * req_header)
{
return snprintf(req_header,HEADER_LEN, "%s%s%s%s", scheme, method,gfc->spath, end_marker);
// return snprintf(req_header,HEADER_LEN, "%s%s%s%s", scheme, method,"/courses/ud923/filecorpus/yellowstone.jpg", end_marker);
}
gfcrequest_t *gfc_create()
{
gfc = (gfcrequest_t*)malloc(1* sizeof(gfcrequest_t));
return gfc;
}
void gfc_set_server(gfcrequest_t *gfr, char* server)
{
strcpy(gfr->servIP, server);
}
void gfc_set_path(gfcrequest_t *gfr, char* path)
{
strcpy(gfr->spath, path);
}
void gfc_set_port(gfcrequest_t *gfr, unsigned short port)
{
snprintf(gfr->cport,12, "%u",port);
}
void gfc_set_headerfunc(gfcrequest_t *gfr, void (*headerfunc)(void*, size_t, void *))
{
gfr->head_handler = headerfunc;
}
void gfc_set_headerarg(gfcrequest_t *gfr, void *headerarg)
{
/*have to change this...*/
gfr->headarg = headerarg;
}
int isEndMarker(char *iheader, int start)
{
char *marker = "\\r\\n\\r\\n";
int i = 0; int ind=0;
while (ind <= 7)
{
if (iheader[start++] == marker[ind++])
{
continue;
}
return 0;
}
return 1;
}
int getFileLen(char *resp_header, gfcrequest_t *gfr)
{
char scheme[8];
char status[4];
int istatus;
char filelen[12];
int contentlen = 0;
int fileindex = 0;
char end_marker[12];
int fexit=0;
int end=0;
sscanf(resp_header, "%7s%3s", scheme, status);
istatus = atoi(status);
if (istatus == 200)
{
gfr->ret_status = GF_OK;
}
else if (istatus == 400)
{
gfr->ret_status = GF_FILE_NOT_FOUND;
}
else if (istatus == 500)
{
gfr->ret_status = GF_ERROR;
}
if (!strcmp(scheme, "GETFILE") && (istatus == 200 || istatus == 400 || istatus == 500))
{
int index = 10;
while(1)
{
if (resp_header[index] == '\\')
{
end = isEndMarker(resp_header, index);
}
if (end)
break;
filelen[fileindex++] = resp_header[index++];
}
filelen[fileindex] = '\0';
}
int head_len = strlen(scheme) + strlen(status) + strlen(filelen) + 8;
return atoi(filelen);
}
void gfc_set_writefunc(gfcrequest_t *gfr, void (*writefunc)(void*, size_t, void *))
{
gfr->fl_handler = writefunc;
}
void gfc_set_writearg(gfcrequest_t *gfr, void *writearg)
{
gfr->fDesc = writearg;
}
int gfc_perform(gfcrequest_t *gfr){
struct addrinfo hints, *servinfo, *p;
int sockfd, rv, totalBytesRcvd;
char req_header[HEADER_LEN];
int bytesRcvd;
int header_len;
char s[INET6_ADDRSTRLEN];
memset(&hints, 0, sizeof(hints));
memset(req_header,0,sizeof(req_header));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; //use my IP
if ((rv = getaddrinfo(NULL, gfr->cport, &hints, &servinfo)) != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL; p = p->ai_next)
{
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
{
perror("client: socket");
continue;
}
if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1)
{
close(sockfd);
perror("client: connect");
continue;
}
break;
}
if (p == NULL)
{
fprintf(stderr, "client: failed to connect\n");
return 2;
}
//printf("connected...\n");
inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof(s));
//Ahsan
// printf("Before getFileRequestHeader...\n");
header_len = getFileRequestHeader(req_header);
//printf("Header Description:%s, Header Len: %u\n", req_header, header_len);
if (send(sockfd, req_header, header_len, 0) != header_len)
perror("send() sent a different number of bytes than expected");
if ((bytesRcvd = recv(sockfd, gfr->filecontent, BUFFER_SIZE, 0)) <= 0)
perror("recv() failed or connection closed prematurely");
//printf("Header Received: %s\n", gfr->filecontent);
gfr->filelen = getFileLen(gfr->filecontent, gfr);
//printf("File Length: %d\n", gfr->filelen);
/* Receive the same string back from the server */
int req_no=1;
gfr->tot_bytes = 0;
while ( 1 )
{
printf("Request: %d ", req_no++);
ssize_t nb = recv( sockfd, gfr->filecontent, BUFFER_SIZE, 0 );
if ( nb == -1 ) err( "recv failed" );
if ( nb == 0 ) {printf("zero bytes received...breaking");break;} /* got end-of-stream */
gfr->fl_handler(gfr->filecontent, nb, gfr->fDesc);
gfr->tot_bytes += nb;
printf("Received Bytes: %zd Total Received Bytes: %d\n", nb, gfr->tot_bytes);
}
return 0;
}
/*
* Returns the string associated with the input status
*/
char* gfc_strstatus(gfstatus_t status)
{
return stringFromError(status);
}
gfstatus_t gfc_get_status(gfcrequest_t *gfr){
return gfr->ret_status;
}
size_t gfc_get_filelen(gfcrequest_t *gfr)
{
return gfr->filelen;
}
size_t gfc_get_bytesreceived(gfcrequest_t *gfr)
{
return gfr->tot_bytes;
}
void gfc_cleanup(gfcrequest_t *gfr)
{
free(gfr);
gfr=NULL;
}
void gfc_global_init()
{
;
// pthread_mutex_lock(&counter_mutex);
//
// gfc = (gfcrequest_t*)malloc(1* sizeof(gfcrequest_t));
//
// pthread_mutex_unlock(&counter_mutex);
}
void gfc_global_cleanup()
{
;
// pthread_mutex_lock(&counter_mutex);
//
// free(gfc);
//
// pthread_mutex_unlock(&counter_mutex);
}
//Server Code
struct gfcontext_t
{
int sockfd;
int clntSock;
};
struct gfserver_t
{
char port[12];
unsigned short max_npending;
char fpath[256];
ssize_t (*fp_handler)(gfcontext_t *ctx, char *, void*);
int *handler_arg;
};
/*Variable decalation*/
static gfserver_t *gfserv;
static gfcontext_t *gfcontext;
int isEndMarker(char *iheader, int start)
{
char *marker = "\\r\\n\\r\\n";
int i = 0; int ind=0;
while (ind <= 7)
{
if (iheader[start++] == marker[ind++])
{
//printf("Header Char:%c Marker:%c\n", iheader[start], marker[ind]);
//start++;
continue;
}
return 0;
}
//printf("Its a marker!!!\n");
return 1;
}
int parseHeader(char *iheader, gfserver_t *gfs, int hlen)
{
//"GETFILEGET/courses/ud923/filecorpus/road.jpg\r\n\r\n"
char scheme[8];
char method[4];
// char path[256];
int pathindex = 0;
char end_marker[12];
int end = 0;
int fexit=0;
sscanf(iheader, "%7s%3s", scheme, method);
int i=10;
if (iheader[i] == '/')
{
// printf("Path has started...\n");
if (!strcmp(scheme, "GETFILE") && !strcmp(method, "GET"))
{
while(1)
{
if (iheader[i] == '\\')
{
end = isEndMarker(iheader, i);
}
if (end)
break;
gfs->fpath[pathindex++] = iheader[i++];
}
gfs->fpath[pathindex] = '\0';
}
}
printf("Scheme: %s Method:%s Path:%s\n", scheme, method, gfs->fpath);
return 0;
}
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);
}
ssize_t gfs_sendheader(gfcontext_t *ctx, gfstatus_t stat, size_t file_len)
{
char resp_header[MAX_REQUEST_LEN];
char end_marker[12] = "\\r\\n\\r\\n";
sprintf(resp_header, "GETFILE%d%zd%s",stat, file_len, end_marker);
printf("Response: %s\n", resp_header);
if (send(ctx->clntSock, resp_header, MAX_REQUEST_LEN, 0) != MAX_REQUEST_LEN)
perror("send() failed");
return 0;
}
ssize_t gfs_send(gfcontext_t *ctx, void *data, size_t len)
{
size_t total = 0;
size_t bytesLeft = len;
size_t n;
int debug_req=1;
while (total < len)
{
n = send(ctx->clntSock, data+total, bytesLeft, 0);
if (n == -1) { printf("Nothing to send...\n"); break; }
fprintf(stderr, "Tries: %d Bytes Sent: %zu\n", debug_req++, n);
total += n;
bytesLeft -= n;
}
// if ( shutdown( ctx->clntSock, SHUT_WR ) == -1 ) err( "socket shutdown failed" );
return total;
}
void gfs_abort(gfcontext_t *ctx){
close(ctx->clntSock);
close(ctx->sockfd);
free(ctx);
free(gfserv);
perror("aborting...");
exit(1);
}
gfserver_t* gfserver_create()
{
gfserv = (gfserver_t*) malloc(1 * sizeof(gfserver_t));
gfcontext = (gfcontext_t*) malloc(1*sizeof(gfcontext_t));
return gfserv;
}
void gfserver_set_port(gfserver_t *gfs, unsigned short port)
{
//set port number in gfs structure
snprintf(gfs->port,12, "%u",port);
}
void gfserver_set_maxpending(gfserver_t *gfs, int max_npending)
{
//set maxpending connections
gfs->max_npending = max_npending;
}
void gfserver_set_handler(gfserver_t *gfs, ssize_t (*handler)(gfcontext_t *, char *, void*))
{
gfs->fp_handler = handler;
}
void gfserver_set_handlerarg(gfserver_t *gfs, void* arg)
{
gfs->handler_arg = (int *)arg;
}
void gfserver_serve(gfserver_t *gfs)
{
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage clntAddr; //connectors address information
socklen_t clntSize;
int yes = 1;
char s[INET6_ADDRSTRLEN];
int rv;
int rcvMsg = 0;
char recvBuff[MAX_REQUEST_LEN];
char *req_path;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; //using stream socket instead of datagrams
hints.ai_flags = AI_PASSIVE; //use my IP
if ((rv = getaddrinfo(NULL, gfs->port, &hints, &servinfo)) != 0)
{
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next)
{
if ((gfcontext->sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
{
perror("server: socket");
continue;
}
//get rid of 'address already in use' error.
if (setsockopt(gfcontext->sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
{
perror("setsockopt");
exit(1);
}
if (bind(gfcontext->sockfd, p->ai_addr, p->ai_addrlen) == -1)
{
close(gfcontext->sockfd);
perror("server: bind");
continue;
}
break;
}
if (p == NULL)
{
fprintf(stderr, "server: failed to bind.\n");
return 2;
}
freeaddrinfo(servinfo); // no need of servinfo structure anymore
if (listen(gfcontext->sockfd, gfs->max_npending) == -1)
{
perror("listen");
exit(1);
}
//printf("server: waiting for connetions...\n");
while(1)
{
clntSize = sizeof(clntAddr);
gfcontext->clntSock = accept(gfcontext->sockfd, (struct sockaddr *)&clntAddr, &clntSize);
if (gfcontext->clntSock == -1)
{
perror("accept");
continue;
}
inet_ntop(clntAddr.ss_family, get_in_addr((struct sockaddr *)&clntAddr), s, sizeof(s));
//printf("server: got connection from %s\n", s);
if (!fork())
{ // this is the child process
if ((rcvMsg = recv(gfcontext->clntSock, recvBuff, MAX_REQUEST_LEN, 0)) < 0)
{
perror("recv() failed");
exit(1);
}
/*Still to parse received request...*/
//printf("Recd Header: %s, Recd %d bytes\n",recvBuff, rcvMsg);
/*Parse the received header...*/
int len = parseHeader(recvBuff, gfs, rcvMsg);
//printf("Requested Path: %s\n", gfs->fpath);
if (gfs->fp_handler(gfcontext, gfs->fpath, NULL) < 0)
{
printf("some problem...\n");
}
if ( shutdown( gfcontext->clntSock, SHUT_WR ) == -1 ) err( "socket shutdown failed" );
//close(gfcontext->clntSock);
}
}
}
//Server gf_send is being called from following function handler function:
Handler:
ssize_t handler_get(gfcontext_t *ctx, char *path, void* arg){
int fildes;
size_t file_len, bytes_transferred;
ssize_t read_len, write_len;
char buffer[BUFFER_SIZE];
printf("Path: %s\n", path);
if( 0 > (fildes = content_get(path)))
return gfs_sendheader(ctx, GF_FILE_NOT_FOUND, 0);
/* Calculating the file size */
file_len = lseek(fildes, 0, SEEK_END);
gfs_sendheader(ctx, GF_OK, file_len);
/* Sending the file contents chunk by chunk. */
int req=1;
bytes_transferred = 0;
while(bytes_transferred < file_len){
read_len = pread(fildes, buffer, BUFFER_SIZE, bytes_transferred);
if (read_len <= 0){
fprintf(stderr, "handle_with_file read error, %zd, %zu, %zu", read_len, bytes_transferred, file_len );
gfs_abort(ctx);
return -1;
}
printf("Request No: %d ", req++);
write_len = gfs_send(ctx, buffer, read_len);
if (write_len != read_len){
fprintf(stderr, "handle_with_file write error");
gfs_abort(ctx);
return -1;
}
bytes_transferred += write_len;
}
printf("Total Bytes sent to client: %zu\n", bytes_transferred);
return bytes_transferred;
}
You didn't specify, so I am assuming you are using TCP here (send/receive semantics is different with UDP),
You are suffering from a very common misconception that one send on one end of a TCP socket corresponds to one receive of sent number of bytes on the other end. This is wrong.
In fact, TCP socket is a bi-directional stream of bytes with no notion of messages. One write can correspond to many reads on the other end, and vise versa. Treat it as a stream.
You need to keep number of bytes sent and received as returned from sending and receiving system calls.
It is also important to let the other side know how much data you are sending, so it will know when, say, an image is fully transferred. This is the job of an application-level protocol that you have to either come up with or use an existing one.
Edit 0:
Here is what looks needed even before setting up any meaningful protocol between client and the server.
First the sending code:
size_t total = 0;
while ( total != len ) {
ssize_t nb = send( s, data + total, len - total, 0 );
if ( nb == -1 ) err( "send failed" );
total += nb;
}
if ( shutdown( s, SHUT_WR ) == -1 ) err( "socket shutdown failed" );
/* also need to close client socket, see below */
Then the receiving code:
char buffer[BUFFER_SIZE]; /* somewhere, might be static */
size_t total = 0; /* everything received */
while ( 1 ) {
ssize_t nb = recv( s, buffer, BUFFER_SIZE, 0 );
if ( nb == -1 ) err( "recv failed" );
if ( nb == 0 ) break; /* got end-of-stream */
if ( write( file_fd, buffer, nb ) == -1 ) err( "file write failed" );
total += nb;
}
/* send an ack here */
if ( close( s ) == -1 ) err( "socket close failed" );
if ( close( file_fd )) err( "file close failed" );
printf( "received and saved total of %zu bytes\n", total );
Then your application-level protocol might be as simple as server sending, say, 64-bit file length immediately after accepting new client connection (you need to decide what endianness to use for that), then after sending that many bytes to the client and shutting down writing on the socket, waiting for the client to acknowledge successful receipt of data. That might be that same number back, or just a single byte - up to you, then finally closing the socket. That way the client knows upfront how many bytes to expect, and the server knows that transfer was successful.
After getting this simple version working you can extend it to allow multiple file transfers per connection, and/or dive into IO multiplexing with select(2)/poll(2).
Hope this helps.
First of all: recv() does not always receive the data in the chunks that it was sent by send(), as a matter of fact - it rarely does - because of buffering (for example, you send 256-bytes receive two buffers of 128-bytes each)
Now to your error: I think the problem is that you are not be calling select() with a FD_SET to reset your socket to a "ready to receive" state before calling recv() a second time.
I have a metric-ton of winsock/c-sockets code on my site if you want to dig through it.
Let me know if I can expand on this answer, I'd be happy to provide additional assistance!
gfr->fl_handler(gfr->filecontent, BUFFER_SIZE, gfr->fDesc);
Usual problem. You're assuming the read filled the buffer. It should be:
gfr->fl_handler(gfr->filecontent, bytesRcvd, gfr->fDesc);
I am trying to execute a command on a router via ssh. After the login, when I execute the command on the device, it asks for an additional password. I am not able to send the password using libssh2_channel_write(). Here is the code snippet (modified the ssh2_exec.c that comes with the library). This is a snippet where the device is authenticated and the command has been issued. This loop just tries to get read the output of the executed command:
for( ;; )
{
/* loop until we block */
int rc;
do
{
char buffer[0x4000];
rc = libssh2_channel_read( channel, buffer, sizeof(buffer) );
if( rc > 0 )
{
int i;
char *enable = "stic-isr2951-t1";
int ret;
bytecount += rc;
fprintf(stderr, "We read [%d] bytes:\n", bytecount);
for( i=0; i < rc; ++i )
fputc( buffer[i], stderr);
**if ( strstr(buffer, "assword:") != NULL ){
fprintf(stderr, "Sending the additional password now\n");
ret = libssh2_channel_write(channel, enable, strlen(enable));
fprintf(stderr, "Wrote [%d] bytes\n", ret);
}**
}
else {
if( rc != LIBSSH2_ERROR_EAGAIN )
/* no need to output this for the EAGAIN case */
fprintf(stderr, "libssh2_channel_read returned %d\n", rc);
}
}
while( rc > 0 );
/* this is due to blocking that would occur otherwise so we loop on
this condition */
if( rc == LIBSSH2_ERROR_EAGAIN )
{
waitsocket(sock, session);
}
else
break;
}
In the snippet above, the code that detects that the device is posting a password prompt is:
if ( strstr(buffer, "assword:") != NULL ){
fprintf(stderr, "Sending the additional password now\n");
ret = libssh2_channel_write(channel, enable, strlen(enable));
fprintf(stderr, "Wrote [%d] bytes\n", ret);
}
That's where I have a problem. The password being sent on the channel isn't working as the device continues to timeout expecting the password. There is no indication that libssh2_channel_write() failed as the return value says it wrote the password properly.
Am I missing something?
EDIT:
The problem with the continuous timeout password prompted was because the password didn't have \n at the end. I was expecting the lib to take care of it but it didn't.
Now that I am able to send the password to the remote device, I run into another issue. After I send the password via libssh2_channel_write(), subsequent libssh2_channel_read() fails with
LIBSSH2_ERROR_SOCKET_RECV
I am not sure why is this happening. Logic was to check if the libssh2_channel_write() was successful by doing a subsequent read() (which would give the command prompt on the remote device) and then issue the command to be executed on the remote device followed by a subsequent read to get the command output. Am I doing something wrong? This doesn't seem to be working. Here's the complete code snippet:
/*
* Sample showing how to use libssh2 to execute a command remotely.
*
* The sample code has fixed values for host name, user name, password
* and command to run.
*
* Run it like this:
*
* $ ./ssh2_exec 127.0.0.1 user password "uptime"
*
*/
#include "libssh2_config.h"
#include <libssh2.h>
#include <string.h>
#ifdef HAVE_WINSOCK2_H
#include <winsock2.h>
#endif
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#include <sys/time.h>
#include <sys/types.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>
static int waitsocket(int socket_fd, LIBSSH2_SESSION *session)
{
struct timeval timeout;
int rc;
fd_set fd;
fd_set *writefd = NULL;
fd_set *readfd = NULL;
int dir;
timeout.tv_sec = 10;
timeout.tv_usec = 0;
FD_ZERO(&fd);
FD_SET(socket_fd, &fd);
/* now make sure we wait in the correct direction */
dir = libssh2_session_block_directions(session);
if(dir & LIBSSH2_SESSION_BLOCK_INBOUND)
readfd = &fd;
if(dir & LIBSSH2_SESSION_BLOCK_OUTBOUND)
writefd = &fd;
rc = select(socket_fd + 1, readfd, writefd, NULL, &timeout);
return rc;
}
int main(int argc, char *argv[])
{
const char *hostname = "10.10.10.10";
const char *commandline = "show version";
const char *username = "user1";
const char *password = "password1";
unsigned long hostaddr;
int flag = 0;
int sock;
struct sockaddr_in sin;
const char *fingerprint;
LIBSSH2_SESSION *session;
LIBSSH2_CHANNEL *channel;
int rc;
int exitcode;
char *exitsignal=(char *)"none";
int bytecount = 0;
size_t len;
LIBSSH2_KNOWNHOSTS *nh;
int type;
if (argc > 1)
/* must be ip address only */
hostname = argv[1];
if (argc > 2) {
username = argv[2];
}
if (argc > 3) {
password = argv[3];
}
if (argc > 4) {
commandline = argv[4];
}
rc = libssh2_init (0);
if (rc != 0) {
fprintf (stderr, "libssh2 initialization failed (%d)\n", rc);
return 1;
}
hostaddr = inet_addr(hostname);
printf("host address is: %ld\n", hostaddr);
/* Ultra basic "connect to port 22 on localhost"
* Your code is responsible for creating the socket establishing the
* connection
*/
sock = socket(AF_INET, SOCK_STREAM, 0);
sin.sin_family = AF_INET;
sin.sin_port = htons(22);
sin.sin_addr.s_addr = hostaddr;
if (connect(sock, (struct sockaddr*)(&sin),
sizeof(struct sockaddr_in)) != 0) {
fprintf(stderr, "failed to connect!\n");
return -1;
}
/* Create a session instance */
session = libssh2_session_init();
if (!session)
return -1;
//libssh2_trace(session, LIBSSH2_TRACE_AUTH|LIBSSH2_TRACE_SOCKET);
/* tell libssh2 we want it all done non-blocking */
libssh2_session_set_blocking(session, 0);
/* ... start it up. This will trade welcome banners, exchange keys,
* and setup crypto, compression, and MAC layers
*/
while ((rc = libssh2_session_handshake(session, sock)) ==
LIBSSH2_ERROR_EAGAIN);
if (rc) {
fprintf(stderr, "Failure establishing SSH session: %d\n", rc);
return -1;
}
nh = libssh2_knownhost_init(session);
if(!nh) {
/* eeek, do cleanup here */
return 2;
}
/* read all hosts from here */
libssh2_knownhost_readfile(nh, "known_hosts",
LIBSSH2_KNOWNHOST_FILE_OPENSSH);
/* store all known hosts to here */
libssh2_knownhost_writefile(nh, "dumpfile",
LIBSSH2_KNOWNHOST_FILE_OPENSSH);
fingerprint = libssh2_session_hostkey(session, &len, &type);
if(fingerprint) {
struct libssh2_knownhost *host;
#if LIBSSH2_VERSION_NUM >= 0x010206
/* introduced in 1.2.6 */
int check = libssh2_knownhost_checkp(nh, hostname, 22,
fingerprint, len,
LIBSSH2_KNOWNHOST_TYPE_PLAIN|
LIBSSH2_KNOWNHOST_KEYENC_RAW,
&host);
#else
/* 1.2.5 or older */
int check = libssh2_knownhost_check(nh, hostname,
fingerprint, len,
LIBSSH2_KNOWNHOST_TYPE_PLAIN|
LIBSSH2_KNOWNHOST_KEYENC_RAW,
&host);
#endif
fprintf(stderr, "Host check: %d, key: %s\n", check,
(check <= LIBSSH2_KNOWNHOST_CHECK_MISMATCH)?
host->key:"<none>");
/*****
* At this point, we could verify that 'check' tells us the key is
* fine or bail out.
*****/
}
else {
/* eeek, do cleanup here */
return 3;
}
libssh2_knownhost_free(nh);
if ( strlen(password) != 0 ) {
/* We could authenticate via password */
while ((rc = libssh2_userauth_password(session, username, password)) ==
LIBSSH2_ERROR_EAGAIN);
if (rc) {
fprintf(stderr, "Authentication by password failed.\n");
goto shutdown;
}
}
else {
/* Or by public key */
while ((rc = libssh2_userauth_publickey_fromfile(session, username,
"/home/user/"
".ssh/id_rsa.pub",
"/home/user/"
".ssh/id_rsa",
password)) ==
LIBSSH2_ERROR_EAGAIN);
if (rc) {
fprintf(stderr, "\tAuthentication by public key failed\n");
goto shutdown;
}
}
#if 1
//libssh2_trace(session, ~0 );
#endif
/* Exec non-blocking on the remove host */
while( (channel = libssh2_channel_open_session(session)) == NULL &&
libssh2_session_last_error(session,NULL,NULL,0) ==
LIBSSH2_ERROR_EAGAIN )
{
waitsocket(sock, session);
}
if( channel == NULL )
{
fprintf(stderr,"Error\n");
exit( 1 );
}
while( (rc = libssh2_channel_exec(channel, commandline)) ==
LIBSSH2_ERROR_EAGAIN )
{
waitsocket(sock, session);
}
if( rc != 0 )
{
fprintf(stderr,"Error\n");
exit( 1 );
}
for( ;; )
{
/* loop until we block */
int rc;
do
{
char buffer[0x4000];
rc = libssh2_channel_read( channel, buffer, sizeof(buffer) );
if( rc > 0 )
{
int i;
char *enable = "check-password\n";
int ret;
bytecount += rc;
fprintf(stderr, "We read [%d] bytes:\n", bytecount);
fputc('[', stderr);
for( i=0; i < rc; ++i )
fputc( buffer[i], stderr);
fputc(']', stderr);
if ( strstr(buffer, "Password:") != NULL ){
fprintf(stderr, "Sending the password now\n");
while((ret = libssh2_channel_write(channel, enable, strlen(enable))) == LIBSSH2_ERROR_EAGAIN) {
printf("ERROR_EAGAIN - sending password again\n");
}
fprintf(stderr, "Wrote [%d] bytes: \n", ret);
flag = 1;
continue;
}
if (!flag){ // start
char *cmd = "show clock\n";
int ret;
fprintf(stderr, "THIS is Fetching show clock command now\n");
while((ret = libssh2_channel_write(channel, cmd, strlen(cmd))) == LIBSSH2_ERROR_EAGAIN) {
printf("ERROR_EAGAIN - sending show clock again\n");
}
flag = 1;
} // end
}
else {
if(rc != LIBSSH2_ERROR_EAGAIN)
fprintf(stderr, "libssh2_channel_read returned [%d]:\n ", rc);
}
}
while( rc > 0 );
/* this is due to blocking that would occur otherwise so we loop on
this condition */
if( rc == LIBSSH2_ERROR_EAGAIN )
{
int check;
check = waitsocket(sock, session);
}
else
break;
}
exitcode = 127;
while( (rc = libssh2_channel_close(channel)) == LIBSSH2_ERROR_EAGAIN )
waitsocket(sock, session);
if( rc == 0 )
{
exitcode = libssh2_channel_get_exit_status( channel );
libssh2_channel_get_exit_signal(channel, &exitsignal,
NULL, NULL, NULL, NULL, NULL);
}
if (exitsignal)
fprintf(stderr, "\nGot signal: %s\n", exitsignal);
else
fprintf(stderr, "\nEXIT: %d bytecount: %d\n", exitcode, bytecount);
libssh2_channel_free(channel);
channel = NULL;
shutdown:
libssh2_session_disconnect(session,
"Normal Shutdown, Thank you for playing");
libssh2_session_free(session);
#ifdef WIN32
closesocket(sock);
#else
close(sock);
#endif
fprintf(stderr, "all done\n");
libssh2_exit();
return 0;
}
Any thoughts?
I would like to obtain a behavior similar to this:
Server run
Client run
Client type a command like "help" or other
Server responds appropriately
go to 3
The problem is that when my function excCommand("help") run just a little text is received and printed.
My text file is this:
COMMAND HELP:
help - Display help
quit - Shutdown client
only COMMAND HELP is printed.
Another problem is that when i type a command nothing is printed and after 2 command client exit.
This is the piece in particular:
while (quit)
{
getLine("client> ", command, 10);
if (strcmp(command, "quit") == 0)
quit = 0;
else
excCommand(command);
}
This is the server:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "common.h"
int main(int argc, char *argv[])
{
if (argc != 2)
ErrorWithUserMessage("Parameter(s)", "<Server Port>");
char *service = argv[1];
int servSock = SetupTCPServerSocket(service);
if (servSock < 0)
ErrorWithUserMessage("SetupTCPServerSocket() failed: ", "unable to establish");
unsigned int childProcessCount = 0;
while (1)
{
int clntSock = AcceptTCPConnection(servSock);
pid_t processID = fork();
if (processID < 0)
ErrorWithSystemMessage("fork() failed");
else if (processID == 0)
{
close(servSock);
HandleTCPClient(clntSock);
exit(EXIT_SUCCESS);
}
printf("with child process: %d\n", processID);
close(clntSock);
childProcessCount++;
//clean up zombies
while (childProcessCount)
{
processID = waitpid((pid_t) - 1, NULL, WNOHANG);
if (processID < 0)
ErrorWithSystemMessage("waitpid() failed");
else if (processID == 0)
break;
else
childProcessCount--;
}
}
}
Handler:
void HandleTCPClient(int clntSock)
{
char buffer[BUFSIZE];
ssize_t numBytesRcvd = recv(clntSock, buffer, BUFSIZE, 0);
buffer[numBytesRcvd] = '\0';
if (numBytesRcvd < 0)
ErrorWithSystemMessage("recv() failed");
if (strcmp(buffer, "help") == 0)
{
FILE *fp = fopen("help.txt", "r");
if (fp)
{
char line[128];
while (fgets(line, sizeof(line), fp) != NULL)
{
if (send(clntSock, line, sizeof(line), 0) < 0)
ErrorWithSystemMessage("send() failed");
}
fclose(fp);
}
}
close(clntSock);
}
and this is my client:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "common.h"
int sock;
void getLine(char *message, char *buf, int maxLen)
{
printf("%s", message);
fgets(buf, maxLen, stdin);
buf[strlen(buf) - 1] = 0;
}
void excCommand(char *command)
{
if ( send(sock, command, strlen(command), 0) < 0)
ErrorWithSystemMessage("send() failed");
char replyMessage[BUFSIZE];
ssize_t numBytesRecv = 0;
do
{
numBytesRecv = recv(sock, replyMessage, BUFSIZE, 0);
if ( numBytesRecv < 0)
ErrorWithSystemMessage("recv() failed");
printf("%s\n", replyMessage);
memset(&replyMessage, 0, sizeof(replyMessage));
}
while (numBytesRecv > 0);
}
void PrintFile(const char *filename)
{
FILE *fp;
fp = fopen(filename, "r");
if (fp)
{
char line[128];
while (fgets(line, sizeof(line), fp) != NULL)
fputs(line, stdout);
fputs("\n", stdout);
fclose(fp);
}
}
int main(int argc, char *argv[])
{
int quit = 1;
char command[10];
if (argc < 2 || argc > 3)
{
ErrorWithUserMessage("Parameter(s)", "<Server Address> <Server Port>");
}
char *server = argv[1];
char *service = argv[2];
sock = SetupTCPClientSocket(server, service);
if (sock < 0)
ErrorWithUserMessage("SetupTCPClientSocket() failed: ", "unable to connect");
printf("Connection established!\n\n");
PrintFile("menu.txt");
excCommand("help");
while (quit)
{
getLine("client> ", command, 10);
if (strcmp(command, "quit") == 0)
quit = 0;
else
excCommand(command);
}
fputs("\n", stdout);
close(sock);
exit(EXIT_SUCCESS);
}
sorry for being so long-winded
The recv() and send() functions do not guarantee to send/recv all data (see man recv, man send)
You need to implement your own send_all() and recv_all(), something like
bool send_all(int socket, void *buffer, size_t length)
{
char *ptr = (char*) buffer;
while (length > 0)
{
int i = send(socket, ptr, length);
if (i < 1) return false;
ptr += i;
length -= i;
}
return true;
}
The following guide may help you Beej's Guide to Network Programming
Usual problems.
void excCommand(char *command)
{
if ( send(sock, command, strlen(command), 0) < 0)
ErrorWithSystemMessage("send() failed");
char replyMessage[BUFSIZE];
ssize_t numBytesRecv = 0;
do
{
numBytesRecv = recv(sock, replyMessage, BUFSIZE, 0);
if ( numBytesRecv < 0)
ErrorWithSystemMessage("recv() failed");
printf("%s\n", replyMessage);
Invalid. numBytesRecv could have been zero, in which case there is no message at all, otherwise at this point must be positive, as you've already tested for negative, and it indicates the actual length of the message, which isn't necessarily null-terminated. Change to:
if (numBytesRecv == 0)
break;
printf("%.*s\n", numBytesRecv, replyMessage);
and then:
memset(&replyMessage, 0, sizeof(replyMessage));
Pointless. Remove.
}
while (numBytesRecv > 0);
At this point you should check for numBytesRecv < 0 and call perror() or one of its friends.
I choose to send before each send() if i have to continue or not.
so i first have 3 define
#define BUFFSIZE 1024
#define CONT "CONT"
#define DONE "DONE"
Then to send my data
int send_to_socket(int sock, char *msg)
{
size_t len;
int ret[2];
len = strlen(msg);
ret[0] = send(sock, (len <= BUFFSIZE) ? DONE : CONT, 4, 0);
ret[1] = send(sock, msg, BUFFSIZE, 0);
if (ret[0] <= 0 || ret[1] <= 0)
{
perror("send_to_socket");
return (-1);
}
if (len > BUFFSIZE)
return (send_to_socket(sock, msg + BUFFSIZE));
return (1);
}
And to receive it :
char *recv_from_socket(int cs)
{
char state[5];
char buff[BUFFSIZE+1];
char *msg;
int ret[2];
msg = NULL;
while (42)
{
bzero(state, 5);
bzero(buff, BUFFSIZE+1);
ret[0] = recv(cs, state, 4, 0);
ret[1] = recv(cs, buff, BUFFSIZE, 0);
if (ret[0] <= 0 || ret[1] <= 0)
{
perror("recv_from_socket");
return (NULL);
}
// strfljoin() is selfmade
// join the string and free the left argument to prevent memory leaks.
// return fresh new string
msg = (msg) ? ft_strfljoin(msg, buff) : strdup(buff);
if (strncmp(state, DONE, 4) == 0)
break ;
i++;
}
return (msg);
}