NETWORK PROGRAMMING SOCKET PART 10 MORE TCP & UDP CLIENT & SERVER PROGRAM EXAMPLES - JohnHau/mis GitHub Wiki

This is a continuation from Part II series,Socket Part 9. Working program examples if any compiled using gcc, tested using the public IPs, run on Linux Fedora 3 with several times update, as normal user. The Fedora machine used for the testing having the "No Stack Execute" disabled and the SELinux set to default configuration. All the program example is generic. Beware codes that expand more than one line. Have a nice ride lol!

This Module will cover the following sub-topics:

Example: select() server Connecting a TCP server and client: Example: Connecting a TCP server to a client, a server program Example: Connecting a TCP client to a server, a client program UDP connectionless client/server Connecting a UDP server and client: Example: Connecting a UDP server to a client, a server program Example: Connecting a UDP client to a server, a client program Connection-oriented server designs: Iterative server spawn() server and spawn() worker sendmsg() server and recvmsg() worker Multiple accept() servers and multiple accept() workers Example: Writing an iterative server program Example: Connection-oriented common client Example: Sending and receiving a multicast datagram Example: Sending a multicast datagram, a server program Example: Receiving a multicast datagram, a client Example: The select() server

The following program example acts like a simple multi-user chat server. Start running it in one window, then telnet to it ("telnet hostname 2020") from other windows.

When you type something in one telnet session, it should appear in all the others windows.

/ select.c**/

/ *******Using select() for I/O multiplexing */

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

/* port we're listening on */

#define PORT 2020

int main(int argc, char *argv[])

{

/* master file descriptor list */

fd_set master;

/* temp file descriptor list for select() */

fd_set read_fds;

/* server address */

struct sockaddr_in serveraddr;

/* client address */

struct sockaddr_in clientaddr;

/* maximum file descriptor number */

int fdmax;

/* listening socket descriptor */

int listener;

/* newly accept()ed socket descriptor */

int newfd;

/* buffer for client data */

char buf[1024];

int nbytes;

/* for setsockopt() SO_REUSEADDR, below */

int yes = 1;

int addrlen;

int i, j;

/* clear the master and temp sets */

FD_ZERO(&master);

FD_ZERO(&read_fds);

/* get the listener */

if((listener = socket(AF_INET, SOCK_STREAM, 0)) == -1)

{

perror("Server-socket() error lol!");

/just exit lol!/

exit(1);

}

printf("Server-socket() is OK...\n");

/*"address already in use" error message */

if(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)

{

perror("Server-setsockopt() error lol!");

exit(1);

}

printf("Server-setsockopt() is OK...\n");

/* bind */

serveraddr.sin_family = AF_INET;

serveraddr.sin_addr.s_addr = INADDR_ANY;

serveraddr.sin_port = htons(PORT);

memset(&(serveraddr.sin_zero), '\0', 8);

if(bind(listener, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) == -1)

{

perror("Server-bind() error lol!");

exit(1);

}

printf("Server-bind() is OK...\n");

/* listen */

if(listen(listener, 10) == -1)

{

 perror("Server-listen() error lol!");

 exit(1);

}

printf("Server-listen() is OK...\n");

/* add the listener to the master set */

FD_SET(listener, &master);

/* keep track of the biggest file descriptor */

fdmax = listener; /* so far, it's this one*/

/* loop */

for(;;)

{

/* copy it */

read_fds = master;

if(select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1)

{

perror("Server-select() error lol!");

exit(1);

}

printf("Server-select() is OK...\n");

/run through the existing connections looking for data to be read/

for(i = 0; i <= fdmax; i++)

{

if(FD_ISSET(i, &read_fds))

{ /* we got one... */

if(i == listener)

 {

     /* handle new connections */

    addrlen = sizeof(clientaddr);

if((newfd = accept(listener, (struct sockaddr *)&clientaddr, &addrlen)) == -1)

{

perror("Server-accept() error lol!");

}

else

{

printf("Server-accept() is OK...\n");

FD_SET(newfd, &master); /* add to master set */

if(newfd > fdmax)

{ /* keep track of the maximum */

fdmax = newfd;

}

printf("%s: New connection from %s on socket %d\n", argv[0], inet_ntoa(clientaddr.sin_addr), newfd);

}

}

else

{

/* handle data from a client */

if((nbytes = recv(i, buf, sizeof(buf), 0)) <= 0)

{

/* got error or connection closed by client */

if(nbytes == 0)

/* connection closed */

printf("%s: socket %d hung up\n", argv[0], i);

else

perror("recv() error lol!");

/* close it... */

close(i);

/* remove from master set */

FD_CLR(i, &master);

}

else

{

/* we got some data from a client*/

for(j = 0; j <= fdmax; j++)

{

/* send to everyone! */

if(FD_ISSET(j, &master))

{

   /* except the listener and ourselves */

   if(j != listener && j != i)

   {

          if(send(j, buf, nbytes, 0) == -1)

                 perror("send() error lol!");

   }

}

}

}

}

}

}

}

return 0;

}

Compile and link the program.

[bodo@bakawali testsocket]$ gcc -g select.c -o select

Run the program.

[bodo@bakawali testsocket]$ ./select

Server-socket() is OK...

Server-setsockopt() is OK...

Server-bind() is OK...

Server-listen() is OK...

You can leave the program running at the background (Ctrl + z).

[bodo@bakawali testsocket]$ ./select

Server-socket() is OK...

Server-setsockopt() is OK...

Server-bind() is OK...

Server-listen() is OK...

[1]+ Stopped ./select

[bodo@bakawali testsocket]$ bg

[1]+ ./select &

[bodo@bakawali testsocket]$

Do some verification.

[bodo@bakawali testsocket]$ ps aux | grep select

bodo 27474 0.0 0.2 1384 292 pts/2 S+ 14:32 0:00 ./select

bodo 27507 0.0 0.5 3724 668 pts/3 S+ 14:34 0:00 grep select

[bodo@bakawali testsocket]$ netstat -a |grep 2020

tcp 0 0 *:2020 : LISTEN

[bodo@bakawali testsocket]$

Telnet from other computers or windows using hostname or the IP address. Here we use hostname, bakawali. Use escape character ( Ctrl + ] ) to terminate command. For other telnet command please type help.

[bodo@bakawali testsocket]$ telnet bakawali 2020

Trying 203.106.93.94...

Connected to bakawali.jmti.gov.my (203.106.93.94).

Escape character is '^]'.

^]

telnet> mode line

testing some text

the most visible one

The last two messages were typed at another two machines that connected through socket 5 and 6 (socket 4 is another window of the server) using telnet. Socket 5 and 6 are from Windows 2000 Server machines.

The following are messages on the server console. There are another two machine connected to the server and the messages at the server console is shown below.

[bodo@bakawali testsocket]$ Server-select() is OK...

Server-accept() is OK...

./select: New connection from 203.106.93.94 on socket 4

Server-select() is OK...

...

Server-accept() is OK...

./select: New connection from 203.106.93.91 on socket 5

Server-select() is OK...

Server-select() is OK...

...

Server-select() is OK...

Server-select() is OK...

Server-accept() is OK...

./select: New connection from 203.106.93.82 on socket 6

When the clients disconnected from the server through socket 4, 5 and 6, the following messages appear on the server console.

...

Server-select() is OK...

Server-select() is OK...

./select: socket 5 hung up

Server-select() is OK...

./select: socket 6 hung up

Server-select() is OK...

./select: socket 4 hung up

There are two file descriptor sets in the code: master and read_fds. The first, master, holds all the socket descriptors that are currently connected, as well as the socket descriptor that is listening for new connections.

The reason we have the master set is that select() actually changes the set you pass into it to reflect which sockets are ready to read. Since we have to keep track of the connections from one call of select() to the next, we must store these safely away somewhere. At the last minute, we copy the master into the read_fds, and then call select().

Then every time we get a new connection, we have to add it to the master set and also every time a connection closes, we have to remove it from the master set.

Notice that we check to see when the listener socket is ready to read. When it is, it means we have a new connection pending, and we accept() it and add it to the master set. Similarly, when a client connection is ready to read, and recv() returns 0, we know that the client has closed the connection, and we must remove it from the master set.

If the client recv() returns non-zero, though, we know some data has been received. So we get it, and then go through the master list and send that data to all the rest of the connected clients.

Connecting a TCP server and client

The following program examples are connection-oriented where sockets use TCP to connect a server to a client, and a client to a server. This example provides more complete sockets’ APIs usage.

Example: Connecting a TCP server to a client, a server program

/tcpserver.c************/

/* header files needed to use the sockets API */

/*File contain Macro, Data Type and Structure etc */

/***********************************************/

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <sys/time.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <errno.h>

#include <unistd.h>

/* BufferLength is 100 bytes */

#define BufferLength 100

/* Server port number */

#define SERVPORT 3111

int main()

{

/* Variable and structure definitions. */

int sd, sd2, rc, length = sizeof(int);

int totalcnt = 0, on = 1;

char temp;

char buffer[BufferLength];

struct sockaddr_in serveraddr;

struct sockaddr_in their_addr;

fd_set read_fd;

struct timeval timeout;

timeout.tv_sec = 15;

timeout.tv_usec = 0;

/* The socket() function returns a socket descriptor */

/* representing an endpoint. The statement also */

/* identifies that the INET (Internet Protocol) */

/* address family with the TCP transport (SOCK_STREAM) */

/* will be used for this socket. */

/************************************************/

/* Get a socket descriptor */

if((sd = socket(AF_INET, SOCK_STREAM, 0)) < 0)

{

perror("Server-socket() error");

/* Just exit */

exit (-1);

}

else

printf("Server-socket() is OK\n");

/* The setsockopt() function is used to allow */

/* the local address to be reused when the server */

/* is restarted before the required wait time */

/* expires. */

/***********************************************/

/* Allow socket descriptor to be reusable */

if((rc = setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on))) < 0)

{

perror("Server-setsockopt() error");

close(sd);

exit (-1);

}

else

printf("Server-setsockopt() is OK\n");

/* bind to an address */

memset(&serveraddr, 0x00, sizeof(struct sockaddr_in));

serveraddr.sin_family = AF_INET;

serveraddr.sin_port = htons(SERVPORT);

serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);

printf("Using %s, listening at %d\n", inet_ntoa(serveraddr.sin_addr), SERVPORT);

/* After the socket descriptor is created, a bind() */

/* function gets a unique name for the socket. */

/* In this example, the user sets the */

/* s_addr to zero, which allows the system to */

/* connect to any client that used port 3005. */

if((rc = bind(sd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))) < 0)

{

perror("Server-bind() error");

/* Close the socket descriptor */

close(sd);

/* and just exit */

exit(-1);

}

else

printf("Server-bind() is OK\n");

/* The listen() function allows the server to accept */

/* incoming client connections. In this example, */

/* the backlog is set to 10. This means that the */

/* system can queue up to 10 connection requests before */

/* the system starts rejecting incoming requests.*/

/*************************************************/

/* Up to 10 clients can be queued */

if((rc = listen(sd, 10)) < 0)

{

perror("Server-listen() error");

close(sd);

exit (-1);

}

else

printf("Server-Ready for client connection...\n");

/* The server will accept a connection request */

/* with this accept() function, provided the */

/* connection request does the following: */

/* - Is part of the same address family */

/* - Uses streams sockets (TCP) */

/* - Attempts to connect to the specified port */

/***********************************************/

/* accept() the incoming connection request. */

int sin_size = sizeof(struct sockaddr_in);

if((sd2 = accept(sd, (struct sockaddr *)&their_addr, &sin_size)) < 0)

{

perror("Server-accept() error");

close(sd);

exit (-1);

}

else

printf("Server-accept() is OK\n");

/client IP/

printf("Server-new socket, sd2 is OK...\n");

printf("Got connection from the f***ing client: %s\n", inet_ntoa(their_addr.sin_addr));

/* The select() function allows the process to */

/* wait for an event to occur and to wake up */

/* the process when the event occurs. In this */

/* example, the system notifies the process */

/* only when data is available to read. */

/***********************************************/

/* Wait for up to 15 seconds on */

/* select() for data to be read. */

FD_ZERO(&read_fd);

FD_SET(sd2, &read_fd);

rc = select(sd2+1, &read_fd, NULL, NULL, &timeout);

if((rc == 1) && (FD_ISSET(sd2, &read_fd)))

{

/* Read data from the client. */

totalcnt = 0;

while(totalcnt < BufferLength)

{

/* When select() indicates that there is data */

/* available, use the read() function to read */

/* 100 bytes of the string that the */

/* client sent. */

/***********************************************/

/* read() from client */

rc = read(sd2, &buffer[totalcnt], (BufferLength - totalcnt));

if(rc < 0)

{

perror("Server-read() error");

close(sd);

close(sd2);

exit (-1);

}

else if (rc == 0)

{

printf("Client program has issued a close()\n");

close(sd);

close(sd2);

exit(-1);

}

else

{

totalcnt += rc;

printf("Server-read() is OK\n");

}

}

}

else if (rc < 0)

{

perror("Server-select() error");

close(sd);

close(sd2);

exit(-1);

}

/* rc == 0 */

else

{

printf("Server-select() timed out.\n");

close(sd);

close(sd2);

exit(-1);

}

/* Shows the data */

printf("Received data from the f***ing client: %s\n", buffer);

/* Echo some bytes of string, back */

/* to the client by using the write() */

/* function. */

/************************************/

/* write() some bytes of string, */

/* back to the client. */

printf("Server-Echoing back to client...\n");

rc = write(sd2, buffer, totalcnt);

if(rc != totalcnt)

{

perror("Server-write() error");

/* Get the error number. */

rc = getsockopt(sd2, SOL_SOCKET, SO_ERROR, &temp, &length);

if(rc == 0)

{

/* Print out the asynchronously */

/* received error. */

errno = temp;

perror("SO_ERROR was: ");

}

else

printf("Server-write() is OK\n");

close(sd);

close(sd2);

exit(-1);

}

/* When the data has been sent, close() */

/* the socket descriptor that was returned */

/* from the accept() verb and close() the */

/* original socket descriptor. */

/*****************************************/

/* Close the connection to the client and */

/* close the server listening socket. */

/******************************************/

close(sd2);

close(sd);

exit(0);

return 0;

}

Compile and link the program. Make sure there is no error.

[bodo@bakawali testsocket]$ gcc -g tcpserver.c -o tcpserver

Run the program. In this example we let the program run in the background.

[bodo@bakawali testsocket]$ ./tcpserver

Server-socket() is OK

Server-setsockopt() is OK

Using 0.0.0.0, listening at 3111

Server-bind() is OK

Server-Ready for client connection...

[1]+ Stopped ./tcpserver

[bodo@bakawali testsocket]$ bg

[1]+ ./tcpserver &

[bodo@bakawali testsocket]$

Do some verification.

[bodo@bakawali testsocket]$ ps aux | grep tcpserver

bodo 7914 0.0 0.2 3172 324 pts/3 S 11:59 0:00 ./tcpserver

bodo 7921 0.0 0.5 5540 648 pts/3 S+ 12:01 0:00 grep tcpserver

[bodo@bakawali testsocket]$ netstat -a | grep 3111

tcp 0 0 *:3111 : LISTEN

When the next program example (the TCP client) is run, the following messages should be expected at the server console.

[bodo@bakawali testsocket]$ Server-accept() is OK

Server-new socket, sd2 is OK...

Got connection from the f***ing client: 203.106.93.94

Server-read() is OK

Received data from the f***ing client: This is a test string from client lol!!!

Server-Echoing back to client...

[1]+ Done ./tcpserver

[bodo@bakawali testsocket]$

If the server program and then the client are run, the following messages should be expected at the server console.

[bodo@bakawali testsocket]$ ./tcpserver

Server-socket() is OK

Server-setsockopt() is OK

Using 0.0.0.0, listening at 3111

Server-bind() is OK

Server-Ready for client connection...

Server-accept() is OK

Server-new socket, sd2 is OK...

Got connection from the f***ing client: 203.106.93.94

Server-read() is OK

Received data from the f***ing client: This is a test string from client lol!!!

Server-Echoing back to client...

[bodo@bakawali testsocket]$

Just telneting the server.

[bodo@bakawali testsocket]$ telnet 203.106.93.94 3111

Trying 203.106.93.94...

Connected to bakawali.jmti.gov.my (203.106.93.94).

Escape character is '^]'.

^]

telnet> help

Commands may be abbreviated. Commands are:

close close current connection

logout forcibly logout remote user and close the connection

display display operating parameters

mode try to enter line or character mode ('mode ?' for more)

open connect to a site

quit exit telnet

send transmit special characters ('send ?' for more)

set set operating parameters ('set ?' for more)

unset unset operating parameters ('unset ?' for more)

status print status information

toggle toggle operating parameters ('toggle ?' for more)

slc change state of special charaters ('slc ?' for more)

auth turn on (off) authentication ('auth ?' for more)

encrypt turn on (off) encryption ('encrypt ?' for more)

forward turn on (off) credential forwarding ('forward ?' for more)

z suspend telnet

! invoke a subshell

environ change environment variables ('environ ?' for more)

? print help information

telnet>quit

Well, it looks that we have had a telnet session with the server.

Continue on next Module…TCP/IP and RAW socket, more program examples.

⚠️ **GitHub.com Fallback** ⚠️