C TCP Server doesn't send data before closing - c

I'm trying to set up a TCP connection between two Ubuntu computers.
The server is supposed to send single char-values to the client. The client is supposed to print these chars.
Establishing the connection between server and client seems to work just as expected but when I call send(), there is no output on the clientside.
The only way to achieve an output is by either sending the same char-value in an infinite loop, which leads to an infinite number of chars printed in the client's console, or by changing the if statement in the client's code (see code below) from if(len > 0) to if(len >= 0). In this case the sent chars will be printed correctly as soon as I close the server via CTRL + C, but it will also print the last transmitted char multiple times. So I'm not able to receive the values on clientside while the server is still running.
This is the server's code:
int serverSocket = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in adresse;
adresse.sin_family = AF_INET;
adresse.sin_addr.s_addr = INADDR_ANY;
adresse.sin_port = htons (2457);
bind ( serverSocket, (struct sockaddr *) &adresse, sizeof (adresse))
listen (serverSocket, 1);
while(1)
{
socklen_t adrlen = sizeof(struct sockaddr_in);
int conSocket= accept ( serverSocket, (struct sockaddr *) &adresse, &adrlen );
if(conSocket => 0)
{
char charToSend;
while(1)
{
charToSend = returnChar();
if(send(conSocket= , &charToSend, 1, 0) == -1)
{
printf("Error\n");
}
}
close(conSocket);
}
close(serverSocket);
}
This is the client's code:
int clientSocket = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in adresse;
adresse.sin_family = AF_INET;
adresse.sin_port = htons (2457);
inet_aton ("192.168.2.101", &adresse.sin_addr);
if (connect ( clientSocket, (struct sockaddr *) &adresse, sizeof (adresse)) == 0)
{
while(1)
{
char recvd[2];
int len = recv(clientSocket, recvd, 1, 0);
recvd[1] = '\0';
if(len > 0)
{
printf("%s", recvd);
}
}
}
The function returnChar() in the server's code is used to process information from a sensor and returns a char value. It won't terminate before a complete signal has been processed and is running a loop in the meantime.
My only idea is that send() "doesn't have the time" to send out the values before the programm continues in another loop. Could this be the problem? Or did I do something wrong in the client's code?

In my opinion the problem is on the client side. You print the char with no line termination. Therefore everything is buffered in the client stdout stream. Please add '\n' to the printf as: printf("%s\n", recvd);

Actually, the problem is probably on the client side.
Make your client's output unbuffered:
setvbuf(stdout, NULL, _IONBF, (size_t)0);
or simply use unbuffered, rather than standard (buffered) I/O:
write(fileno(stdout), recvd, (size_t)1);
Although, since your traffic is 1-way, the suggestion to use TCP_NODELAY will help speed the sending a bit.

Related

How to handle TCP client disconnect in C

I am trying to write a basic TCP server that streams serial data to a client. The server would connect to a serial device, read data from said device, and then transmit it as a byte stream to the client. Writing the TCP server is no problem. The issue is that the server will crash when a client disconnects. In other languages, like Python, I can simply wrap the write() statement in a try-catch block. The program will try to write to the socket, but if the client has disconnected then an exception will be thrown. In another project, this code snippet worked for me:
try:
client_socket.send(bytes(buf, encoding='utf8'))
except Exception as e:
logger.info("Client disconnected: %s", client_id)
I can handle client disconnects in my C code, but only by first reading from the socket and checking if the read is equal to 0. If it is, then my client has disconnected and I can carry on as usual. The problem with this solution is that my client has to ping back to the server after every write, which is less than ideal.
Does anyone know how to gracefully handle TCP client disconnects in C? My example code is shown below. Thank you!
// Define a TCP socket
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
// Allow for the backlog of 100 connections to the socket
int backlog = 100;
// Supply a port to bind the TCP server to
short port = 9527;
// Set up server attributes
struct sockaddr_in servaddr;
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
// Set the socket so that we can bind to the same port when we exit the program
int flag = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1) {
perror("setsockopt fail");
}
// Bind the socket to the specified port
int res = bind(sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
if (res < 0) {
perror("bind fail");
exit(1);
}
// Listen for incoming connections
if (listen(sockfd, backlog) == -1) {
perror("listen fail");
exit(1);
} else {
printf("Server listening on port\n", port);
}
for(;;) {
// Wait for incoming connection
struct sockaddr_in cliaddr;
socklen_t len = sizeof(cliaddr);
int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &len);
if (-1 == connfd) {
perror("Could not accept incoming client");
continue;
}
//Resolving Client Address
char buff[INET_ADDRSTRLEN + 1] = {0};
inet_ntop(AF_INET, &cliaddr.sin_addr, buff, INET_ADDRSTRLEN);
uint16_t cli_port = ntohs(cliaddr.sin_port);
printf("connection from %s, port %d\n", buff, cli_port);
for(;;) {
// Read from serial device into variable here, then send
if(send(connfd, "Data...Data...Data\n", 19, 0) < 0) {
printf("Client disconnected...\n");
break;
}
}
}
Looks like a duplicate of this, this and this.
Long story short you can't detect the disconnection until you perform some write (or read) on that connection. More exactly, even if it seems there is no error returned by send, this is not a guarantee that this operation was really sent and received by the client. The reason is that the socket operations are buffered and the payload of send is just queued so that the kernel will dispatch it later on.
Depending on the context, the requirements and the assumptions you can do something more.
For example, if you are under the hypothesys that you will send periodic message at constant frequency, you can use select and a timeout approach to detect an anomaly.
In other words if you have not received anything in the last 3 minutes you assume that there is an issue.
As you can easily found, this and this are a good read on the topic.
Look at that for a far more detailed explanation and other ideas.
What you call the ping (intended as a message that is sent for every received packet) is more similar to what is usually known as an ACK.
You only need something like that (ACK/NACK) if you also want to be sure that the client received and processed that message.
Thanks to #emmanuaf, this is the solution that fits my project criteria. The thing that I was missing was the MSG_NOSIGNAL flag, referenced here.
I use Mashpoe's C Vector Library to create a new vector, which will hold all of my incoming client connections.
int* client_array = vector_create();
I then spawn a pthread that continually reads from a serial device, stores that data in a variable, and then sends it to each client in the client list
void* serve_clients(int *vargp) {
for(;;) {
// Perform a microsleep
sleep(0.1);
// Read from the Serial device
// Get the size of the client array vector
int client_vector_size = vector_size(vargp);
for(int i = 0 ; i < client_vector_size ; i++) {
// Make a reference to the socket
int* conn_fd = &vargp[i];
/*
In order to properly handle client disconnects, we supply a MSG_NOSIGNAL
flag to the send() call. That way, if the client disconnects, we will
be able to detect this, and properly remove them from the client list.
Referenced from: https://beej.us/guide/bgnet/html//index.html#sendman
*/
if (send(*conn_fd, "Reply from server\n", 18, MSG_NOSIGNAL) < 0) {
printf("Client disconnected...\n");
// Close the client connection
close(*conn_fd);
// Remove client socket from the vector
vector_remove(vargp, i);
// Decrement index and client_server_size by 1
i--;
client_vector_size--;
}
}
}
}
To spawn the pthread:
// Spawn the thread that serves clients
pthread_t serving_thread;
pthread_create(&serving_thread, NULL, serve_clients, client_array);
When a new connection comes in, I simply add the new connection to the client vector
while(1) {
// Wait for incoming connection
struct sockaddr_in cliaddr;
socklen_t len = sizeof(cliaddr);
int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &len);
if (-1 == connfd) {
perror("Could not accept incoming client");
continue;
}
//Resolving Client Address
char buff[INET_ADDRSTRLEN + 1] = {0};
inet_ntop(AF_INET, &cliaddr.sin_addr, buff, INET_ADDRSTRLEN);
uint16_t cli_port = ntohs(cliaddr.sin_port);
printf("connection from %s:%d -- Connfd: %d\n", buff, cli_port, connfd);
// Add client to vector list
vector_add(&client_array, connfd);
}
In the end, we have a TCP server that can multiplex data to many clients, and handle when those clients disconnect.

Creating Multiple Client and One Server for UDP application using select() system call with C

Help me to correct this program I want to create a server that can run indefinitely and can serve many clients and for that, I am using a select system call for UDP (User Datagram Protocol) application.
My issue is that this code is running for multiple clients in a different session that means after running for one client it stops and again when I start the server again then it can serve another client also.
I want my code to work indefinitely in one session only and serve as many clients as I want.
Server Code:-
'''
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define port1 8080
#define MAXN 1024
#define TRUE 1
int main(){
int sockfd,sockfd1;
struct sockaddr_in servaddr,cliaddr;
char buffer[MAXN];
char buff[MAXN];
int max_clients=2,valread,new_socket;
char *hello = "Hello Client";
char *message = "hiiii Server";
struct timeval timeout;
timeout.tv_sec = 20;
timeout.tv_usec = 0;
//create socket 2
sockfd = socket(AF_INET, SOCK_DGRAM,0);
if(sockfd<0){
perror("Error Creating Socket0");
exit(1);
}
//memset(&servaddr, 0, sizeof(servaddr));
//memset(&cliaddr, 0, sizeof(cliaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(port1);
servaddr.sin_addr.s_addr = INADDR_ANY;
if(bind(sockfd, (const *)&servaddr, sizeof(servaddr))<0){
perror("Error in binding0 ");
exit(1);
}
//Use Select......
//I have created 2 socket having file deescriptor sockfd and sockfd1
int s;
int client_socket[2]={0,0};
fd_set readfds;
while(){
FD_ZERO(&readfds);
FD_SET(sockfd,&readfds);
//Let's say sockfd is max_fd
int max_fd = sockfd,sd,activity;
for(int i=0;i<2;i++){
sd = client_socket[i];
if(sd>0){
FD_SET(sd,&readfds);
}
if(sd>max_fd)
max_fd = sd;
}
activity = select( max_fd + 1 , &readfds , NULL , NULL , &timeout);
if ((activity < 0))
{
printf("select error");
}
int addrlen = sizeof(servaddr);
//If something happened on the master socket ,
//then its an incoming connection
if (FD_ISSET(sockfd, &readfds))
{
//inform user of socket number - used in send and receive commands
printf("New connection , socket fd is %d , ip is : %s , port : %d\n " , new_socket , inet_ntoa(servaddr.sin_addr) , ntohs
(servaddr.sin_port));
//send new connection greeting message
if( send(new_socket, message, strlen(message), 0) != strlen(message) )
{
perror("send");
}
puts("Welcome message sent successfully");
//add new socket to array of sockets
for (int i = 0; i < max_clients; i++)
{
//if position is empty
if( client_socket[i] == 0 )
{
client_socket[i] = new_socket;
printf("Adding to list of sockets as %d\n" , i);
break;
}
}
}
//else its some IO operation on some other socket
for (int i = 0; i < max_clients; i++)
{
sd = client_socket[i];
if (FD_ISSET( sd , &readfds))
{
//Check if it was for closing , and also read the
//incoming message
if ((valread = read( sd , buffer, 1024)) == 0)
{
//Somebody disconnected , get his details and print
getpeername(sd , (struct sockaddr*)&servaddr , \
(socklen_t*)&addrlen);
printf("Host disconnected , ip %s , port %d \n" ,
inet_ntoa(servaddr.sin_addr) , ntohs(servaddr.sin_port));
//Close the socket and mark as 0 in list for reuse
//close( sd );
client_socket[i] = 0;
}
//Echo back the message that came in
else
{
//set the string terminating NULL byte on the end
//of the data read
buffer[valread] = '\0';
send(sd , buffer , strlen(buffer) , 0 );
}
}
}
}
return 0;
}
'''
This is one of the Client Code and this code is running :-
'''
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#define PORT 8080
#define MAXN 1024
// Driver code
int main() {
int sockfd;
char buffer[MAXN];
char *hello = "Hello from Multipleclient";
struct sockaddr_in servaddr;
// Creating socket file descriptor
if ( (sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("Error in socket creation");
exit(1);
}
memset(&servaddr, 0, sizeof(servaddr));
// Filling server information
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(PORT);
servaddr.sin_addr.s_addr = INADDR_ANY;
int n, len;
sendto(sockfd, (const char *)hello, strlen(hello),
MSG_CONFIRM, (const struct sockaddr *) &servaddr,
sizeof(servaddr));
printf("Hello message sent.\n");
n = recvfrom(sockfd, (char *)buffer, MAXN,
MSG_WAITALL, (struct sockaddr *) &servaddr,
&len);
buffer[n] = '\0';
printf("Server : %s\n", buffer);
close(sockfd);
return 0;
}
Some minor things to start with:
In the client code, you do not initialize len before passing it to recvfrom. Also, it is of the wrong type (int instead of socklen_t). This can be corrected by defining len as socklen_t len = sizeof(servaddr); However, since you do not use the length or address anywhere, you can just pass in NULL for both. Also, UDP does not support MSG_WAITALL, so pass 0 instead for the flags. recvfrom(sockfd, (char *)buffer, MAXN, 0, NULL, NULL).
In the client code, you fill in the server IP address as IPADDR_ANY, i.e. 0.0.0.0, which is only valid when calling bind. You are calling sendto. When specifying a destination address, you probably want IPADDR_LOOPBACK for the local computer.
In the server code, the main while() loop is missing a condition inside the parenthesis, so the code does not even compile. You probably want while(1) for an infinite loop.
In the server code, in the call to bind, you are casting to the wrong type. (const *) means (const int *), but you want (const struct sockaddr *).
The biggest problem:
Based on the comments and code in the server code, you seem to think that UDP communication has persistent connections, and try to save client_socket values for these. However, UDP does not have persistent connection. There is no such thing as a client socket in this context. select does not tell you that a new "connection" is ready. Instead, it tells you that data has been received on your one and only UDP socket (sockfd). This is entirely unlike TCP, where you accept a connection that lasts until one side terminates it.
From the comments in the server code, it seems that you intend for clients to have persistent state in the server, at least enough to be recognized when they send more datagrams (otherwise they would always get the welcome message).
A possible way to make this work is the following:
// create and bind a UDP socket as you already do;
// initialize and empty list of clients;
while(1) {
struct sockaddr_in cliaddr;
socklen_t cliaddr_len = sizeof(cliaddr);
recvfrom(sockfd, buffer, bufferlen, /*flags*/ 0, (struct sockaddr *)&cliaddr, &cliaddr_len);
// check whether cliaddr is in your list;
if (!is_in_list) {
// this is a new client
// add to list of clients;
// send welcome message;
}
// do something else, maybe echo back the buffer contents;
}
You will notice that the code does not actually use select anymore. select is unnecessary if you have nothing to do while waiting, and you only listen to one socket. If you are doing other stuff not shown in the question, you can add the select back at the top of the infinite loop, and only handle the UDP socket if sockfd is set in the readfds from select.
Also note that the client cannot actually receive answers from the server, since there is no persistent connection, and the client has not passed any socket to bind. And also note that UDP messages may get lost without warning, and you should make sure your code can deal with that. It seems that this client-server-strategy would be easier to implement using TCP.
As a side note, it is strongly recommended to enable warnings in your compiler. That would have caught the wrong type for the address length variables, as well as told you that new_socket is used but never set. It is polite to fix all warnings before posting a question, unless of course the question is about the warning.

The accept() function in the server doesn't seem to accept new connection from the client

I was trying the code from UNIX Network Programming, the socket networking API
Where I encountered a the code for a simple server client and server based example
the code for server is
#define BL 10
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET)
{
return &(((struct sockaddr_in*) sa)->sin_addr);
}
return &(((struct sockaddr_in6*) sa)->sin6_addr);
}
int main(int argc, char ** argv)
{
struct sockaddr_in server, client;
int listenfd;
int port;
if (argc < 2)
{
printf("Port Not specified Error ~~ ! ");
exit(0);
}
port = atoi(argv[1]);
memset(&server, 0, sizeof(server));
server.sin_family = AF_INET;
server.sin_addr.s_addr = htons(INADDR_ANY);
server.sin_port = htons(port);
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (bind(listenfd, (struct sockaddr *) &server, sizeof(server)) == 0)
printf("\n [~] Bind Sucessfull \n");
if (listen(listenfd, BL) == 0)
printf("\n [~] Listenig on port %d \n\n ", port);
while (1)
{
socklen_t clientlen = sizeof(client);
printf("%d", clientlen);
int conffd = accept(listenfd, (struct sockaddr *) &client, &clientlen);
char s[INET6_ADDRSTRLEN];
inet_ntop(AF_INET, get_in_addr((struct sockaddr *) &client), s, sizeof(s));
if (conffd == -1)
printf("\n not Accepted \n");
else
{
printf("Got a connection from %s", s);
}
}
return 0;
}
and then the code for the client is
int main(int argc, char ** argv)
{
struct sockaddr_in server;
int listenfd;
int port = 11111;
memset(&server, 0, sizeof(server));
inet_pton(AF_INET, argv[1], &server.sin_addr);
server.sin_family = AF_INET;
server.sin_port = htons(port);
listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (connect(listenfd, (struct sockaddr *) &server, sizeof(server)) != 0)
{
printf("Connection error ! \n");
}
return 0;
}
The problem here is when I start the server and run the client , nothing happens in the server but its supposed to say " got a connection from #IP "
the output is :
./server 11111
[~] Bind Sucessfull
[~] Listenig on port 11111
the should have been output :
./server 11111
[~] Bind Sucessfull
[~] Listenig on port 11111
Got an connection from 127.0.0.1
So I thought there was some thing wrong with the client ,
so I checked it , I closed the server and ran the client again now it said connection error
which means that the client is working fine , so the problem was with server's accept()
I can't seem to find whats causing the problem .
If anyone could explain whats wrong would be really helpfull :)
As a follow up of the comments above, you should know that the standard output stdout is line buffered, which means, it will send the contents of the buffer to the output screen only upon encountering a newline \n. So until and unless your print string is having a newline [or you're flushing the buffer manually, using fflush(stdout)], the content of the buffer will not be flushed to the screen.
In your code, upon a successful accept(), printf() is called and the string supplied with printf() is send to the buffer related with stdout, but the content is not flushed to the screen. The control will continue and it will be in blocked state in next accept() call. So, your output buffer is not getting a chance to be flushed to the screen in your case.
Please add a newline character [\n] in your print string. It will cause the content of the buffer to be flushed immediately and you'll be able to see the output on screen.
printf("Got a connection from %s\n", s);
SideNote: [Just for checking the presence of the content in your output buffer] As we know, the program termination will cause all the associated buffers to be flushed, you can try terminating the server execution manually, [maybe by pressing crtl+c] and you'll be able to see the print statement on your screen
Change the line given below in server code
server.sin_addr.s_addr = htons(INADDR_ANY);
to
server.sin_addr.s_addr = INADDR_ANY;

send() and sendto() blocking in a file transfer program

it seems that when i use send() function (in a TCP file transfer program) like this
while((count = recv(socketConnection, buff, 100000, 0))>0)
myfile.write(buff,count);
the function recv() just waits untill the whole data comes and exits the loop when it is no more receiving any data but in a similar program for a UDP program
while((n = recvfrom(sockfd,mesg,1024,0,(struct sockaddr *)&cliaddr,&len))>0)
myfile.write(mesg,n);
the recvfrom() function just blocks and does not exit the loop for some reason, as far as i know both recv() and recvfrom() are blocking right?? Then why the difference. Does it have something to do with the functions or just the nature of TCP,UDP(which i guess is not a reason)??
P.S. Please help me understand this guys, I'm a newbie to socket programming and networking.
EDIT: full server program for both TCP and UDP
UDP server (with recvfrom() )
int i=0;
int sockfd,n;
struct sockaddr_in servaddr,cliaddr;
socklen_t len;
char mesg[1024];
sockfd=socket(AF_INET,SOCK_DGRAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(32000);
bind(sockfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
ofstream myfile;
// fcntl(sockfd,F_SETFL,O_NONBLOCK);
myfile.open("2gb",ios::out);
while((n = recvfrom(sockfd,mesg,1024,0,(struct sockaddr *)&cliaddr,&len))>0)
myfile.write(mesg,n);
TCP (recv() ) server program
struct sockaddr_in socketInfo;
char sysHost[MAXHOSTNAME+1]; // Hostname of this computer we are running on
struct hostent *hPtr;
int socketHandle;
int portNumber = 8070;
//queue<char*> my_queue;
bzero(&socketInfo, sizeof(sockaddr_in)); // Clear structure memory
gethostname(sysHost, MAXHOSTNAME); // Get the name of this computer we are running on
if((hPtr = gethostbyname(sysHost)) == NULL)
{
cerr << "System hostname misconfigured." << endl;
exit(EXIT_FAILURE);
}
if((socketHandle = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
close(socketHandle);
exit(EXIT_FAILURE);
}
// std::cout<<"hi starting server";
socklen_t optlen;
int rcvbuff=262144;
optlen = sizeof(rcvbuff);
socketInfo.sin_family = AF_INET;
socketInfo.sin_addr.s_addr = htonl(INADDR_ANY);
socketInfo.sin_port = htons(portNumber); // Set port number
if( bind(socketHandle, (struct sockaddr *) &socketInfo, sizeof(socketInfo)) < 0)
{
close(socketHandle);
perror("bind");
exit(EXIT_FAILURE);
}
listen(socketHandle, 1);
int socketConnection;
if( (socketConnection = accept(socketHandle, NULL, NULL)) < 0)
{
exit(EXIT_FAILURE);
}
close(socketHandle);
time_start(boost::posix_time::microsec_clock::local_time());
int rc = 0; // Actual number of bytes read
int count=0;
char *buff;
int a=100000;
buff=new char[a];
ofstream myfile;
myfile.open("345kb.doc",ios::out|ios::app);
if(myfile.is_open())
{
long i=0;
while((count = recv(socketConnection, buff, 100000, 0))>0)
{
myfile.write(buff,count);
}}
the function recv() just waits untill the whole data comes and exits the loop when it is no more receiving any data
recv() on a TCP connection returns 0 when the sending side has closed the connection and this is the condition for your loop to terminate.
for a UDP program the recvfrom() function just blocks and does not exit the loop for some reason,
Because UDP is a connection-less protocol hence there is no special return code from recv() for a closed UDP connection. Unless someone sends you a 0-length datagram.
recv() will end the loop because at the other side the socket is closed, so recv() will return 0 (socket gracefully closed) whereas, recvfrom that does not have that signal, it does not know about closing, because it's an unconnected socket. It's stay there until it receives a packet or timeout, with UDP you need a way to tell that the communication is over (finish).

UDP Socket Programming in C: 2 Clients and 1 Server

Im very new to network programming. I have a UDP client/server that sends a message to a server in lower or uppercase. The server receives the message and relays it back with switches cases. I cant figure out how I would instead of relaying it back to the first client, sending it to client2. heres my code.
Server:
/*
Simple udp server
*/
#include<stdio.h> //printf
#include<string.h> //memset
#include<stdlib.h> //exit(0);
#include<arpa/inet.h>
#include<sys/socket.h>
#include<ctype.h>
#define BUFLEN 512 //Max length of buffer
#define PORT 8888 //The port on which to listen for incoming data
void die(char *s)
{
perror(s);
exit(1);
}
int main(void)
{
struct sockaddr_in si_me, si_other, si_other2;
int s, i, slen = sizeof(si_other) , recv_len;
char buf[BUFLEN];
//create a UDP socket
if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1)
{
die("socket");
}
// zero out the structure
memset((char *) &si_me, 0, sizeof(si_me));
si_me.sin_family = AF_INET;
si_me.sin_port = htons(PORT);
si_me.sin_addr.s_addr = htonl(INADDR_ANY);
//bind socket to port
if( bind(s , (struct sockaddr*)&si_me, sizeof(si_me) ) == -1)
{
die("bind");
}
//keep listening for data
while(1)
{
printf("Waiting for data...");
fflush(stdout);
//try to receive some data, this is a blocking call
if ((recv_len = recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen)) == -1) // read datagram from server socket
{
die("recvfrom()");
}
//print details of the client/peer and the data received
printf("Received packet from %s:%d\n", inet_ntoa(si_other.sin_addr), ntohs(si_other.sin_port)); printf("Data: %s\n" , buf);
//now reply to server socket/the client with the same data
if (sendto(s, buf, recv_len, 0, (struct sockaddr*) &si_other, slen) == -1)
{
die("sendto()");
}
}
close(s);
return 0;
}
The Client:
/*
Simple udp client
*/
#include<stdio.h> //printf
#include<string.h> //memset
#include<stdlib.h> //exit(0);
#include<arpa/inet.h>
#include<sys/socket.h>
#include<ctype.h>
#define SERVER "192.x.x.x"
#define BUFLEN 512 //Max length of buffer
#define PORT 8888 //The port on which to send data
void die(char *s)
{
perror(s);
exit(1);
}
int main(void)
{
struct sockaddr_in si_other;
int s, s2, i, slen=sizeof(si_other);
char buf[BUFLEN];
char message[BUFLEN];
if ( (s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) // create a client socket
{
die("socket");
}
memset((char *) &si_other, 0, sizeof(si_other));
si_other.sin_family = AF_INET;
si_other.sin_port = htons(PORT);
if (inet_aton(SERVER , &si_other.sin_addr) == 0) // Create datagram with server IP and port.
{
fprintf(stderr, "inet_aton() failed\n");
exit(1);
}
while(1)
{
printf("Enter message : ");
gets(message);
int a;
char message2[BUFLEN];
for(a=0;a<=BUFLEN-1;a++)
{
if(message[a] >= 97 && message[a] <= 122)
message2[a] = toupper(message[a]);
else
message2[a] = tolower(message[a]);
}
if (sendto(s, message2, strlen(message2) , 0 , (struct sockaddr *) &si_other, slen)==-1)
{
die("sendto()");
}
//receive a reply and print it
//clear the buffer by filling null, it might have previously received data
memset(buf,'\0', BUFLEN);
//try to receive some data, this is a blocking call
if (recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen) == -1) // read datagram from client socket
{
die("recvfrom()");
}
puts(buf);
}
close(s);
return 0;
}
Since this has 21K views with no explicit answer, and it is basic understanding of coding for UDP.. I will give it some love.
As mentioned in the comments already: In your server code, you receive a message from the client using:
recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &si_other, &slen))
The result of this function is that the message data will be written into buf and the ip address and port number of the socket that sent the message will be filled into si_other (which must be of type struct sockaddr_in).
So then, when you send a response using:
sendto(s, buf, recv_len, 0, (struct sockaddr*) &si_other, slen)
Since si_other, which you are passing as the destination address for sendto, contains the ip/port of the last message you got, the response will always go back to the sender of the last message you got. In many server applications this is a pretty common scenario: you get a request from somewhere, you send a response back to the same place.
If you want the message to go someplace else, other than the process that sent you the request, then you need to create a different struct sockaddr_in variable which contains the ip address and port of where you would like the message to go.
And in your client code, you already have the code that does that, eg (cleaned up a bit):
struct sockaddr_in si_client2;
memset((char *) &si_client2, 0, sizeof(si_client2));
si_client2.sin_family = AF_INET;
si_client2.sin_port = htons(CLIENT2_PORT);
if(inet_aton(CLIENT2_HOST, &si_client2.sin_addr) == 0)
perror("inet_aton");
So now, if you use si_client2 in your sendto(), the packet will go to that client.
Because it's UDP, delivery is not guaranteed. If there is a process listening for UDP at that ip address, on that port number, then (if no network error occurs) it will get the message. If not, nothing will happen.. your message vanishes into the void.
Keep in mind that if client 1 and client 2 are both running on the same machine, they will need to use different port numbers, because every destination (whether in a client or server role) must have a unique combination of IP and PORT.
Now, in real life applications, it is rare that a server will ever know the IP and PORT of its clients ahead of time.. usually clients will not use fixed port numbers but instead use "ephemeral ports".. port numbers which the operating system assigns at run-time. Whereas, the client will often be configured with the IP and port of the server.
So, in most cases, you would have some code in the server that keeps a list of client addresses.. perhaps a simple messaging service would keep a list of the last 100 clients it got messages from... But how this is actually done would be dictated by the needs of the application. For a simple exercise like this one, you can just hard code the addresses as I said...
The bottom line is that, to send a UDP packet to a specific destination, the sender must know the IP and PORT of that specific destination. And the only way to know that is either to have some configuration data, or for someone (such as the destination) to send a packet ahead of time letting you know of its existence. Just keep in mind that with a UDP socket you can get a message from anywhere, and you'll be given the ip/port along with the message. If you need to send a message, you need to know the ip/port of where you want to send it. It's your applications problem to figure out how it's going to get that information and where to store that information for later use.

Resources