problem with client server unix domain stream sockets (servered is able to be accessed by multiple clients)
Posted By: Anonymous
as mention above i am trying to make a server and client program.
the client should send the length of the sequence of int and the sequence to the server. from the server it will receive the average if it is greater than 10 and a confirmation message else it will receive a failure message. The server creates threads and in the function executed by the threads it does what the client wants plus it keeps count of the successful as well as the total sequences which he receives . I have tried what follows and after i insert the array elements it does nothing and i am not sure why(as you may have guessed i am new to sockets)
server code
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define SOCK_PATH "socket"
#define SUCCESS "Sequence ok"
#define FAILURE "Check failed"
int succcounter = 0;
int totalcounter = 0;
void *average_function (void *arg)
{
int so = (int) arg;
int done, n;
int sum=0;
int *num,m;
float average;
char str[100];
done = 1 ;
do {
n = recv(so, str, 4, 0);
m = (int)str;
num = (int *)malloc(m);
for(int i=0; i<m; i++){
n = recv(so, str, sizeof(num[m]), 0);
num[i] = (int) str[i];
}
for (int i=0; i<m; i++){
sum += num[i];
}
average = sum/m;
if (average > 10){
send(so,(char *)&average,sizeof(average),0);
send(so, SUCCESS, strlen(SUCCESS), 0);
succcounter++;
totalcounter++;
}
else{
average = 0;
send(so,(char *)&average,sizeof(average),0);
send(so,FAILURE,strlen(FAILURE),0);
totalcounter++;
}
if (n==1) done = 1;
} while (!done);
close(so);
pthread_exit(NULL);
}
int main(void)
{
int s, s2, i, t, len;
struct sockaddr_un local, remote;
pthread_t thread[50];
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
local.sun_family = AF_UNIX;
strcpy(local.sun_path, SOCK_PATH);
unlink(local.sun_path);
len = strlen(local.sun_path) + sizeof(local.sun_family);
if (bind(s, (struct sockaddr *)&local, len) == -1) {
perror("bind");
exit(1);
}
if (listen(s, 5) == -1) {
perror("listen");
exit(1);
}
i=0;
for(;;) {
t = sizeof(remote);
if ((s2 = accept(s, (struct sockaddr *)&remote, &t)) == -1) {
perror("accept");
exit(1);
}
pthread_create(&(thread[i++]), NULL, average_function, (void *)s2);
}
return 0;
}
client code
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#define SOCKET_PATH "socket"
int main(void)
{
int s,len,t,N = 0;
int *arr;
char str[100],ch;
float avg=0;
struct sockaddr_un remote;
if((s = socket(AF_UNIX,SOCK_STREAM,0)) == -1) { //check if socket was created
perror("socket");
exit(1);
}
remote.sun_family = AF_UNIX; //passing the values to the struct
strcpy(remote.sun_path,SOCKET_PATH); //same
len = strlen(remote.sun_path) + sizeof(remote.sun_family);
if(connect(s,(struct sockaddr *)&remote,len) == -1){ //connetct and check
perror("connect");
exit(1);
}
do{
printf("Give array dimension: ");
scanf("%d",&N);
arr = (int *)malloc(N); //dynamicly allocating memory for the array
for(int i=0; i<N; i++){
printf("Give array element: ");
scanf("%d",&arr[i]); //populating the array
}
send(s,(char *)&N,sizeof(int),0); //send(file descriptor,void *buff,size,flags) sending N
for(int i=0; i<N; i++){
send(s,(char *)&arr[i],sizeof(arr),0); //sending array elements
}
t = recv(s,str,100,0);
str[t] = '';
printf("Server sent this message %sn",str); // receive confirmation or failure
recv(s,(char *)&avg,sizeof(float),0); //receiving average and printing it
if(avg != 0){
printf("Server also sent the average which is %.2f",avg);
}
printf("Do you want to continue?(Y/N)");
ch = getchar();
free(arr);
}
while (ch != 'N'|| ch != 'n');
close(s);
return 0;
}
Solution
Server sends a variable length string (confirmation) but we need a fixed length message because client can’t know beforehand the length to read. Better to just send an int
that is 0=fail, 1=success.
Server sends average
first but client reads success/fail message first (i.e. a mismatch in the order of send/recv
for these.
Method to terminate client connection in server is broken. Better to have client send an explicit stop message (and server has to check connection).
The server doesn’t do pthread_join
so it can overrun the thread ID array. I changed that to a linked list of a [new] per-task struct
. But, I couldn’t get the join code to work. Still, it should give you some idea of how to control threads in the general case, even though it’s broken somewhat.
There are many other issues, too numerous to mention individually, but, the code below is refactored and [most of] the bugs are annotated. It’s still a bit fragile in places (e.g. server can get "Broken pipe") but the basic communication now works.
I’ve used cpp
conditionals to denote old vs. new code:
#if 0
// old code
#else
// new code
#endif
#if 1
// new code
#endif
server code:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#if 1
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/stat.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#if 0
#define SOCKET_PATH "socket"
#else
#define SOCKET_PATH "funny_socket"
#endif
#define STRMAX 100
#define SUCCESS "Sequence ok"
#define FAILURE "Check failed"
int succcounter = 0;
int totalcounter = 0;
#define dbgprt(_fmt...)
printf(_fmt)
#if 1
typedef struct tsk {
int tsk_sock;
pthread_t tsk_tid;
void *tsk_rtn;
struct tsk *tsk_next;
} tsk_t;
tsk_t *tsklist = NULL;
#endif
void *
average_function(void *arg)
{
#if 0
int so = (int) arg;
#else
tsk_t *tsk = arg;
int so = tsk->tsk_sock;
#endif
int done, n;
int sum = 0;
int *num, m;
float average;
// NOTE/FRAGILE: this must match str in client
#if 0
char str[100];
#else
char str[STRMAX];
#endif
long code = 0;
#if 0
done = 1;
#else
done = 0;
#endif
do {
// get count
#if 0
n = recv(so, str, 4, 0);
#else
n = recv(so, &m, sizeof(m), 0);
if (n < 0) {
perror("recv/m");
code = 1;
break;
}
// client wants a stop
if (m < 0)
break;
#endif
// allocate space for array
#if 0
m = (int) str;
num = (int *) malloc(m);
#else
num = malloc(sizeof (*num) * m);
#endif
for (int i = 0; i < m; i++) {
#if 0
n = recv(so, str, sizeof(num[m]), 0);
num[i] = (int) str[i];
#else
n = recv(so, &num[i], sizeof(*num), 0);
#endif
}
// compute average
// NOTE/BUG: must reset sum to 0 for subsequent iterations
#if 1
sum = 0;
#endif
for (int i = 0; i < m; i++) {
sum += num[i];
}
average = sum / m;
// NOTE/BUG: these are sent out-of-order relative to client
// NOTE/BUG: client reads 100 for message but we do a short send based on
// strlen
#if 0
if (average > 10) {
send(so, &average, sizeof(average), 0);
send(so, SUCCESS, strlen(SUCCESS), 0);
succcounter++;
totalcounter++;
}
else {
average = 0;
send(so, &average, sizeof(average), 0);
send(so, FAILURE, strlen(FAILURE), 0);
totalcounter++;
}
#else
if (average > 10) {
strcpy(str,SUCCESS);
succcounter++;
}
else {
strcpy(str,FAILURE);
average = 0;
}
send(so, str, sizeof(str), 0);
send(so, &average, sizeof(average), 0);
totalcounter++;
#endif
#if 0
if (n == 1)
done = 1;
#endif
#if 1
// free space for array
free(num);
#endif
} while (!done);
close(so);
dbgprt("average_function: exit with %dn",code);
#if 0
pthread_exit(NULL);
#else
return (void *) code;
#endif
}
int
main(void)
{
int s, s2, i, t, len;
struct sockaddr_un local, remote;
pthread_t thread[50];
#if 1
int err;
#endif
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
local.sun_family = AF_UNIX;
strcpy(local.sun_path, SOCKET_PATH);
unlink(local.sun_path);
// NOTE/BUG: must enable for reuse to prevent bind failure
#if 0
int enable = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
#endif
len = strlen(local.sun_path) + sizeof(local.sun_family);
if (bind(s, (struct sockaddr *) &local, len) == -1) {
perror("bind");
exit(1);
}
if (listen(s, 5) == -1) {
perror("listen");
exit(1);
}
i = 0;
for (;;) {
t = sizeof(remote);
if ((s2 = accept(s, (struct sockaddr *) &remote, &t)) == -1) {
perror("accept");
exit(1);
}
// NOTE/BUG: this will eventually overrun thread -- we need to do pthread_join
// and clean up
#if 0
pthread_create(&(thread[i++]), NULL, average_function, (void *) s2);
#else
tsk_t *tsk;
tsk_t *prev;
tsk_t *next;
tsk = malloc(sizeof(*tsk));
tsk->tsk_sock = s2;
pthread_create(&tsk->tsk_tid, NULL, average_function, tsk);
dbgprt("created: %dn",tsk->tsk_tid);
tsk->tsk_next = tsklist;
tsklist = tsk;
// FIXME/CAE -- this is broken
#if 1
continue;
#endif
prev = NULL;
for (tsk = tsklist; tsk != NULL; tsk = next) {
next = tsk->tsk_next;
// NOTE: this is non-standard but other ways to reclaim are more
// complicated
err = pthread_tryjoin_np(tsk->tsk_tid,&tsk->tsk_rtn);
if (err <= 0) {
prev = tsk;
continue;
}
dbgprt("joined: %dn",tsk->tsk_tid);
if (prev != NULL)
prev->tsk_next = next;
else
tsklist = next;
free(tsk);
}
#endif
}
return 0;
}
client code:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#if 1
#include <unistd.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
// NOTE: change name to make a grep of netstat easy to find
#if 0
#define SOCKET_PATH "socket"
#else
#define SOCKET_PATH "funny_socket"
#endif
#if 0
#define SOCKET_PATH "socket"
#else
#define SOCKET_PATH "funny_socket"
#endif
#define STRMAX 100
int
getprompt(const char *prompt,char *str,int len)
{
char buf[1000];
if (str == NULL) {
str = buf;
len = sizeof(buf);
}
printf("%s ",prompt);
fflush(stdout);
if (fgets(str,len,stdin) == NULL)
exit(1);
str[strcspn(str,"n")] = 0;
int val = atoi(str);
return val;
}
int
main(void)
{
int s, len, t, N = 0;
int *arr;
// NOTE/FRAGILE: this must match str in server
#if 0
char str[100];
#else
char str[STRMAX];
#endif
char ch;
float avg = 0;
struct sockaddr_un remote;
// check if socket was created
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
// passing the values to the struct
remote.sun_family = AF_UNIX;
strcpy(remote.sun_path, SOCKET_PATH);
// connect and check
len = strlen(remote.sun_path) + sizeof(remote.sun_family);
if (connect(s, (struct sockaddr *) &remote, len) == -1) {
perror("connect");
exit(1);
}
setbuf(stdout,NULL);
#if 0
do {
#else
while (1) {
#endif
#if 0
printf("Give array dimension: ");
scanf("%d", &N);
#else
N = getprompt("Give array dimension:",NULL,0);
#endif
// NOTE/BUG: does not allocate sufficient space
// dynamicly allocating memory for the array
#if 0
arr = (int *) malloc(N);
#else
arr = malloc(sizeof(*arr) * N);
#endif
// populating the array
for (int i = 0; i < N; i++) {
#if 0
printf("Give array element: ");
scanf("%d", &arr[i]);
#else
arr[i] = getprompt("Give array element:",NULL,0);
#endif
}
// send(file descriptor,void *buff,size,flags) sending N
// NOTE/BETTER: no need to cast as send uses "void *"
#if 0
send(s, (char *) &N, sizeof(int), 0);
#else
send(s, &N, sizeof(N), 0);
#endif
// sending array elements
for (int i = 0; i < N; i++) {
#if 0
send(s, (char *) &arr[i], sizeof(arr), 0);
#else
send(s, &arr[i], sizeof(*arr), 0);
#endif
}
// NOTE/BETTER -- this is fragile if the length of str is ever changed above
#if 0
t = recv(s, str, 100, 0);
#else
t = recv(s, str, sizeof(str), 0);
if (t < 0) {
perror("recv/str");
exit(1);
}
#endif
str[t] = '';
// receive confirmation or failure
printf("Server sent this message %sn", str);
// NOTE/BETTER: use sizeof
// receiving average and printing it
#if 0
recv(s, (char *) &avg, sizeof(float), 0);
#else
t = recv(s, &avg, sizeof(avg), 0);
if (t < 0) {
perror("recv/avg");
exit(1);
}
#endif
if (avg != 0) {
printf("Server also sent the average which is %.2fn", avg);
}
free(arr);
#if 0
printf("Do you want to continue?(Y/N)");
ch = getchar();
} while ((ch != 'N') && (ch == 'n'));
#else
getprompt("Do you want to continue (Y/N)?",str,sizeof(str));
ch = str[0];
if (ch == 'n')
break;
if (ch == 'N')
break;
}
#endif
// send stop to server
#if 1
N = -1;
send(s, &N, sizeof(N), 0);
#endif
close(s);
return 0;
}
UPDATE:
I’ve come up with version of the server code that correctly cleans up after a thread has terminated along with a fix for the SIGFPE
:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#if 1
#include <fcntl.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <stdatomic.h>
#include <poll.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#define SOCKET_PATH "funny_socket"
#define STRMAX 100
#define MAXCONN 5
#define SUCCESS "Sequence ok"
#define FAILURE "Check failed"
int succcounter = 0;
int totalcounter = 0;
#define dbgprt(_fmt...)
printf(_fmt)
//#define USETID
typedef struct tsk {
int tsk_xid;
pid_t tsk_tid;
pthread_t tsk_pthr;
int tsk_busy;
int tsk_sock;
void *tsk_rtn;
int tsk_done;
} tsk_t;
#define TSKFORALL(_tsk)
_tsk = tsklist; _tsk < &tsklist[MAXCONN]; ++tsk
tsk_t tsklist[MAXCONN];
void *
average_function(void *arg)
{
tsk_t *tsk = arg;
int so = tsk->tsk_sock;
pid_t tid = syscall(SYS_gettid);
int n;
int sum = 0;
int m;
int *num = NULL;
float average;
char str[STRMAX];
long code = 0;
atomic_store(&tsk->tsk_tid,tid);
while (1) {
// get count
n = recv(so, &m, sizeof(m), 0);
if (n <= 0) {
if (n < 0)
perror("recv/m");
else
dbgprt("short read mn");
code = 1;
break;
}
// client wants a stop
if (m <= 0)
break;
// allocate space for array
num = malloc(sizeof (*num) * m);
for (int i = 0; i < m; i++) {
n = recv(so, &num[i], sizeof(*num), 0);
}
// compute average
sum = 0;
for (int i = 0; i < m; i++) {
sum += num[i];
}
average = sum / m;
if (average > 10) {
strcpy(str,SUCCESS);
succcounter++;
}
else {
strcpy(str,FAILURE);
average = 0;
}
send(so, str, sizeof(str), 0);
send(so, &average, sizeof(average), 0);
totalcounter++;
}
close(so);
// free space for array
if (num != NULL)
free(num);
dbgprt("average_function: exit with %ldn",code);
return (void *) code;
}
void
tskreapall(void)
{
int err;
tsk_t *tsk;
pid_t tid = 0;
int xid = 0;
for (TSKFORALL(tsk)) {
tsk->tsk_xid = ++xid;
if (! tsk->tsk_busy)
continue;
#ifdef USETID
pid_t pid = getpid();
tid = atomic_load(&tsk->tsk_tid);
err = syscall(SYS_tgkill,pid,tid,0);
if (err >= 0)
continue;
#else
err = pthread_kill(tsk->tsk_pthr,0);
if (err)
continue;
#endif
dbgprt("tskreapall: joining %d/%dn",tsk->tsk_xid,tid);
err = pthread_join(tsk->tsk_pthr,&tsk->tsk_rtn);
tsk->tsk_busy = 0;
}
}
tsk_t *
tskfind(void)
{
tsk_t *tsk;
tsk_t *rtn = NULL;
for (TSKFORALL(tsk)) {
if (! tsk->tsk_busy) {
rtn = tsk;
break;
}
}
if (rtn != NULL) {
atomic_store(&rtn->tsk_tid,0);
rtn->tsk_busy = 1;
}
return rtn;
}
int
main(void)
{
int s, s2, len;
struct sockaddr_un local, remote;
socklen_t t;
int err;
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
local.sun_family = AF_UNIX;
strcpy(local.sun_path, SOCKET_PATH);
unlink(local.sun_path);
len = strlen(local.sun_path) + sizeof(local.sun_family);
if (bind(s, (struct sockaddr *) &local, len) == -1) {
perror("bind");
exit(1);
}
if (listen(s, MAXCONN) == -1) {
perror("listen");
exit(1);
}
for (;;) {
struct pollfd polls[1];
struct pollfd *fd = &polls[0];
fd->fd = s;
fd->events = ~0;
err = poll(fd,1,100);
if (err < 0)
err = -errno;
tskreapall();
if (err != 1)
continue;
dbgprt("err=%d revents=%4.4Xn",err,fd->revents);
t = sizeof(remote);
s2 = accept(s, (struct sockaddr *) &remote, &t);
if (s2 == -1) {
perror("accept");
exit(1);
}
tsk_t *tsk;
pid_t tid = 0;
tsk = tskfind();
tsk->tsk_sock = s2;
pthread_create(&tsk->tsk_pthr, NULL, average_function, tsk);
// wait for thread to start
while (1) {
tid = atomic_load(&tsk->tsk_tid);
if (tid)
break;
}
dbgprt("created: %d/%dn",tsk->tsk_xid,tid);
}
return 0;
}
Answered By: Anonymous
Disclaimer: This content is shared under creative common license cc-by-sa 3.0. It is generated from StackExchange Website Network.