/* 08/31/04 PDM - Simple guidelines for creating client/server application */ 1. Create a Communications Protocol This is the first step in the design of a client/server application. Decide on the interaction pattern that the client/server should exhibit, and formulate the communication protocol. This could be as simple as having the client write some number of bytes to stream and then closing the stream, or it could be a complicated communication sequence with multiple messages from client to server and vice versa. 2. Create the Server application This is a relatively painless task given the Java libraries. It's a little bit more complicated for C/C++, but its essentially the same process. Conceptually, one needs to listen on a specific port for an incoming connection request. Once the connection has been established, data is written to a stream in much the same way that file i/o is performed. 2.1 Java Server Application With Java, one does this by creating an instance of java.net.ServerSocket and calling the accept() method of this instance. The return value is an instance of class java.net.Socket. The resulting socket has input and output streams (socket.getInputStream(), socket.getOutputStream()) that can be used for communication. Usually it is most convenient to wrap these streams with a higher level stream, e.g. an instance of java.io.DataInputStream or java.io.DataOutputStream, in order to send/receive formatted data. See any recent Java API documentation for parameter descriptions. 2.2 C/C++ Server Application With C/C++, one creates a sockaddr_in instance to represent the server address. A socket file descriptor is created, bound to the server address, and willingness to receive connections indicated by invoking 'listen'. Subsequent calls to 'accept' will return connection requests in the form of socket file descriptors. The C library io functions 'read' and 'write' are used to transmit information between the server and client. Sample below: /* initialize address */ struct sockaddr_in srvaddr; memset(&srvaddr, 0, sizeof(srvaddr)); srvaddr.sin_family = AF_INET; srvaddr.sin_addr.s_addr = INADDR_ANY; srvaddr.sin_port = htons(gPort); /* bind socket to address and start waiting for connections */ int srvfd = socket(AF_INET, SOCK_STREAM, 0); const int on=1; setsockopt(srvfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); bind(srvfd, (struct sockaddr*)&srvaddr, sizeof(srvaddr)); listen(srvfd, 20); /* accept incoming connection requests */ struct sockaddr_in connaddr; socklen_t connlen = sizeof(connaddr); int connfd = accept(srvfd, (struct sockaddr*)&connaddr, &connlen); 3. Create a client application In concept, the client application opens a connection to the server by making a request to the server on the port that is accepting connections. A socket is used as the means for communication between the client and server. 3.1 Java Client Application Again, this is simple in Java. Create a new instance of class java.net.Socket, initializing it with the address of the server and the port to connect to. If successful, the input/output streams of the socket can be used for communication from/to the server (socket.getInputStream(), socket.getOutputStream()). 3.2 C/C++ Client Application In C, a socket is created with a call to 'socket' and is connected to the server with a call to 'connect'. Once the connection has been made, data may be transferred by invoking 'read' or 'write' with the socket file descriptor. Example below: const char *host = "moscow.cs.rochester.edu"; struct hostent *he = gethostbyname(host); struct sockaddr_in srvaddr; const char *data = "MESSAGE"; int fd = socket(AF_INET, SOCK_STREAM, 0); memset(&srvaddr, 0, sizeof(srvaddr)); srvaddr.sin_family = AF_INET; srvaddr.sin_addr.s_addr = ((struct in_addr*)he->h_addr)->s_addr; srvaddr.sin_port = htons(gPort); connect(fd, (struct sockaddr *)&srvaddr, sizeof(srvaddr)); write(fd, data, strlen(data));