Here we describe a simple means of using sockets to pass arbitrary data bi-directionally between two running processes. While there are more complicated possibilities it is this simple case which occurs most often in code written here at the lab.
The basic idea is simple:
Within each process, creating the connection is similar to opening a file using the open() call. What you end up with is an integer representing the number of the opened stream. Reading and writing is then just a simple matter of calling the system read/write routines and passing them that integer stream number.
struct stuffStruct
{
int integer;
float real;
char string[20];
} first;
and you wish to send it from process A to B. Then the actual C code for
process A is simply:
write(streamNumber, &first, sizeof(first));and the code for process B is simply:
read(streamNumber, &first, sizeof(first));Now, actually correct code would be a little more complicated, since we should check the return values from read and write to look for errors. Nevertheless the basic idea is not very complicated.
The library has just 5 functions:
int jsSockAccept( int* socket, char* service, char* protocol); int jsSockConnect( int* socket, char* rhost, char* service, char* protocol); int jsSockRead( int socket, void* buffer, int size); int jsSockWrite( int socket, void* buffer, int size); int jsSockClose( int socket );The accept and connect routines are used to create the socket connection. From process A call jsSockAccept and from process B call jsSockConnect. Then data may be written and read by calling the jsSockRead and jsSockWrite routines. Finally, when you're all done with the connection, each process should call jsSockClose.
Source code is currently in
~tele/sun4/src/socket/jsSocket.[ch] on grip.cisand is freely available for use here in the lab - it compiles using gcc on the Sun and any of the compilers (even CC) on the SGI machines. There's no need for any special compiler switches.
You are encouraged to browse through the source code and see that its doing what you expect - in particular check the jsSockRead and jsSockWrite functions and see how they are nothing but wrappers around the low-level system read and write functions.
tele1 7511/tcp tele2 7512/tcpThe first word on each line is the "service" - think of it as a textual designator for the socket. The number is the system's internal description for that socket while "tcp" is the protocol we'll be using.
Making use of someone else's entry in /etc/services is a very bad practice - the person who owns it may be trying to use it from the other side of the country - if you are going to use sockets you must get your own entry.
For process A the code is:
#include <stdio.h>
#include "jsSocket.h"
typedef struct stuffStruct
{
int integer;
float real;
char string[20];
} stuffStruct;
int main(int argc, char *argv[])
{
stuffStruct stuff;
int sock;
if( argc != 3)
{
fprintf(stderr, "Usage: %s service protocol\n", argv[0]);
exit(-1);
}
if( jsSockAccept(&sock, argv[1], argv[2]) ) exit(-1);
stuff.integer=10, stuff.real=0.5, strcpy(stuff.string, "test1");
if( jsSockWrite(sock, &stuff, sizeof(stuff))) exit(-1);
printf("1. Wrote: %d %f %s\n", stuff.integer, stuff.real, stuff.string);
if(jsSockRead(sock, &stuff, sizeof(stuff))) exit(-1);
printf("1. Read: %d %f %s\n", stuff.integer, stuff.real, stuff.string);
jsSockClose(sock);
return 0;
}
And for the second process the code is:
#include <stdio.h>
#include "jsSocket.h"
typedef struct stuffStruct
{
int integer;
float real;
char string[20];
} stuffStruct;
int main(int argc, char *argv[])
{
stuffStruct stuff;
int sock;
if( argc != 4)
{
fprintf(stderr, "Usage: %s machine service protocol\n",argv[0]);
exit(-1);
}
if( jsSockConnect(&sock, argv[1], argv[2], argv[3])) exit(-1);
if(jsSockRead(sock, &stuff, sizeof(stuff))) exit(-1);
printf("2. Read: %d %f %s\n", stuff.integer, stuff.real, stuff.string);
stuff.integer=20, stuff.real=1.0, strcpy(stuff.string, "test2");
if( jsSockWrite(sock, &stuff, sizeof(stuff))) exit(-1);
printf("2. Wrote: %d %f %s\n", stuff.integer, stuff.real, stuff.string);
jsSockClose(sock);
return 0;
}
Running the two processes is just a case of using the appropriate
command-line arguments so that each knows the name of the socket
service, the protocol to use and the name of the machine to which
it should connect.For example, using the "tele1" socket with process A running on a machine called "marlin" would require running:
processA tele1 tcpon the marlin machine and:
processB marlin tele1 tcpon some other machine.
In the unlikely event that you do need to communicate between incompatible machines then you have two choices - either add code yourself to flip the byte order around or use xdr (see man pages). However, either way its messy and slow and best avoided if possible!
If you have a choice between several different fixed-size messages then you can send a single character to indicate which type of message will follow.
One which I use with X/Xt code is to make use of XtAppAddInput(). This allows you to specify a stream and a function. When data arrives on that stream the system will automatically call the specified function. If you're not using X you can do something similar by writing your own code using the system select() function.
An alternative is to have the system send your program an interrupt whenever input on a socket arrives. Another alternative is to spawn off a separate thread to handle i/o - this is particularly easy on the iris since you can use sproc(). A final alternative is to set the sockets to be non-blocking and use polling. (See the man pages and network programming guides for more details).
This error should go away all by itself - just wait a minute or two and try again. If it doesn't disappear check for any suspended processes (at either end) which may be holding the socket open. You can avoid having to wait by being careful to shutdown each socket before terminating.
The library code is derived from code by Janez Funda which is itself based on the examples from the networking manual.