unix domain socket programming - c

I have a unix domain socket program, the client try to connect to the server and send a message, when the server accept the client and read the message,it will sleep for 5 seconds and send another message.During the 5 seconds if I use ctrl+c to kill the client,then the server will quit.How can I handle this situation?My program as follows:
client:
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <string.h>
#include <assert.h>
#include <stddef.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdint.h>
#define INFO_SERVER_PATH "/var/info_server_path"
int create_route_client()
{
int client_fd;
int addr_len;
struct sockaddr_un server_addr;
if ((client_fd = socket(AF_LOCAL, SOCK_STREAM, 0)) < 0) {
perror("create route info client socket");
return -1;
}
memset(&server_addr, 0, sizeof(struct sockaddr_un));
server_addr.sun_family = AF_LOCAL;
strcpy(server_addr.sun_path, INFO_SERVER_PATH);
addr_len = offsetof(struct sockaddr_un,sun_path) + strlen(server_addr.sun_path);
if (connect(client_fd, (struct sockaddr*)&server_addr, addr_len) < 0) {
perror("socket connect");
return -1;
}
return client_fd;
}
int main(int argc, char const *argv[])
{
char *sendline = "hello server";
char recvline[512];
int client_fd;
int nwrite;
int nread;
client_fd = create_route_client();
assert(client_fd > 0);
nwrite = write(client_fd, sendline, strlen(sendline));
if (nwrite < 0) {
perror("failed to send command to the info server");
close(client_fd);
return 1;
}
nread = read(client_fd, recvline, sizeof(recvline));
if (nread < 0) {
perror("failed to read route state");
close(client_fd);
return 1;
}
recvline[nread] = '\0';
printf("%s\n", recvline);
close(client_fd);
return 0;
}
server:
#include <unistd.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <string.h>
#include <assert.h>
#include <stddef.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdint.h>
#define INFO_SERVER_PATH "/var/info_server_path"
int create_command_server()
{
struct sockaddr_un server_addr;
size_t addr_len;
int server_fd;
if ((server_fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
perror("create socket");
return -1;
}
unlink(INFO_SERVER_PATH);
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sun_family = AF_UNIX;
strcpy(server_addr.sun_path, INFO_SERVER_PATH);
addr_len = offsetof(struct sockaddr_un, sun_path) + strlen(INFO_SERVER_PATH);
if (bind(server_fd, (struct sockaddr*)&server_addr, addr_len) < 0) {
perror("socket bind");
return -1;
}
if (listen(server_fd, 1) < 0) {
perror("socket listen");
return -1;
}
return server_fd;
}
int main(int argc, char const *argv[])
{
int info_server_fd = create_command_server();
char recvline[512];
char *sendline = "hello client";
int nread;
int nwrite;
while (1) {
int info_client_fd = accept(info_server_fd, NULL, NULL);
nread = read(info_client_fd, recvline, 512);
if (nread) {
int i;
for (i = 0; i < 5; i++) {
printf("i = %d\n", i);
sleep(1);
}
nwrite = write(info_client_fd , sendline, strlen(sendline));
printf("nwrite = %d\n", nwrite);
if (nwrite < 0)
perror("failed to send to client");
}
close(info_client_fd);
}
return 0;
}

Hard to tell exactly without compiling and running your code, but I'd guess you're getting a SIGPIPE signal due to writing to the connection that was closed when you killed the client. The default action for a process receiving SIGPIPE is to terminate the process.
You can block the SIGPIPE signal using sigprocmask(), or tell the kernel you want to block it, ignore it or register an asynchronous signal handler for it using sigaction(). Then, when you call write(), it will return -1 and errno will be set to EPIPE. See the man page for write() http://man7.org/linux/man-pages/man2/write.2.html.
See the man page for signals http://man7.org/linux/man-pages/man7/signal.7.html for more information on signals and how to handle them. But, be warned that handling signals should not be done using an asynchronous signal handler unless you are very very careful and know exactly what you are doing. This is the source of many bugs. It's safest (by far) to ignore them if you don't need them, or block them and use a synchronous signal handling approach, like sigwait() or a signal fd (Linux-specific). In your case, you don't need them. The write() call will tell you when the connection is gone.

Related

C Program Attempting to send data, from serveride to client via tcp causing immediate crash

As the title stated - any atempts made by the serverside to send data back to the client result in an imediate crash (segmentation fault). This is a simple tcp chat app - and I am only looking to send strings bidirectionaly between client and server.
Server side below - the chat() function handles communication , after calling fgets , inputting my string , and attempting to send the data - I get an immediate (segmentation fault) and crash.
#include <stdio.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <ifaddrs.h>
#define SA struct sockaddr
int chat(int sockfd, int port) {
for (;;) {
char *buffer_send;
char *buffer_recv;
recv(sockfd, buffer_recv, port , 0);
printf("%s", buffer_recv);
printf(":"); fgets(buffer_send, 512, stdin);
char* exit_func;
exit_func = strstr(buffer_send, "exit");
if (exit_func = strstr(buffer_send, "exit")) {
close(sockfd);
return 0;
} else {
send(sockfd, buffer_send, 512, 0);
}
}
}
int main () {
int server_socket, new_socket, c;
struct sockaddr_in socket_address, client;
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == -1) {
printf("socket creation failed! \n");
return 1;
} printf("socket created! \n");
socket_address.sin_addr.s_addr = inet_addr("192.168.0.10");
socket_address.sin_family = AF_INET;
socket_address.sin_port = (8003);
if( bind(server_socket,(struct sockaddr *)&socket_address , sizeof(socket_address)) < 0) {
printf("bind failed! \n");
return 1;
} printf("bind done! \n");
listen(server_socket , 3);
printf("Waiting for incoming connections...\n");
c = sizeof(struct sockaddr_in);
new_socket = accept(server_socket, (struct sockaddr *)&client, (socklen_t*)&c);
if (new_socket<0) {
printf("accept failed\n");
return 1;
} printf("connection accepted!\n");
chat(new_socket, socket_address.sin_port);
return 0;
}
however the same way of sending data on my client seems to work fine (without crashing while trying to send data):
#include <stdio.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <ifaddrs.h>
int chat(int sockfd, int port) {
for (;;) {
char *buffer_send;
char *buffer_recv;
printf(":"); fgets(buffer_send, 512, stdin);
char* exit_func;
exit_func = strstr(buffer_send, "exit");
if (exit_func = strstr(buffer_send, "exit")) {
close(sockfd);
return 0;
} else {
send(sockfd, buffer_send, 512, 0);
}
recv(sockfd, buffer_recv, port , 0);
printf("%s", buffer_recv);
}
}
int main () {
int target_socket;
struct sockaddr_in target_server;
target_socket = socket(AF_INET, SOCK_STREAM, 0);
if (target_socket == -1) {
printf("socket creation failed!\n");
return 1;
} printf("socket created!\n");
target_server.sin_addr.s_addr = inet_addr("192.168.0.10");
target_server.sin_family = AF_INET;
target_server.sin_port = (8003);
if (connect(target_socket , (struct sockaddr *)&target_server , sizeof(target_server)) < 0) {
printf("connection failed!\n");
return 1;
} printf("connected!\n");
chat(target_socket, target_server.sin_port);
return 0;
}
You did not allocated the room for incoming messages, the same for the buffer you want to send. I expect to do some char buffer_send[512 + 1] = {}; and char buffer_recv[512 + 1] = {}; to make some place for the message content.
The + 1 is added for the extra safety, to not overwrite the NULL terminator when the message received is large enough to fill the entire allocated buffer.

Recv() function messing up other parts of my code

I am trying to execute cat|grep using a client server set-up, works as follows: client sends word to search for using grep, server executes cat|grep, sends results to client but the recv() function seems to be messing up with my code.
What's the problem?
Adding the recv() function makes other parts of my code not working, every puts() works until puts("test5"); which is where my code is stuck in the execution, putting the recv() function as a comment makes the code run fine.
Till now I am not using the word I send from the client in any way so the problem must be with the receive function itself, it's not giving an error and when I print the content I send it works fine.
Here is the relevant client part:
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include<errno.h>
#define PORT 8080
int main(int argc, char const *argv[])
{
int sock = 0, valread;
struct sockaddr_in serv_addr;
int buffer[1024];
char buffer2[1024]={0};
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("\n Socket creation error \n");
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
if(inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0)
{
perror("Invalid address \n");
return -1;
}
if (connect(sock, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)
{
perror("Connection Failed \n");
return -1;
}
int i, array[argc], countsize=0;
if(argc>=2)
{
for(i=1; i<argc; i++)
{
int number=atoi(argv[i]);
array[i-1]=number;
countsize++;
}
if(send(sock, array, countsize*sizeof(int), 0)<0)
{
printf("Error in send! %s\n", strerror(errno));
return -1;
}
if(argc>=2)
{
int i=0;
for(int i=0; i<argc; i++)
{
if(atoi(argv[i])==6)
{
puts("Please enter the name/word you want to search for in the history file: ");
char word[30];
fgets(word, 30, stdin);
if(send(sock, &word , 30, 0)<0)
printf("Error in send! %s\n", strerror(errno));
valread = read( sock , buffer2, 1024);
puts("The result cat|grep is:");
printf("%s\n", buffer2);
}
}
}
}
return 0;
}
Here is the server's main method:
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include<errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdbool.h>
#include <sys/wait.h>
#include<time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <pthread.h>
#include <arpa/inet.h>
#define PORT 8080
void *catgrep(void *);
int main()
{
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer2[1024]={0};
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
if (setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR , &opt, sizeof(opt)))
{
perror("setsockopt");
exit(EXIT_FAILURE);
}
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons( PORT );
if (bind(server_fd, (struct sockaddr *)&address, sizeof(address))<0)
{
perror("bind failed");
exit(EXIT_FAILURE);
}
while (1)
{
if (listen(server_fd, 20) < 0)
{
perror("listen");
exit(EXIT_FAILURE);
}
if ((new_socket = accept(server_fd, (struct sockaddr *)&address,(socklen_t*)&addrlen))<0)
{
perror("accept");
exit(EXIT_FAILURE);
}
int arguments[10]={0};
int n = recv(new_socket, arguments ,1024*sizeof(int),0);
int j;
int argumentsize=n/sizeof(int);
for(j=0; j<argumentsize;j++)
{
if(arguments[j]==6)
{
pthread_t th5;
pthread_attr_t attr5;
pthread_attr_init(&attr5);
if(pthread_create(&th5,&attr5, catgrep,&new_socket)!=0)
{
printf("Error in pthread_create %s\n", strerror(errno));
return -1;
}
pthread_join(th5, NULL);
return -1;
}
}
close(new_socket);
}
close(server_fd);
return 1;
}
Here is my catgrep() method:
void *catgrep(void * param)
{
int *sock = (int*) param;
int new_sock = *sock;
int fd[2];
pipe(fd);
pid_t pid = fork();
char word[30];
recv(new_sock, word ,30, 0); //when I put this line code
starts messing up.
puts(word);
if(pid==0)
{
close(1);
dup(fd[1]);
close(fd[0]);
close(fd[1]);
char *cat_args[] = {"/bin/cat", "GameData.txt", NULL};
if(execv(cat_args[0], cat_args)<0)
{
printf("Error in execv! %s\n", strerror(errno));
}
exit(0);
}
if(pid > 0)
{
close(0);
dup(fd[0]);
close (fd[1]);
close(fd[0]);
puts("test2");
FILE *fp2;
if ((fp2 = popen("grep -w tries", "r")) == NULL)
{
perror("popen failed");
return NULL;
}
puts("test3");
size_t str_size = 1024;
char *stringts2 = malloc(str_size);
if (!stringts2)
{
perror("stringts allocation failed");
return NULL;
}
puts("test4");
stringts2[0] = '\0';
char buf[128];
size_t n;
puts("test5"); //when I use the recv() program gets stuck here.
while ((n = fread(buf, 1, sizeof(buf) - 1, fp2)) > 0)
{
puts("test10");
buf[n] = '\0';
size_t capacity = str_size - strlen(stringts2) - 1;
while (n > capacity)
{
str_size *= 2;
stringts2 = realloc(stringts2, str_size);
if (!stringts2)
{
perror("stringts realloation failed");
return NULL;
}
capacity = str_size - strlen(stringts2) - 1;
}
strcat(stringts2, buf);
}
puts("test6");
if (pclose(fp2) != 0)
{
perror("pclose failed");
return NULL;
}
puts("test7");
if(send(new_sock, stringts2, 10000, 0)<0)
{
printf("Error in send! %s\n", strerror(errno));
}
}
return NULL;
}
Few notes:
I am aware that in this particular piece of code I am not using the word sent by the client, hence why some lines are as comments, I will implement this when my problem gets fixed.
I am using popen() as I want to return the output of catgrep().
I isolated the problem and not it's only happening when I include the recv() function.
The word I am sending is being printed when I use recv() so the function isn't causing errors but it's messing up other parts.
UPDATE:
As suggested by someone in the comments I changed the way I receive the word sent by my client, I am now using the following:
int count = 0;
int total = 0;
while ((count = recv(new_sock, &word[total], sizeof word - count, 0)) > 0)
{
total=total+count;
}
if (count==-1)
{
perror("error in recv()");
}
Still having the same problem and same output.
The basic problem is that you are confusing strings and byte streams -- they're not the same thing.
In your client, you send some data with:
char word[30];
fgets(word, 30, stdin);
if(send(sock, &word , 30, 0)<0)
This will read a line (including a newline) into the beginning of an on-stack buffer, and then send the entire buffer, including whatever garbage happens to be in it after the end of the string. You probably don't want the newline, maybe don't want the NUL terminator, and certainly don't want the garbage.
In addition, you don't check the return value of send for a short send -- in some (admittedly rare) situations, a send might not send all the data you request.
On the reading side you don't check the return value of recv to see how many bytes you got, which may be different from what you expect -- there's no guarentee that there will be 1:1 correspondence between send and recv calls on a connection. One send might get broken up and split across multiple recvs, and several sends might have their data combined and returned in one recv. So you always need to check the return value of recv to see how many bytes you actually got.

unix socket send() succeeds, but recv() fails

I am writing a simple client server program using unix domain sockets, but am having issues with the recv() call in my client program.
The program executes as follows:
Server sets up socket and waits for a connection
Client connects and sends a string
Server receives string, and sends string back to client (like an echo)
Client recv() call fails, returning "resource temporarily unavailable"
Client exits
Server waits for another connection
I have also tried using a poll() call in my client to wait for the response from the server.
In this case however, the recv() call simply receives a 0, implying the connection has been closed serverside, which it has not.
I have exhausted google on this error, but no fixes I came accross seem applicable to my code.
I have included my client (with poll() code commented out) and server code below.
I'm probably missing something obvious... but any insight would be greatly appreciated!
Server code:
/*
* testServer.c
*
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <termios.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/un.h>
#include <linux/spi/spidev.h>
#include <linux/sockios.h>
#include <errno.h>
#define SOCK_PATH "/var/run/ts.serv"
void handleSockIO(int *sockDesc);
int main ()
{
int sock;
struct sockaddr_un sock_addr;
int len, p;
struct pollfd poll_fd[1];
printf("[TS] testServer Started.\r\n");
if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
{
perror("[TS]wr_sock creation");
}
else
{
printf("[TS] Created socket descriptor.\r\n");
}
sock_addr.sun_family = AF_UNIX;
strcpy(sock_addr.sun_path, SOCK_PATH);
unlink(sock_addr.sun_path);
len = strlen(sock_addr.sun_path) + sizeof(sock_addr.sun_family);
if (bind(sock, (struct sockaddr *)&sock_addr, len) == -1)
{
perror("[TS]sock bind failed\r\n");
}
else
{
printf("[TS] Bound socket to sock_addr.\r\n");
}
if (listen(sock, 5) == -1)
{
perror("[TS] sock listen fail");
}
else
{
printf("[TS] Socket now listening.\r\n");
}
poll_fd[0].fd = sock;
poll_fd[0].events = POLLIN;
printf("[TS] Waiting for a connection...\r\n");
while (1)
{
p = poll(poll_fd, 1, 1); //Wait for 1 ms for data
if (p == -1)
{
perror("[TS] Poll");
}
else if (p == 0)
{
//printf("Timeout occurred!\n");
}
else
{
if (poll_fd[0].revents & POLLIN)//Data available to read without blocking
{
printf("[TS] Data available on sock..\r\n");
handleSockIO(&sock);
printf("[TS] Waiting for another connection...\r\n");
}
}
}//While(1)
return 0;
}
void handleSockIO(int *sockDesc)
{
int ioSock, n;
socklen_t t;
struct sockaddr_un remote_addr;
char str[15];
memset(str, ' ', sizeof(str));
t = sizeof(remote_addr);
if ((ioSock = accept(*sockDesc, (struct sockaddr *)&remote_addr, &t)) == -1)
{
perror("accept failed\r\n");
}
else
{
printf("[TS] Receiving...\r\n");
n = recv(ioSock, str, sizeof(str), 0);
if (n < 0)
printf("[TS] recvfrom failed: %s\r\n", strerror(errno));
else if(n == 0)
{
printf("[TS] Received %d on ioSock...\r\n", n);
}
else if(n > 0)
{
printf("[TS] Received: %s, which is %d long.\r\n", str, strlen(str));
printf("[TS] Echoing response...\r\n");
if (send(ioSock, str, n, 0) == -1) //Echo str back
{
printf("[TS] send failed: %s\r\n", strerror(errno));
}
else
{
printf("[TS] Send successful\r\n");
}
//============Wait to close IO descriptor=================
int r;
char temp[1]; //Arbitrary buffer to satisfy recv()
do
{
printf("[TS] Waiting for client to close connection...\r\n");
r = recv(ioSock, temp, sizeof(temp), 0);
if (r == 0)
{
printf("[TS] Client closed connection, closing ioSock...\r\n");
close(ioSock);
}
} while (r != 0);
//========================================================
}//if(n>0) else...
}
}
Client code:
/*
* testClient.c
*
*/
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <termios.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/un.h>
#include <linux/sockios.h>
#include <errno.h>
#define CHAR_BUF_SIZE 15
#define SEND_STRING "Hello!"
#define SOCK_PATH "/var/run/ts.serv"
int main ()
{
char str[CHAR_BUF_SIZE] = {0};
int c, len, n, p;
int s; // s will hold a socket descriptor returned by socket()
struct sockaddr_un serv_addr;
struct pollfd poll_fd[1];
printf("[TC] testClient Started.\r\n");
//===============SOCKET SETUP===============================
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
{
printf("[TC] Socket failed: %s\r\n", strerror(errno));
}
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sun_family = AF_UNIX;
strcpy(serv_addr.sun_path, SOCK_PATH);
len = strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family);
//==========================================================
// printf("[TC]Trying to connect to TS socket...\r\n");
//===============RESPONSE POLL SETUP========================
poll_fd[0].fd = s;
poll_fd[0].events = POLLIN;
//==========================================================
printf("[TC] Connecting to SOCK_PATH...\r\n");
c = connect(s, (struct sockaddr *)&serv_addr, len);
if (c == -1)
{
printf("[TC] Connection failed: %s\r\n", strerror(errno));
}
else
{
printf("[TC] Connected. Sending string....\r\n");
if (send(s, SEND_STRING, strlen(SEND_STRING), 0) == -1)
{
printf("[TC] send() failed: %s\r\n", strerror(errno));
}
else
{
printf("[TC] Send on SOCK_PATH successful.\r\n");
//Sending complete------------------------------------------------
//Wait for response...
printf("[TC] Waiting for server response...\r\n");
// p = poll(poll_fd, 1, -1); //Wait for a response
//
// if (p == -1)
// {
// perror("[TC] Poll");
// }
// else
// {
// if(poll_fd[0].revents & POLLIN)
// {
n = recv(s, str, sizeof(str), 0);
if (n < 0)
{
printf("[TC] Receive on SOCK_PATH failed: %s\r\n",
strerror(errno));
}
else if(n == 0)
{
printf("[TC] %d Received on SOCK_PATH.\r\n", n);
}
else if(n > 0)
{
printf("[TC] Received %d from SOCK_PATH: %s\r\n",
n, str);
}
// }
// }
}//if(send())
}//if(connect())
printf("[TC] Transction complete, closing connection and exiting.\r\n");
close(s);
return 0;
}
len = sizeof(serv_addr) instead of len = strlen(serv_addr.sun_path) + sizeof(serv_addr.sun_family) should solve you problem. Also do not ignore compiler warnings, say n = recv(s, str, strlen(str), 0) with n declared as int and ssize_t returned by recv. It will help you to avoid a future errors.

writing to a close socket didn't raise a SIGPIPE as expected

I've already read about how to prevent SIGPIPE, then I write a small program to test it. Here is the code.
server.c
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
void hdl(int sig_num, siginfo_t *sig_info, void *context)
{
printf("got you, SIGPIPE!\n");
}
int main()
{
int sfd, cfd;
struct sockaddr_in saddr, caddr;
struct sigaction act;
memset (&act, '\0', sizeof(act));
act.sa_sigaction = hdl;
act.sa_flags = SA_SIGINFO;
if (sigaction(SIGPIPE, &act, NULL) < 0) {
return 1;
}
sfd= socket(AF_INET, SOCK_STREAM, 0);
saddr.sin_family=AF_INET;
saddr.sin_addr.s_addr=inet_addr("192.168.22.91");
saddr.sin_port=htons(12345);
if(bind(sfd, (struct sockaddr *)&saddr, sizeof(saddr)) )
{
printf("bind error\n");
return -1;
}
if(listen(sfd, 1))
{
printf("error\n");
return -1;
}
char buf[1024] = {0};
while(1) {
printf("Server listening...\n");
cfd=accept(sfd, (struct sockaddr *)NULL, NULL);
fcntl(cfd, F_SETFL, O_NONBLOCK);
int size = read(cfd, buf, 1024);
if(size == -1)
printf("read error\n");
sleep(2); // sleep for a while to make sure the client closed the socket
int ret;
if((ret = write(cfd, buf, strlen(buf)))<0)
{
if(errno == EPIPE)
fprintf(stderr, "SIGPIPE");
}
ret = write(cfd, buf, strlen(buf)); // write again.
printf("write return %d\n", ret);
}
close(sfd);
}
client.c
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <assert.h>
int main()
{
int ret, fd;
struct sockaddr_in sa_dst;
char buffer[] = "hello, world";
char rcv_buf[128] = {0};
fd = socket(AF_INET, SOCK_STREAM, 0);
memset(&sa_dst, 0, sizeof(struct sockaddr_in));
sa_dst.sin_family = AF_INET;
sa_dst.sin_port = htons(12345);
sa_dst.sin_addr.s_addr = inet_addr("192.168.22.91");
ret = connect(fd, (struct sockaddr *)&sa_dst, sizeof(struct sockaddr));
if(ret != -1)
{
send(fd, buffer, strlen(buffer), 0);
close(fd);
}
return 0;
}
When I run the server and the client on the same linux machine, on the server side, the first write() returns the number of bytes written while I expect a SIGPIPE signal because I closed the socket on the client side, the second write() does generate a SIGPIPE signal.
But when I ran the client on another linux machine or on a Windows machine(implement the same client with Winsock), I did't catch any SIGPIPE signal, and the second write() still returns the size of the buffer. Can someone tell me what's going on?
It can't happen on the first write, for two reasons:
The localhost doesn't know that the peer has closed the socket for reading. A FIN has been received but that could just be because the peer has shutdown for output. Only an RST will tell it that, and it doesn't get that util the next I/O at the earliest.
Buffering.
NB you're corrupting the value of errno by calling perror(), so testing it afterwards isn't valid.
Just Change this in SERVER and it will work
fcntl(cfd, F_SETFL, O_NONBLOCK);
int size = read(cfd, buf, 1024);
if(size == -1)
printf("read error\n");
sleep(2); // sleep for a while to make sure the client closed the socket
int ret;
ret = write(cfd, buf, strlen(buf));
sleep(2);
ret = write(cfd, buf, strlen(buf)); // write again.
printf("write return %d\n", ret);

Processes not terminating

There are some strange things happening in my client-server application. Please, look at these simple fork client/server:
CLIENT:
#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <errno.h>
#include <sys/time.h>
#include <sys/wait.h>
#define IP_SERVER "192.168.1.89"
#define PORT_SERVER 65000
#define BUFFERSIZE 1024
#define NUMFILES 3
double timeElapsed(struct timeval* before, struct timeval* after) {
return after->tv_sec - before->tv_sec + (double) (after->tv_usec - before->tv_usec)/1000000;
}
void getFile(char *request, struct sockaddr_in server) {
char buffer[1024];
int sockProc, res;
int file;
int sizeServ = sizeof(server);
int writeFile;
sockProc = socket(AF_INET, SOCK_STREAM, 0);
if (sockProc < 0) {
printf("Error on creating socket client\n");
perror("");
exit(1);
}
file = open(request, O_CREAT | O_WRONLY, S_IRWXU);
res = connect(sockProc, (struct sockaddr*)&server, (socklen_t)sizeServ);
if (res < 0) {
printf("Error on connecting to server!\n");
perror("");
exit(1);
}
res = send(sockProc, (void*)request, strlen(request), 0);
memset(buffer, 0, sizeof(buffer));
while((res = recv(sockProc, (void*)buffer, sizeof(buffer), 0)) > 0) {
write(file, (void*)buffer, strlen(buffer));
memset(buffer, 0, sizeof(buffer));
}
close(sockProc);
close(file);
return;
}
int main(int argc, char** argv) {
int sockCli, res, i;
struct sockaddr_in server;
int sizeServ = sizeof(server);
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
inet_pton(AF_INET, IP_SERVER, &server.sin_addr);
server.sin_port = htons(PORT_SERVER);
char files[NUMFILES][32];
char nameFile[32];
char command[32] = "rm *.txt";
system(command);
struct timeval begin;
struct timeval end;
pid_t processes[NUMFILES];
for(i = 0; i<NUMFILES; i++) {
memset(nameFile, 0, sizeof(nameFile));
printf("Inserisci nome file (con estensione) da ricevere:\n");
scanf("%s", nameFile);
strcpy(files[i], nameFile);
}
gettimeofday(&begin, NULL);
for(i=0; i<NUMFILES; i++) {
pid_t child = fork();
if(child == 0) {
getFile(files[i], server);
exit(0);
}
else {
processes[i] = child;
continue;
}
}
/*for(i=0; i<NUMFILES; i++) {
waitpid(processes[i], NULL, 0);
}*/
wait(NULL);
gettimeofday(&end, NULL);
printf("Time elapsed on TCP is %f seconds\n", timeElapsed(&begin, &end));
return 0;
}
and the SERVER:
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include <arpa/inet.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#define IP_SERVER "192.168.1.89"
#define PORT_SERVER 65000
#define BUFFERSIZE 1024
void execRequest(int* sockCli, struct sockaddr_in* client) {
char buffer[BUFFERSIZE];
char request[BUFFERSIZE];
int res;
memset(request, 0, sizeof(request));
res = recv(*sockCli, (void*)request, sizeof(request), 0);
if(res < 0) {
printf("Error on recv()\n");
perror("");
exit(1);
}
printf("Requested file %s\n", request);
char resource[32] = "files/";
strcat(resource, request);
int file = open(resource, O_RDONLY);
if (file < 0) {
printf("File %s does not exist\n", request);
exit(1);
}
memset(buffer, 0, sizeof(buffer));
while((res = read(file, (void*)buffer, sizeof(buffer))) > 0) {
send(*sockCli, (void*)buffer, strlen(buffer), 0);
memset(buffer, 0, sizeof(buffer));
}
close((*sockCli));
close(file);
free(sockCli);
free(client);
return;
}
int main(int argc, char** argv) {
int sockServ, i, res;
int *sockCli;
struct sockaddr_in server;
struct sockaddr_in* client;
sockServ = socket(AF_INET, SOCK_STREAM, 0);
if(sockServ < 0) {
printf("Error in creating socket\n");
perror("");
exit(1);
}
memset(&server, 0, sizeof(server));
server.sin_addr.s_addr = inet_addr(IP_SERVER);
server.sin_port = htons(PORT_SERVER);
server.sin_family = AF_INET;
int reuse = 1;
res = setsockopt(sockServ, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(int));
if (res < 0) {
printf("setsockopt() REUSEADDR failed\n");
perror("");
exit(1);
}
res = bind(sockServ, (struct sockaddr*)&server, sizeof(server));
if (res < 0) {
printf("Error on bindind TCP server!\n");
perror("");
exit(1);
}
res = listen(sockServ, 5);
if (res < 0) {
printf("Error on listening TCP server!\n");
perror("");
exit(1);
}
while(1) {
sockCli = (int*)malloc(sizeof(int));
client = (struct sockaddr_in*)malloc(sizeof(struct sockaddr_in));
int sizeClient = sizeof(struct sockaddr_in);
*sockCli = accept(sockServ, (struct sockaddr*)client, &sizeClient);
if ((*sockCli) < 0) {
printf("accept() failed\n");
perror("");
continue;
}
printf("Connected to %s:%d\n", inet_ntoa(client->sin_addr), client->sin_port);
if( !fork() ) {
execRequest(sockCli, client);
exit(0);
}
else
continue;
}
return 0;
}
This is very strange. The processes created by the client don't terminate even if the server closes the sockets and so recv() should return 0 and let client processes exit from the loop. Moreover there's something strange about reading files:
the server simply reads files.txt but in doing this it includes the string ".txt" in the read characters and sends all this mixture to the client...why?
they are simple file mono character like
aaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaa
but the server reads and and sends:
aaaaaaaaaaaaaaaaaa.txt
aaaaaaaaaaaaaaaaaa
can I solve all this?
You can't use strlen(buffer), just because you're loading characters from a text file doesn't mean that buffer will be a valid string unless you take steps to ensure it is. And you don't; there's no termination since you can fill all of buffer with data from the file.
How many times must we play the broken record here on Stack Overflow? Don't cast malloc!
I chalk this error to failure to read the manual(s), to find out what header to include, what a string is (and hence what strlen/strcat/str*{anything}* expects of its input, what printf expects of arguments that correspond to a %s format specifier, etc.) and what read/recv produces.
res = recv(*sockCli, (void*)request, sizeof(request), 0);
if(res < 0) {
printf("Error on recv()\n");
perror("");
exit(1);
}
printf("Requested file %.*s\n", res, request); // NOTE the field width provided by 'res'
By the manual, examples such as res = read(file, (void*)buffer, sizeof(buffer)) supposedly store either an error or a length. The condition ensures that the send code will only execute when it's a length value, so why not use it as one? send(*sockCli, (void*)buffer, res, 0);?
The presense of these problems seems to indicate that your method of learning isn't working. Which book are you reading? Learning C without a book is a bit like learning which berries are poisonous without communication.

Resources