Showing posts with label sockets. Show all posts
Showing posts with label sockets. Show all posts

Sunday, October 31, 2010

C Socket Server example, with semaphores, signal handling

This is a quick program that combines various examples from the GNU/Linux Application Programming book I have been studying.

The program is a BSD Socket based server (ch. 13), that accepts connections on a specific port and sends over TCP a "temperature" value, which at the moment I am simply randomly generating for example. I have added the use of counting semaphores (ch 17) to demonstrate a critical section of code as well as used POSIX signal handling (ch. 14) as a mechanism to cleanly exit. I developed this in Eclipse CDT IDE. I am not a C expert, this is my first C program since approx 2002 but it runs and compiles without warning!

Therm- ServerProject set up and dependencies
I created two static libraries for generating the random number/temperature reading. This are linked in my Eclipse project. For the purposes of this example you only need to know about these function prototypes (from thermapi.h)
/*
* Initialization for Therm API. Call before using any other function
*/
extern void initTherm( void );

/*
* Returns the current temperature reading from sensor
*/
extern float getTemp( void );

Other than this, I depend on various standard libraries in Linux for sockets, signal handling, etc..

thermserv.c - Includes and Defines.
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

/* sockets and addresses */
#include <sys.h>
#include <arpa.h>
#include <netinet.h>
#include <netinet.h>
#include <netinet.h>
#include <netinet.h>
#include <netdb.h>

/* semaphores */
#include <sys.h>

/* signal handling */
#include <sys.h>
#include <sys.h>
#include <signal.h>

/* my own includes for custom functions */
#include <thermapi.h>
#include "thermserv.h"

/* magic number constants */
#define MAX_BUFFER 128
#define THERM_SERVER_PORT 6777
#define THERM_SEM_ID 6778

static int loopCounter = 1;

/* Exit handler function called by sigaction */
void exitHandler(int sig, siginfo_t *siginfo, void *ignore) {
printf("*** Got %d signal from %d\n", siginfo->si_signo, siginfo->si_pid);
loopCounter = 0;

return;
}


Print Server Info utility method just to clean up the main function a little. It still is too long.
/*
* Prints out basic server information for startup
*/
void printTServerInfo(struct sockaddr_in servaddr, int cliLen,
time_t startupTime) {

printf("*** Thermo Server initialized.\n");
printf("*** cliLen size: %d\n", cliLen);
printf("*** Server Listening as %s:%d\n", inet_ntoa(servaddr.sin_addr),
ntohs(servaddr.sin_port));
printf("*** Server PID: %d\n", getpid());
printf("*** Start up time: %s", ctime(&startupTime));

}

Note: I was getting warnings from gcc for this function.

../thermserv.c: In function ‘main’:
../thermserv.c:122: warning: implicit declaration of function ‘printTServerInfo’
../thermserv.c: At top level:
../thermserv.c:180: warning: conflicting types for ‘printTServerInfo’
../thermserv.c:122: note: previous implicit declaration of ‘printTServerInfo’ was here

To resolve these, I added thermserv.h:

#ifndef THERMSERV_H_
#define THERMSERV_H_

/*
* Prints out basic server information for startup
*/
void printTServerInfo(struct sockaddr_in servaddr,
int cliLen,
time_t startupTime);

#endif /* THERMSERV_H_ */

Warnings resolved!

back to the rest of thermserv.c:
main() - set up code

/* start main */
int main(void) {

/*
* TODO implement server as daemon, per best
* practices, Advanced Programming in Unix Environment (ch 13, p417)
*/

int serverFd, connectionFd;
struct sockaddr_in servaddr;
char tempBuffer[MAX_BUFFER + 1];
time_t currentTime;
time_t startupTime;
float fread;
socklen_t cliLen;
struct sockaddr_in cliaAddr;
int therm_sem_id;
struct sembuf semAcquireBuffer;
struct sembuf semReleaseBuffer;
struct sigaction act;

/*init socket*/
serverFd = socket(AF_INET, SOCK_STREAM, 0);

/* set up semaphore */
therm_sem_id = semget(THERM_SEM_ID, 1, 0666 | IPC_CREAT);
if (therm_sem_id >= 0) {
printf("*** Semaphore %d\n", therm_sem_id);
/* init semaphore count to 1 */
semctl(therm_sem_id, 0, SETVAL, 1);
} else {
printf("Could not create/get semaphore with errno: %d %s", errno,
strerror(errno));
exit(-1);
}

semAcquireBuffer.sem_flg = 0;
semAcquireBuffer.sem_op = -1;
semAcquireBuffer.sem_num = 0;

semReleaseBuffer.sem_flg = 0;
semReleaseBuffer.sem_op = 1;
semReleaseBuffer.sem_num = 0;

/* Set exit handler function for SIGUSR1 , SIGINT (ctrl+c) */
act.sa_flags = SA_SIGINFO;
act.sa_sigaction = exitHandler;
sigaction(SIGUSR1, &act, 0);
sigaction(SIGINT, &act, 0);

/* Set socket server */
memset(&servaddr, 0, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(THERM_SERVER_PORT);

bind(serverFd, (struct sockaddr *) &servaddr, sizeof(servaddr));

listen(serverFd, 5);

/* initialize thermometer api (required by rand api) */
initTherm();

/* get set up to capture client socket address */
cliLen = sizeof(struct sockaddr_in);
startupTime = time(NULL);
printTServerInfo(servaddr, cliLen, startupTime);



Now the block with all the hot action, an infinite while loop for the socket server to accept connections, get the current temperature value and then write to the socket. If a SIGUSR1 or SIGINT (ctrl+c) is received by the handlers the loop value loopCounter will be set to false and the while loop will end allowing for some clean up at the end to close the sockets, semaphores, etc...

/* infinite loop until SIGUSR1 or SIGINT is handled */
while (loopCounter) {

printf("Waiting for Client connection\n");
connectionFd = accept(serverFd, (struct sockaddr*) &cliaAddr, &cliLen);
/* valid connection to client accepted */
if (connectionFd >= 0) {

currentTime = time(NULL);
/*print client address info and time of connection*/
/* http://www.cs.odu.edu/~cs476/fall03/lectures/sockets.htm
*
* Note ctime() seems to build in trailing \n
* */
printf("Client Connected %s:%d at %s",
inet_ntoa(cliaAddr.sin_addr), ntohs(cliaAddr.sin_port),
ctime(&currentTime));

/*Begin Critical Section acquire Semaphore*/
if (semop(therm_sem_id, &semAcquireBuffer, 1) == -1) {
printf("Critical Section Begin failed semid: %d\n",
therm_sem_id);
} else {
printf("Critical Section Begin\n");
/*write current temp in F to connected socket*/
fread = getTemp();
snprintf(tempBuffer, MAX_BUFFER, "%f\n", fread);

/*End Critical Section Release Semaphore*/
if (semop(therm_sem_id, &semReleaseBuffer, 1) == -1) {
printf("Critical Section End failed semid: %d\n",
therm_sem_id);
}
printf("Critical Section End\n");
}
/*write string tempBuffer to connected socket, close connection*/
write(connectionFd, tempBuffer, strlen(tempBuffer));
close(connectionFd);

}

}


The clean up code is reached by the signal handler function changing the loopCounter to allow the while loop to end. Other signals could cause the process to exit, I am not sure what that really means for the still open socket, and more importantly the semaphore. Might there be a better facility in C to handle this case?

/* clean up socket, semaphore before process exits*/
/* TODO better way to do this? ie Java finally{}?*/
printf("Shutting down server\n");
close(serverFd);
semctl(therm_sem_id, 0, IPC_RMID);

return 0;

}


Building therm-server

Invoking: GCC C Compiler - note libtherm and libexp are my custom static libraries
gcc -I"/home/ammianus/workspace/libtherm" -I"/home/ammianus/workspace/libexp" -O0 -g3 -pedantic -Wall -c -fmessage-length=0 -MMD -MP -MF"thermserv.d" -MT"thermserv.d" -o"thermserv.o" "../thermserv.c

Invoking: GCC C Linker
gcc -static -L"/home/ammianus/workspace/libtherm/Debug" -L"/home/ammianus/workspace/libexp/Debug" -o"therm-server" ./thermserv.o -ltherm -lexp

Running therm-server
For the fun part. Open a terminal and navigate to /home/ammianus/workspace/libtherm/Debug
./therm-serv
This launches the server, it acquires the semaphore and inits the socket listening on port 6777

*** Semaphore 131075
*** Thermo Server initialized.
*** cliLen size: 16
*** Server Listening as 0.0.0.0:6777
*** Server PID: 4790
*** Start up time: Sun Oct 31 12:27:11 2010
Waiting for Client connection



To connect to the socket with a client, use telnet or a web browser and connect to 0.0.0.0:6777

ammianus@Hadrian:~$ telnet 0.0.0.0 6777
Trying 0.0.0.0...
Connected to 0.0.0.0.
Escape character is '^]'.
72.720497
Connection closed by foreign host.

Getting 72.720497 is the "temperature" that was our goal from the start!

On the server side it logged this connection attempt:
Waiting for Client connection
Client Connected 127.0.0.1:57090 at Sun Oct 31 12:30:17 2010
Critical Section Begin
Critical Section End
Waiting for Client connection

Etc..

The server will continue to run and accept connections, (try connecting with a browser and holding down 'F5').

To shutdown the server, you can send a signal to its PID
ammianus@Hadrian:~$ kill -s SIGUSR1 4790
And server handles this signal gracefully
*** Got 10 signal from 4794
Shutting down server


References
http://www.suite101.com/content/c-header-files-a2936
http://stackoverflow.com/questions/2406986/c-warning-implicit-declaration-of-function-exit
http://stackoverflow.com/questions/4009090/what-is-correct-way-to-have-single-signal-handler-function-for-multiple-signals
http://www.cs.odu.edu/~cs476/fall03/lectures/sockets.htm
Jones, M. Tim. GNULinux application programming. 2nd ed. Cengage Learning, 2005. Print.
Richard, W., and Stephen A. Advanced Programming in the UNIX Environment. Addison-Wesley Professional, 2008. Print.

Saturday, October 23, 2010

Java Sockets

I had the fortunate experience of getting cross-over benefit at work from my unrelated home experiments in Linux (Ubuntu Experiment, 8/1/2010). A few weeks ago I had gone through Chapter 13 of GNU/Linux Application Programming, covering BSD4.4 Sockets API. I had gone through the sample code for the Daytime protocol server/client sockets in C and made my own socket server examples.

At work there was a customer issue related to a workflow engine that appeared to hang on sending emails through the client's SMTP servers. We had some basic questions on whether or not the workflow engine would hang or eventually timeout if it stayed connected to the server? Or cause exceptions?

It dawned on me I could easily set up a socket server that listened on the STMP port and simulated a server not releasing a connection just for a test.

The socket model is basically: Create Socket > Bind to an address > Listen > Accept client connection > [Data Transfer] > Close > loop back to Accept.

Given my recent experience it would be pretty trivial to replace [Data Transfer] with [Delay indefinitely] in order to see what effect that had. Since we use Windows 7 at work, and I didn't have the C code handy, I figured I would just write the same sort of socket server in Java.

I created a new class, SMTPServerSocket

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Date;
import javax.net.ServerSocketFactory;

Just a few necessary imports from java.net and javax.net packages.


public class SMTPSocketServer
{
public SMTPSocketServer(){
super();
}

Definition and default constructor


public static void main(String[] args)
{

try
{

//get the default server socket factory
ServerSocketFactory socketFactory = ServerSocketFactory.getDefault();

//port 25 - SMTP, allow a backlog of 1000 before connections are refused.
ServerSocket smtpServer = socketFactory.createServerSocket(25, 1000);

System.out.println("Started: "+smtpServer.getLocalSocketAddress().toString());
//TODO need a way to break out of this infinite loop
boolean listen = true;
while(listen){
System.out.println("Waiting for connection "+(new Date()));
Socket sock = smtpServer.accept();
//print some info on the connection
System.out.println("Accepted: "+sock.getInetAddress().toString());
System.out.println("SO_TIMEOUT: "+sock.getSoTimeout());
System.out.println("Data: "+sock.getReceiveBufferSize());

System.out.println("Sleeping "+(new Date()));
//sleep for n * 1000 milliseconds
Thread.sleep(2000 * 1000 );
System.out.println("Done "+(new Date()));
sock.close();
}
//close
smtpServer.close();
}
catch (IOException e)
{
e.printStackTrace();
}
catch (InterruptedException e)
{
e.printStackTrace();
}

This was the main body. The socket is created using the ServerSocketFactory. I set it up to listen on port 25 (default SMTP port). I entered an infinite while loop. For the purposes of this demo I didn't build anything in that would allow it to break out of the loop. Ctrl+C seems to do the trick. The socket calls accept() which blocks until a client connects. Then when the client connects I printed some debug info, then put the process to sleep for a long amount of time to test the effects on our workflow engine. After the sleep period the connection is closed.

This actually ended up working ok. We proved that the workflow doesn't appear to have any timeout when it tries to connect to the SMTP server. I could reproduce the behavior of the hanging process while it waited on my SMTPServerSocket during the long sleep.