This is a basic TCP-Server implementation for teaching purposes. Are there any error or improvements to do. Any suggest is welcome!
I only have a doubt:
signal(SIGCHLD, SIG_IGN);
Is that call used to prevent zoombie-child processes?
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define BACKLOG 5
#define MAXSIZE 1024 //max-bytes for read-buffer
void main(){
int sock_ds, ret, length;
int acc_ds; //Accept socket descriptor
struct sockaddr_in addr; //this addres
struct sockaddr rem_addr; //remote address (generic)
char buff[MAXSIZE];
sock_ds = socket(AF_INET, SOCK_STREAM, 0); // => TCP
bzero((char *)&addr, sizeof(addr)); //reset struct
addr.sin_family = AF_INET;
addr.sin_port = htons(25000);
addr.sin_addr.s_addr = INADDR_ANY;
ret = bind(sock_ds, &addr, sizeof(addr));
if(ret == -1){
perror("Binding error");
exit(1);
}
ret = listen(sock_ds, BACKLOG); // backlog queue
if(ret == -1){
perror("Listen error");
exit(1);
}
length = sizeof(rem_addr);
signal(SIGCHLD, SIG_IGN); //zombie children management
/*Busy-waiting (server) and concurrency */
while(1){
/*Repeat until success*/
while(acc_ds = accept(sock_ds, &rem_addr, &length) == -1){
if(fork() == 0){ //child-process
close(sock_ds); //unused from child
do{
read(acc_ds, buff, MAXSIZE);
printf("Message from remote host:&s\n", buff);
}while(strcmp(buff, "quit") == 0);
/*Transimission completed: server response */
write(acc_ds, "Reading Done", 10);
close(acc_ds); //socket closed
exit(0); //exiting from child
}
else{
close(acc_ds); //unused from parent
}
}
}
}
Return type of main is not int. It should be. Either return EXIT_SUCCESS or EXIT_FAILURE.
Result of socket() call is not checked. It should be, or bind will fail but perror() will tell "Invalid argument" instead of the actual error.
A return value of read() is not checked possibly triggering undefined behavior when printing.
There is no &s format specified, it should be %s.
%s expects a null-terminated string. This is not guaranteed by the code (see point #3). strcmp() may crap out as well.
As for the SIGCHLD, #cnicutar has kindly answered that already, nothing to add there.
Hope it helps. Good Luck!
Yes, that's exactly what ignoring SIGCHLD is for. From TLPI:
There is a further possibility for dealing with dead child processes.
Explicitly setting the disposition of SIGCHLD to SIG_IGN causes any
child process that subsequently terminates to be immediately removed
from the system instead of being converted into a zombie.
It is standard across Unix implementations.
I need to learn how this works, too. So, I googled "simple tcp-server", found this little program, and fixed up the code to be happier with gcc -Wall and the comments. here is what I put together:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/ptrace.h>
#define BACKLOG 5
#define MAXSIZE 1024 //max-bytes for read-buffer
#define PORT 25000
/****************************************************************/
int main() {
int sock_ds, ret; unsigned int length;
int acc_ds; //Accept socket descriptor
struct sockaddr_in addr; //this address
struct sockaddr rem_addr; //remote address (generic)
char buff[MAXSIZE+1];
if (!(sock_ds = socket(AF_INET, SOCK_STREAM, 0))) perror("socket call failed"); // => TCP
bzero( (char *)&addr, sizeof(addr)); //reset struct
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = INADDR_ANY;
ret = bind(sock_ds, (struct sockaddr *)&addr, sizeof(addr));
if (ret == -1) {
perror("Binding error");
exit(EXIT_FAILURE);
}
ret = listen(sock_ds, BACKLOG); // backlog queue
if (ret == (-1)) {
perror("Listen error");
exit(EXIT_FAILURE);
}
length = sizeof (rem_addr);
// sigaction(SIGCHLD, SA_NOCLDWAIT); //zombie children management
/*Busy-waiting (server) and concurrency */
while (1) {
fprintf(stderr, "[Waiting for client %d]\n", getpid());
/*Repeat until success*/
while ((acc_ds = accept(sock_ds, &rem_addr, &length)) == -1) {
fprintf(stderr, "[Accepted from client %d]\n", getpid());
if (fork() == 0) { //child-process
close(sock_ds); //unused from child
fprintf(stderr, "[Reading from client %d]\n", getpid());
while (read(acc_ds, buff, MAXSIZE)) {
buff[MAXSIZE]='\0';
printf("Message from remote host:%s\n", buff);
fflush(stdout);
if (strncmp (buff, "quit", 5) == 0) break;
}
/*Transmission completed: server response */
if (write(acc_ds, "Reading Done", 10)) fprintf(stderr, "failed write\n");
close(acc_ds); //socket closed
exit(EXIT_SUCCESS); //exiting from child
}
else{
close(acc_ds); //unused from parent
}
}
}
return EXIT_SUCCESS;
}
two problems. the first is that gnu linux gcc refuses to accept
sigaction(SIGCHLD, SA_NOCLDWAIT); //zombie children management
tcp-server2.c: In function ‘main’:
tcp-server2.c:53:3: warning: passing argument 2 of ‘sigaction’ makes pointer from integer without a cast [enabled by default]
In file included from tcp-server2.c:7:0:
/usr/include/signal.h:267:12: note: expected ‘const struct sigaction * restrict’ but argument is of type ‘int’
tcp-server2.c:53:3: error: too few arguments to function ‘sigaction’
In file included from tcp-server2.c:7:0:
/usr/include/signal.h:267:12: note: declared here
the second is that when I telnet to port 25000 and type a few things, nothing ever is echoed as received. so the server does not seem to work. it never gets to accepted from client.
now, I could pick up a programming example from somewhere else, but I thought I should just report this here, so we get a simplest tcp server posted correct here.
Related
I am trying to learn the basic of network communication using sockets in C. My client program takes in a message from the user, echoes it server side and back, and prints out the received message. When I fire both of them up for the first time, they both work exactly as expected. However, if I quit the client side and then fire it up again while keeping the server program running, my echoed messages become off by one.
I assumed it was because the last message is getting caught in the pipe or something, and after poking around, I saw that someone suggested to use shutdown() to flush out the pipe, but that doesn't seem to be working. I also tried to zero out the buffers wherever I thought they may be lingering, but that didn't seem to help, either.
server.c
#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#define PORT 12403
#define BUFFER_MAX 1024
#define BACKLOG_MAX 1024
int clientSocket;
int serverSocket;
void listening()
{
while (1)
{
struct sockaddr_in clientAddress;
socklen_t addressLength = sizeof(clientAddress);
/*---accept a connection (creating a data pipe)---*/
clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &addressLength);
if (clientSocket > -1)
{
printf("%s:%d connected\n", inet_ntoa(clientAddress.sin_addr), ntohs(clientAddress.sin_port));
break;
}
}
}
int main(int Count, char *Strings[])
{
struct sockaddr_in socketInfo;
char buffer[BUFFER_MAX];
//Create socket
if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Error creating socket");
exit(errno);
}
//Setting the linger option to off and resuse address option to on for testing
int option = 0;
setsockopt(serverSocket, SOL_SOCKET, SO_LINGER, &option, sizeof(option));
option = 1;
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
//Initialize socket information
bzero(&socketInfo, sizeof(socketInfo));
socketInfo.sin_family = AF_INET;
socketInfo.sin_port = htons(PORT);
socketInfo.sin_addr.s_addr = INADDR_ANY;
//Assign a port number to the socket
if (bind(serverSocket, (struct sockaddr*)&socketInfo, sizeof(socketInfo)) != 0)
{
perror("Error binding socket");
exit(errno);
}
//Set socket to listen
if (listen(serverSocket, BACKLOG_MAX) != 0)
{
perror("Error setting socket to listen");
exit(errno);
}
listening();
//Once first socket has been connected, begin echoing process
int i = 0;
while (1)
{
//Clear the buffer
bzero(buffer, BUFFER_MAX);
//Echo back anything sent
//Close connection and begin listening process again if the client disconnects
int sendCheck;
int readCheck;
readCheck = recv(clientSocket, buffer, BUFFER_MAX, 0);
if (readCheck <= 0)
{
shutdown(clientSocket, SHUT_WR);
close(clientSocket);
sleep(1);
listening();
}
sendCheck = send(clientSocket, buffer, BUFFER_MAX, 0);
if (sendCheck <= 0)
{
shutdown(clientSocket, SHUT_WR);
close(clientSocket);
sleep(1);
listening();
}
i++;
}
close(serverSocket);
return 0;
}
client.c
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <stdlib.h>
#include <resolv.h>
#include <netdb.h>
#define PORT 12403
#define LOCALHOST "127.0.0.1"
#define BUFFER_MAX 1024
int socketStatus = 0;
void sigpipeHandler()
{
perror("Connection to server terminated\n");
socketStatus = 0;
}
int main()
{
int mySocket;
struct sockaddr_in socketInfo;
char buffer[BUFFER_MAX];
int count = 0;
//Create socket
if ((mySocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Error creating socket");
exit(errno);
}
//Get IP address of required host machine
char* hostName = "<host name removed>";
int portNumber = PORT;
char* ipAddr = NULL;
struct hostent* host = NULL;
host = gethostbyname(hostName);
ipAddr = inet_ntoa(*((struct in_addr*) host->h_addr_list[0]));
//Initialize server information
bzero(&socketInfo, sizeof(socketInfo));
socketInfo.sin_family = AF_INET;
socketInfo.sin_port = htons(portNumber);
if (inet_aton(ipAddr, (struct in_addr *)&socketInfo.sin_addr.s_addr) == 0)
{
perror("Error assigning IP address");
exit(errno);
}
//Set up sigpipe handler
signal(SIGPIPE, sigpipeHandler);
//Connect to server
if (connect(mySocket, (struct sockaddr*)&socketInfo, sizeof(socketInfo)) != 0)
{
perror("Error connecting");
exit(errno);
}
//Indicate that socket is OK
socketStatus = 1;
while(1)
{
if(!socketStatus) {shutdown(mySocket, SHUT_WR); break;}
printf("Please enter a command.\n");
char command[BUFFER_MAX];
bzero(command, BUFFER_MAX);
fgets(command, sizeof(command), stdin);
send(mySocket, command, BUFFER_MAX, 0);
//Get echoed message
bzero(buffer, BUFFER_MAX);
recv(mySocket, buffer, sizeof(buffer), 0);
printf("Echo [%d]:%s\n", ++count, buffer);
}
//Close socket
close(mySocket);
return 0;
}
I did some cleanup on your server code and this seems to work.
For my testing, the client code is unchanged. But, as others have suggested, you should check the error codes from send and recv. Also, note that if you ctrl-c the server, the client will hang in the fgets, so it won't detect the server abort until you hit return after the prompt. Not a big deal, but I thought I'd mention it.
I also added a fork so you can have multiple clients talking to the same server instance simultaneously.
I tested this with two clients [in two xterm windows] talking with the single server instance.
I moved your echo code into a new function docomm. A small difference from your code is that any error from either recv or send breaks out of the loop and closes the connection. All connections from new clients are guaranteed to start with a recv call.
In your code, you would not always break out of the loop, but close the connection and call listening again. This would happen for either send or recv. If it happened on the wrong one, this might be the source of the problem you were having because you could do a send before a recv to a new client initially.
#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <arpa/inet.h>
#include <errno.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <sys/wait.h>
#define PORT 12403
#define BUFFER_MAX 1024
#define BACKLOG_MAX 1024
int clientSocket;
int serverSocket;
int forkflg = 1;
void listening()
{
while (1)
{
struct sockaddr_in clientAddress;
socklen_t addressLength = sizeof(clientAddress);
/*---accept a connection (creating a data pipe)---*/
clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &addressLength);
if (clientSocket > -1)
{
printf("%s:%d connected\n", inet_ntoa(clientAddress.sin_addr), ntohs(clientAddress.sin_port));
break;
}
}
}
void
docomm(void)
{
char buffer[BUFFER_MAX];
//Once first socket has been connected, begin echoing process
int i = 0;
while (1) {
//Clear the buffer
bzero(buffer, BUFFER_MAX);
//Echo back anything sent
//Close connection and begin listening process again if the client disconnects
int sendCheck;
int readCheck;
readCheck = recv(clientSocket, buffer, BUFFER_MAX, 0);
if (readCheck <= 0)
break;
sendCheck = send(clientSocket, buffer, BUFFER_MAX, 0);
if (sendCheck <= 0)
break;
i++;
}
printf("close\n");
shutdown(clientSocket, SHUT_WR);
close(clientSocket);
}
int main(int Count, char *Strings[])
{
struct sockaddr_in socketInfo;
//Create socket
if ((serverSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Error creating socket");
exit(errno);
}
//Setting the linger option to off and resuse address option to on for testing
int option = 0;
setsockopt(serverSocket, SOL_SOCKET, SO_LINGER, &option, sizeof(option));
option = 1;
setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option));
//Initialize socket information
bzero(&socketInfo, sizeof(socketInfo));
socketInfo.sin_family = AF_INET;
socketInfo.sin_port = htons(PORT);
socketInfo.sin_addr.s_addr = INADDR_ANY;
//Assign a port number to the socket
if (bind(serverSocket, (struct sockaddr*)&socketInfo, sizeof(socketInfo)) != 0)
{
perror("Error binding socket");
exit(errno);
}
//Set socket to listen
if (listen(serverSocket, BACKLOG_MAX) != 0)
{
perror("Error setting socket to listen");
exit(errno);
}
while (1) {
listening();
if (! forkflg) {
docomm();
continue;
}
pid_t pid = fork();
if (pid == 0) {
docomm();
exit(0);
}
while (waitpid(0,NULL,WNOHANG) > 0);
}
close(serverSocket);
return 0;
}
UPDATE:
Just from a glance: 1) Can I ask why you created a fork flag if you never change the value of it? Should it be changed somewhere?
I used forkflg so you can set it to zero (e.g. int forkflg = 0;) to run sequentially. Or, you could add some code and parse argv looking for an option (e.g. -f) to set/clear it [for testing/debug purposes]. For production code, you'd want forkflg to be set and could remove the flag and just do the fork case always [adjusting the code to match].
Just tracing through the program mentally, it seems like the forking section will never be executed. Correct me where I'm wrong: after initially setting the socket to listen, the while loop will enter, and listening() will be called. Execution will halt in listening() until a connection is accepted.
Yes, that's true.
Control will return to main, where docomm() gets called. Control stays in docomm() until the connection breaks, at which point it returns to main and continue gets called, skipping the fork stuff and starting the process over again. So does the fork stuff ever get executed?
What you're describing is the behavior if forkflg is zero.
The fork is called if forkflg is set. Note that, in that case, docomm is called in the child and not the parent (because fork returned 0). So, the parent will not be blocked while the child does the echoing.
Thus, the parent returns immediately and is free to do the waitpid loop to reap any old children and restart the main/outer loop.
The waitpid loop only happens when a new connection comes in, so several children may have already terminated and will stay in zombie state until the waitpid loop gets executed [which will reap any/multiple pending children].
A cleaner way to reap the children might be to set up a signal handler for SIGCHLD and have it do the waitpid loop. This would reap all spent children immediately, without having to wait for a new connection to roll in.
Or, with the signal handler, add the waitpid loop to listening [inside the current loop] because if a SIGCHLD signal comes in, accept will return immediately with errno set to EINTR
First off, this is homework, so please no outright answers. I am writing a back and forth chat program in C. I'm extremely new to C (just started learning for this class). Currently I have three files:
server.c
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include "chat.h"
#define SERVER_PORT 1725
#define MAX_PENDING 5
#define MAX_LINE 256
int main()
{
struct sockaddr_in sin;
char buf[MAX_LINE];
int len;
int s, new_s;
struct chat_packet packet;
/* build address data structure */
bzero((char *)&sin, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(SERVER_PORT);
/* setup passive open */
if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
perror("simplex-talk: socket");
exit(1);
}
if ((bind(s, (struct sockaddr *)&sin, sizeof(sin))) < 0)
{
perror("simplex-talk: bind");
exit(1);
}
listen(s, MAX_PENDING);
/* wait for connection, then receive and print text */
while(1)
{
if ((new_s = accept(s, (struct sockaddr *)&sin, &len)) < 0)
{
perror("simplex-talk: accept");
exit(1);
}
/* Stay in the following loop until CTRL+C */
while (len = recv(new_s, &packet, sizeof(packet), 0))
{
fputs(packet.sender_name, stdout);
fputs(": ", stdout);
fputs(packet.data, stdout);
fputs("\nYou: ", stdout);
while (fgets(buf, sizeof(buf), stdin))
{
if(strlen(buf) > 144)
{
printf("Your message is too long. Please enter a new message.\n");
continue;
}
else
{
buf[MAX_LINE-1] = '\0';
strncpy(packet.data,buf,144);
char sender[8] = "Mason"; /*should be argv[index of name]*/
strncpy(packet.sender_name, sender, 8);
send(new_s, &packet, sizeof(packet),0);
}
}
}
close(new_s);
}
}
client.c
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include "chat.h"
#define SERVER_PORT 1725
#define MAX_LINE 256
int main(int argc, char * argv[])
{
FILE *fp;
struct hostent *hp;
struct sockaddr_in sin;
char *host;
char buf[MAX_LINE];
int s;
int len;
struct chat_packet packet;
if (argc==2)
{
host = argv[1];
}
else
{
fprintf(stderr, "usage: simplex-talk host\n");
exit(1);
}
/* translate host name into peer's IP address */
hp = gethostbyname(host);
if (!hp) {
fprintf(stderr, "simplex-talk: unknown host: %s\n", host);
exit(1);
}
/* build address data structure */
bzero((char *)&sin, sizeof(sin));
sin.sin_family = AF_INET;
bcopy(hp->h_addr, (char *)&sin.sin_addr, hp->h_length);
sin.sin_port = htons(SERVER_PORT);
/* active open */
if ((s = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
perror("simplex-talk: socket");
exit(1);
}
if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0)
{
perror("simplex-talk: connect");
close(s);
exit(1);
}
/* main loop: get and send lines of text */
while (fgets(buf, sizeof(buf), stdin))
{
if(strlen(buf) > 144)
{
printf("Your message is too long. Please enter a new message.\n");
continue; /*This allows the user to re-enter a message post-error*/
}
else
{
buf[MAX_LINE-1] = '\0';
strncpy(packet.data, buf, 144);
char sender[8] = "Abby"; /*should be argv[index of name]*/
strncpy(packet.sender_name, sender, 8);
send(s, &packet, sizeof(packet), 0);
recv(s, &packet, sizeof(packet),0);
fputs(packet.sender_name, stdout);
fputs(": ", stdout);
fputs(packet.data, stdout);
fputs("\nYou: ", stdout);
}
}
}
chat.h
#include <stdint.h> /* Needed for unsigned types */
#define MAX_DATA_LEN 144 /* So we are on 16-bit boundary */
#define USER_NAME_LEN 8
/* You must send this packet across the socket. Notice there are
* no pointers inside this packet. Why?*/
struct chat_packet {
u_short version; /* 16 bits -- Set to version 2 in code */
char sender_name[8]; /* 64 bits */
char data[MAX_DATA_LEN]; /* Message goes in here */
};
Everything except what is in the client and server while loops were given to me by my instructor. The base part of the assignment is getting back-and-forth chat functionality. I'm running everything in PuTTY using the command line. I duplicate the session and run client in one and server in the other. To run:
./client serverName
./server
I am able to go back and forth one time, and then nothing else sends or receives. I am still able to type, but the two sessions cannot see each other's messages past the first back and forth. I am not sure where my code is wrong. Any advice would be appreciated, as I'm very new to the language. Thanks in advance!
Okay, here's my hint: Think about what happens when you recv() zero characters. Also, check what happens when the server calls accept() vs. when the client calls connect().
You might also want to check the return values of your recv() calls more judiciously. (and send(), for that matter; if a call can fail, check its return value!) Here's a hint from the man recv page:
RETURN VALUES
These calls return the number of bytes received, or -1 if an error occurred.
Also, if you aren't familiar with a debugger (such as gdb), I would recommend learning it. In a pinch, you might consider adding printf() statements to your code, to figure out what is happening.
Also, think about where your "blocking calls" are. If you're not familiar with what it means to be a "blocking call", we call it "blocking" when you call a function, and that function doesn't return ("blocks") until some specified thing happens. For example, your accept() will block until a connection is accepted. Your fgets() will block until a line of text is received. send() would block if you've already sent too much data, and the buffer is full. recv() would block until you've received the specified number of bytes. recv() also has a behavior you might not expect, that you may need to account for:
If no messages are available at the socket, the receive call waits for a
message to arrive, unless the socket is nonblocking (see fcntl(2)) in
which case the value -1 is returned and the external variable errno set
to EAGAIN. The receive calls normally return any data available, up to
the requested amount, rather than waiting for receipt of the full amount
requested; this behavior is affected by the socket-level options
SO_RCVLOWAT and SO_RCVTIMEO described in getsockopt(2).
In your case, your packets might be small enough that you won't run into cases where you have to reassemble them yourself. But it couldn't hurt to check.
I think that gives you some avenues to explore...
My socket server program is mentioned below. It works fine with the single client but when I try to connect it with another client at the same time, I am unable to connect. But I have defined MAX_CLIENTS in my program as 2 but still why I am unable to connect with multiple clients? What is the correct process to connect with multiple client? Will I be able to connect with multiple client by modifying this code? Any possible fix?
Socket Server Code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <error.h>
#include <strings.h>
#include <unistd.h>
#include <arpa/inet.h>
#define ERROR -1
#define MAX_CLIENTS 2
#define MAX_DATA 1024
main (int argc, char **argv){
struct sockaddr_in server;
struct sockaddr_in client;
int sock;
int new;
int sockaddr_len = sizeof (struct sockaddr_in);
int data_len;
char data [MAX_DATA];
if ((sock = socket (AF_INET, SOCK_STREAM, 0)) == -1)
{
perror ("socket: ");
exit (-1);
}
printf("after socket");
server.sin_family = AF_INET;
server.sin_port = htons(atoi(argv[2]));
server.sin_addr.s_addr = INADDR_ANY;
bzero (&server.sin_zero, 8);
printf("after server");
if ((bind (sock, (struct sockaddr*)&server, sockaddr_len)) == -1)
{
perror ("bind");
exit (-1);
}
printf("after bind");
if ((listen(sock, MAX_CLIENTS)) == ERROR)
{
perror ("listen");
exit (-1);
}
printf("after listen");
while(1)
{
if ((new = accept(sock, (struct sockaddr*)&client, &sockaddr_len)) == ERROR)
{
perror ("accept");
exit (-1);
}
printf("after new");
printf("New client connected from port no %d and IP %s\n",ntohs(client.sin_port), inet_ntoa(client.sin_addr));
data_len = 1;
while (data_len)
{
data_len = recv (new, data, MAX_DATA, 0);
if (data_len)
{
send (new, data, data_len, 0) ;
data [data_len]='\0';
printf("Sent mesg: %s", data);
}
printf("after datalen");
}
printf("Client Disconnected\n");
close(new);
}
printf("after close new");
close (sock);
}
Your program is single-threaded, and only does one thing at a time. When you have accepted a socket connection from a client (in your outer while loop) you start communicating with that client (in your inner while loop), and you don't get back to the accept call until the first client has disconnected.
Either use threads, with one thread that waits for new connections and one additional thread for each client, waiting for input from that client, or use the select call, which lets you wait for input simultaneously from several different sources.
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);
I have a simple tcp based client and server written in c, but once I establish a connection after the server sends a message back to the client, the connection stops working. Could you help me fix this - I would like the server to continue receiving/ sending messages to the client.
Code:
Server.c
#include <stdio.h>
#include <errno.h>
#include <sys/socket.h>
#include <resolv.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <unistd.h>
#define port 5000
#define buf 512
void answer(int);
int main(int argc, char *argv[]){
int sock = socket(AF_INET, SOCK_STREAM, 0);
int portid;
int backlog = 10;
struct sockaddr_in addr;
char BUFF[buf];
int n;
int received = 0;
int connfd;
if(sock == -1){
perror("socket error");
exit(errno);
}
memset(&addr,0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = INADDR_ANY;
if(bind(sock, (struct sockaddr*) &addr, sizeof(addr)) == -1){
perror("bind error");
exit(errno);
}
if(listen(sock, backlog) == -1){
perror("listen error");
exit(errno);
}
listen(sock, 5);
struct sockaddr_in cliaddr;
//struct sockaddr_in addr;
int cliaddr_len = sizeof(cliaddr);
while(1){
connfd = accept(sock, (struct sockaddr*)&cliaddr,&cliaddr_len);
if(connfd == -1){
perror("error connfd");
exit(errno);
}
portid = fork();
if(portid<0)
perror("error on fork");
if(portid ==0){
close(sock);
answer(connfd);
exit(0);
}
//close(sock);
}
close(connfd);
return 0;
//close(sock);
//return 0;
}
void answer(int connfd){
int n;
char BUFF[buf];
memset(BUFF,buf,buf);
n = read(connfd,BUFF,buf);
printf("connected: %s",BUFF);
//received = 1;
//memset(BUFF,0,buf);
printf("please enter your message: ");
memset(BUFF,0,buf);
fgets(BUFF,buf, stdin);
n = write(connfd, BUFF,strlen(BUFF));
if(n<0) err("writing to socket problem");
}
Your server is calling fork, then if(portid ==0){...get and send message...}, then it loops back to the top of the loop where it tries to accept() --- not the job of the child, but the child is trying to do that anyway.
Refactor your code to do what you want. Break it into smaller parts that are simple for you to inspect so you can see things like this.
This is because you recv() data just one time. Use a loop to receive until the client disconnects.
while ((n = read(connfd,BUFF,buf))
{
printf("connected: %s",BUFF);
//received = 1;
//memset(BUFF,0,buf);
printf("please enter your message: ");
memset(BUFF,0,buf);
fgets(BUFF,buf, stdin);
n = write(connfd, BUFF,strlen(BUFF));
if(n<0) err("writing to socket problem");
}
You are reading the data in your child, so once the child is done the program exits, so try to read in the parent and send the data along to the child.