I have a client/server setup communicating over tcp sockets.
Functional wise works very nice except i got an errno 9 , randomly but quite often, when reading the socket on the server side.
According to docs and discussions i could find, errno is raised when read operation is done for a socket which is locally closed.
I'm pretty sure i'm not closing the socket in the reading loop on server side. The socket is closed only by client after message is sent.
Here is the reading loop on server side
void *client_listener_thread(void *args)
{
struct th_params *param = (void *) args;
int sockfd, n, client_pos;
message_t messageR;
char logbuf[256], buf[256];
sockfd = param->socket;
while(1)
{
n = read(sockfd, &messageR, sizeof(message_t));
if (n < 0)
{
sprintf(logbuf, "cmd_thread: ERROR reading from socket errno=%d sock=%d thread=%x", errno, sockfd, (uint32_t)(param->client_listener));
logwts(logbuf);
break;
}
else if(n == 0)
{
sprintf(logbuf, "cmd_thread: socket closed by remote peer 2 %x", (uint32_t)(param->client_listener));
logwts(logbuf);
break;
}
else
{
inet_ntop(AF_INET, &messageR.header.sender_ip, buf, 255);
sprintf(logbuf, "cmd_thread:URC Message: ID = %d sender = %s\n", messageR.header.messageID, buf);
logwts(logbuf);
// process message
switch(messageR.header.messageID)
{
case IDENT_ACK:
...
sprintf(logbuf, "New client registered on socket %d %d / %s:%d", sockfd, client_pos, buf, messageR.header.sender_port);
logwts(logbuf);
break;
...
default:
sprintf(logbuf, "cmd_thread:unprocessed message %d", messageR.header.messageID);
logwts(logbuf);
break;
}
}
}
if(sockfd)
close(sockfd);
pthread_exit(NULL);
}
On client side, it is just open the socket, send the message, close the socket.
When running, on server side, i got "New client registered ..." message for the first read and on the second read i got either the expected "socket closed by remote peer..." message or unexpected "ERROR reading from socket.." with errno = 9
As i said functional has no problem, no message lost, the message is received and processed. I could ignore the error but i'm tring to understand it first.
Thx to strace could find the culprit.
A race condition between threads. One thread was closing the socket while the other thread was still using it. A mutex did the job.
Completely true #Sinic's comment
"... that the descriptor (in this case 5) is getting closed elsewhere..."
Related
I am creating two apps (client and server) in c to communicate via IPC message queues.
The server have to operate for many clients using only one queue. Clients' ids have to be provided for the server as the Command Line arguments, as well as the id for each client. Clients are recognized by a mesg_type.
I have a problem to receive messages from all clients - I can receive the message a few times from client A, then a few times from client B, a few times from client A and so on. I think that the problem is in the following part of the code:
int status;
key_t key;
int msgid;
for(int i=0;i<argc-1;i++){
clients_ids[i]=atoi(argv[i+1]);
}
key = ftok(".", 50);
msgid = msgget(key, 0666 | IPC_CREAT);
if (msgid == -1) {
fprintf(stderr, "msgget failed: %d\n", errno);
exit(EXIT_FAILURE);
}
do{
for (int k=0;k<=argc-2;k++){
status = msgrcv(msgid, &message, sizeof(message), clients_ids[k], 0);
if(status != -1){
type = message.mesg_type;
strcpy(mesg, message.mesg_text);
}
}
printf("Message received (from id: %d): %s ",type, mesg);
Can you please advise me what should I change?
move the call to printf() to inside the for (int k=0;k<=argc-2;k++){ loop. then all messages will be displayed.
I am new to socket programming and i currently facing a problem send/receiving information between my client and server. Currently i am sending "TRN" to my client which is received and handled on my client side but then my program gets stuck. The console prints the [DEBUG] Sent start message. but then it never makes it to the MADE IT HERE 1 message. On the client side when it receives the "TRN" it is handled and an int is sent back to the server which i want to be received however it doesn't make it that far. This is my code breakdown:
/* Send the start message. */
write_client_msg(cli_sockfd, "TRN");
printf("\n[DEBUG] Sent start message.\n");
int place_reveal = 0;
printf("\nMADE IT HERE 1");
place_reveal = recv_int(cli_sockfd);
printf("\nThis is the users results: %d", place_reveal);
/* Writes a message to a client socket. */
void write_client_msg(int cli_sockfd, char * msg)
{
int n = write(cli_sockfd, msg, strlen(msg));
if (n < 0)
error("ERROR writing msg to client socket");
}
/* Reads an int from a client socket. */
int recv_int(int cli_sockfd)
{
printf(" MADE IT HERE 2");
int msg = 0;
int n = read(cli_sockfd, &msg, sizeof(int));
if (n < 0 || n != sizeof(int)) /* Not what we were expecting. Client likely disconnected. */
return -1;
printf("[DEBUG] Received int: %d\n", msg);
return msg;
}
What i have found although is that when i hit Control C to exit the program the console output is this:
^C
MADE IT HERE 1 MADE IT HERE 2
OUCH you hit Ctrl-C?!
Goodbye!
I am trying to create two programs a client and server, where the client opens a socket connection and then writes data to the server who on accepting the connection spawns a new threads and then detaches it, to handle the rest of the read/writes. The problem is that when I make multiple writes then reads from the client the reads aren't getting the correct data, however on the server side it prints that it sent the correct data.
This is what my code looks like to generate new threads, and how I handle those threads.
while(1){
listen(sockfd,5);
// determine the size of a clientAddressInfo struct
clilen = sizeof(clientAddressInfo);
int *newsockfd = malloc(sizeof(int));
// block until a client connects, when it does, create a client socket
*newsockfd = accept(sockfd, (struct sockaddr *) &clientAddressInfo, &clilen);
// if the connection blew up for some reason, complain and exit
if (*newsockfd < 0){
error("ERROR on accept");
}
connection_args *args = malloc(sizeof(connection_args));
args->file_descrp = newsockfd;
pthread_t tid;
pthread_create(&tid,NULL, handle_connect, args);
}
void * handle_connect(void* args){
connection_args* connect_arg = (connection_args*)args;
pthread_detach(pthread_self());
int n = -1;
char buffer[256];
bzero(buffer,256);
//while not close;
while(1){
// try to read from the client socket
n = read(*connect_arg->file_descrp,buffer,255);
printf("input: %s\n", buffer);
// if the read from the client blew up, complain and exit
if (n < 0){
error("ERROR reading from socket");
}
int fd;
if(strcmp("open",buffer) == 0){
fd = open("file.txt",0);
bzero(buffer,256);
sprintf(buffer,"%d",fd);
}else if(strcmp("read",buffer) == 0){
char *read_buffer = malloc(sizeof(char)*256);
bzero(read_buffer,256);
fd = read(get_filedescrp(),read_buffer,30);
bzero(buffer,256);
sprintf(buffer,"%s,%d",read_buffer,fd);
}else if(strcmp("close",buffer) == 0){
break;
}
printf("buffer_send: %s\n",buffer);
// try to write to the client socket
n = write(*connect_arg->file_descrp,buffer,sizeof(buffer));
// if the write to the client below up, complain and exit
if (n < 0){
printf("here!!\n");
error("ERROR writing to socket");
}
bzero(buffer,256);
}
printf("Left thread\n");
return NULL;
}
You cannot implement client server communication over TCP/IP without some sort of protocol. The data written by the sender can be sliced and diced along the way and come in different chunk lengths to the reader side. You must have a way to tell if you have received a full frame before trying to interpret the data.
For example, you can use a very simple line based protocol: read data upto and including the '\n' byte. Reading one byte at a time into a lien buffer is somewhat inefficient but easy to implement.
A socket read call may or may not return the entire data sent by client in a single call.
Each read call returns number of bytes are that are read in that call. So the application should call read in a loop till expected number of bytes are read.
I am trying to create a server and client program that sends a string from client to server where the server executes that string and sends the output back to the client. I am doing this in linux and I am very confused why my program isnt working the least bit. Here is the code.
**Client**
int main()
{
//Code to use unix socket here
if (connect(s, (struct sockaddr *)&remote, len) == -1) {
perror("connect");
exit(1);
}
printf("Connected.\n");
while(printf("> "), fgets(str, MAX, stdin), !feof(stdin)) {
if (send(s, str, strlen(str), 0) == -1) {
perror("send");
exit(1);
}
}
done=0;
do {
if(t=recv(s, str, MAX, 0)<0)
perror("recv failed at client side!\n");
str[t] = '\0';
if(strcmp(str, "ENDOFTRANS")==0)
{
printf("\nRead ENDOFTRANS. Breaking loop.\n");
done=1;
}
printf("Server > %s", str);
} while(!done);
}
And then the server code is:
**Server**
#define MAX 1000
int main(void)
{
//Unix socket code
//This process is now a daemon.
daemon();
//Listens for client connections, up to 5 clients can queue up at the same time.
if (listen(s, 5) == -1) {
perror("listen");
exit(1);
}
for(;;) {
int done, n, status;
printf("Waiting for a connection...\n");
t = sizeof(remote);
if ((newsock= accept(s, (struct sockaddr *)&remote, &t)) == -1) {
perror("accept");
exit(1);
}
printf("Connected.\n");
done = 0;
do {
switch(fork())
{
case -1: //ERROR
perror("Could not fork.\n");
break;
case 0: //CHILD
//Accept string from client.
//Edit: Why am I getting an error here? says: Invalid argument.
if(n = recv(newsock, str, MAX, 0)) {
perror("Recv error at server side.\n");
exit(1);
}
str[n]='\0';
if (n <= 0) {
if (n < 0)
perror("recv");
done = 1;
}
printf("String=>%s<",str);
//Redirect socket to STDOUT & STDERR.
test = close(WRITE); assert(test==0);
test = dup(newsock); assert(test==WRITE);
test = close(ERROR); assert(test==0);
test = dup(newsock); assert(test==ERROR);
if (!done)
{
if (str==something)
{
//execute command
}
else {
//Fork and execvp the command
}
//Sends End of Transaction character.
ENDTHETRANS();
exit(0);
}
break;
default: //PARENT
//Parent keeps accepting further clients.
wait(&status);
if ((newsock= accept(s, (struct sockaddr *)&remote, &t)) == -1) {
perror("accept");
exit(1);
}
printf("Connected.\n");
done=1;
break;
}
} while (!done);
}
close(s);
}
Im relatively new to programming in general and from my understanding the client code is good except that when it recieves the text back from the server it only recieves the text in small bits (2 rows at a time). I have to keep pressing enter on client promt to get the rest of the input. I have tried so many things that by this point I dont even know what I am doing wrong anymore.
Firstly, in the server code, after it recieves the string from the client I have a printf("String=>%s<",str); that outputs the string. However when the server prints the output as String=>ls -l the < key at the end gets eaten up somehow. It shouldnt be doing that right?
Any help much appreciated. Please bare in mind that I am a beginner and have only used pipes as inter process communcation before. Now I wanna make my first unix socket program.
Thanks in advance.
The usual problem in cases such as this is not realizing that SOCK_STREAM sockets don't preserve message boundaries. So data sent with a send call might be split up and received in multiple recvs, or it might be coalesced and multiple sends end up in a single recv. Most importantly, when a kernel send buffer fills up, a send call might write partial data (sending only some of the requested data) and return a short return value. You need to test for this and resend the rest of the data.
Another problem that often shows up is issues with line endings (particularly when talking between linux and windows). There may be extra carriage return characters (\r) in the either the client or server that confuse the other side. These tend to result in apparently missing or truncated output when printed.
edit
The line
if(t=recv(s, str, MAX, 0)<0)
is equivalent to
if(t = (recv(s, str, MAX, 0)<0))
that is, it sets t to 0 or 1 depending on whether there was an error or not. As with most errors of this type, turning on warnings will give you some indication about it.
I basically have a server set up and I'm accepting new clients(UNIX) and i'm using select() command to wait for activity on file descriptor but I'm not sure how to write from the clients side and then read it on the servers side
FD_ZERO(&readfds);
FD_SET(server_sockfd, &readfds);
FD_SET(STDIN_FILENO, &readfds);
while (1) {
testfds = readfds;
select(4 + MAXCLIENTS, &testfds, NULL, NULL, NULL);
for (fd = 0; fd < 4 + MAX_CLIENTS; fd++) {
if (FD_ISSET(fd, &testfds)) {
if (fd == server_sockfd) { /* new connection request */
client_sockfd = accept(server_sockfd, NULL, NULL);
if (num_clients < MAXCLIENTS) {
FD_SET(client_sockfd, &readfds);
num_clients++;
} else {
sprintf(message, "XSorry, too many clients. Try again later.\n");
write(client_sockfd, message, strlen(message));
close(client_sockfd);
}
} else if (fd == STDIN_FILENO) {
fgets(kb_message, BUFSIZ + 1, stdin);
if (strcmp(kb_message, "quit\n") == 0) {
sprintf(message, "XServer is shutting down.\n");
for (fd2 = 4; fd2 < 4 + MAX_CLIENTS; fd2++) {
if (FD_ISSET(fd2, &readfds)) {
write(fd2, message, strlen(message));
close(fd2);
}
}
close(server_sockfd);
exit(EXIT_SUCCESS);
} else {
sprintf(message, "M%s", kb_message);
for (fd2 = 4; fd2 < 4 + MAX_CLIENTS; fd2++)
if (FD_ISSET(fd2, &readfds))
write(fd2, message, strlen(message));
}
} else { /* client leaving */
close(fd);
FD_CLR(fd, &readfds);
num_clients--;
}
}
}
}
How would I handle write request from clients and then write back to them, would it be under "else" and how can I check if client is exiting or writing.
Thanks
The most common mistake with select(2) is not re-initializing the descriptor sets, since second, third, and forth arguments are input-output parameters.
Setup an fd_set, for reading before the outer loop, add listening socket descriptor to it, enter the loop, make a copy of the this fd_set and give the copy to select(2). When new connection arrives, add its descriptor to the original fd_set. Same for closed socket (error or EOF on read(2)) - remove the descriptor from the original fd_set.
Hope this helps.
You are correct in thinking you need the read code in your 'else' block. If a file descriptor triggers and it isn't stdin or the 'connect' descriptor, then it is one of your clients attempting to send you data. When one of those file descriptors is triggered in the select, you need to call 'read' on that descriptor to read the data into the buffer. The read command will return you the number of bytes read. If this is a positive number, then it indicates the client has sent you data. If it is zero, then that indicates that the client has ended the TCP connection to your server.
The else block will look something like:
else
{
//Existing connection has data for us to read
if((nBytes = read(fd, buffer, MAXBUFFER)) <= 0)
{
if(nBytes == 0)
{
//Actually, its sending us zero bytes, connection closed
printf("Socket %d hung up\n", fd;
}
else
printf ("Read Error"\n)
}
Also, Follow Nikolai N Fetissov's advice above and make sure that when client's connect you store their fd in a permanent fd_set structure, as the one you are using is being modified by the select call.
Your problem might be that you have a variable called read. It's going to mask one of hte functions you need to use - the read() system call to get data out of the socket. The client puts it in with write(). You might also want to check the return value from select(), which will tell you how many of the file descriptors are ready for reading. Then you can check which ones using FD_ISSET(). It looks like you're doing that part already (except you seem to be checking the wrong variable?)... just call read() on that file descriptor to get out the data the client wrote.
else
{
bzero(buf,100);
n=read(i,buf,100); // Read the client message
buf[n]='\0';
if(n==0) // Check the client is closed or not
{
printf("%d is closed\n",i);
close(i);
FD_CLR(i, &master);
if(i==fdmax)
fdmax--;
}
else
{
n=strlen(buf);
write(1,buf,n);
fflush(stdout);
write(1,"Enter the message\n",18);
bzero(buf,100);
read(0,buf,100);
buf[n]='\0';
write(i,buf,n);
fflush(stdout);
}
}
Notes:
After accept the client, Add the client in the fd set.
Then read the message from the client
If the message is equal to 0, then the client is closed.
If you want to send the message to the client, using the client fd, you can send to the client