MacSockets
Volume Number: 7
Issue Number: 12
Column Tag: Com Workshop
Library for UNIX-based Clients 
By Frank Storbeck, The Netherlands
Introduction.
From the moment that Apple Computer, Inc. came with its UDP-TCP/IP driver
for the Macintosh, there was suddenly an increased potential for use of client-server
programs as developed in the UNIX-world. Alas, Apple came with its own calls for
addressing their UDP-TCP/IP driver (“.IPP”). The calls are essentially the standard
low level PBOpen, PBControl and PBClose calls from the DeviceManager. The software
includes a file named “TCPPB.h”, in which one finds the definitions for the various
DeviceManager calls for the .IPP-driver. However it would be easy if one could use the
standard UNIX calls for socket-based communication instead. This gives you the
opportunity to port the UNIX software to the Mac. So I started to write a small library
with the .IPP equivalents for the client-functions close, connect, recv, recvfrom,
recvmsg, send, sendto, sendmsg, shutdown, and socket. To use the Mac as a server, you
also need the equivalents of the functions accept, bind, listen and select, but, as I only
needed the client-software, I left them out of the project. There are also some other
constraints. These will be addressed in the next paragraph. To use the MacTCP software
you need a Mac-512E or better and the appropriate LocalTalk- or Ethernet-compatible
hardware.
The MacTCP package is available as a single-user developer kit (APDA™ #
M0230LL/D). It contains the MacTCP software, a programmers guide and an
administrator guide. A licence is required to redistribute the software.
Sockets.
In the manual titled “Network Programming Guide” from SUN®-micro systems
(part number 800-3850-10) you can find a good explanation of the in’s and out’s of
“Socket-Based Interprocess Communication”. Here only a short summary will follow.
Normally some host sets up a particular service. A client that wants to
communicate with this server has to know the address of the host and what is called the
port-number of the service. For the standard UNIX-services these port-numbers are
“well known” (here I quoted from the manual pages), but if you want to start to offer a
new service there is a range of numbers from which you can choose. In the example
given here for the use of the code I choose the value 31768 (in a UNIX-system these
values for the port-numbers can be found in the file /etc/services).
A socket-pair is a “two way communication channel between two processes”.
Each of these processes has a socket that can be used both for sending and receiving of
data to and from the other process. The processes may reside on the same or different
hosts, but they have to use the same address domains, the same communication method
and the same protocol. As I had to use the software for a special project for
communication on board of a research vessel, I allowed some more constraints to the
software: only internet addressing mode (AF_INET), stream-sockets (SOCK_STREAM)
and of course the TCP-IP protocol are supported.
Normally a server-program runs on a host for as long as possible (i.e. between
start-up and shut-down). It then waits for the action of some client-program. The
client tries to connect to the server, sends a message that it wants some service and
then waits for the answer from the server. This can go on for as long as the service is
needed. Then the connection is closed down and the server waits for the next client to be
served. It is also possible that a server works for more than one client simultaneously.
In this case it has a socket for each of the various clients to be served.
The client and server programs
This scheme shows up in the sample code for client.c and server.c that can be
found in the listing. They are examples of how to use the calls to the functions for the
socket-based communication. After the server has been activated, the client can be
started, and it sends the following message to the server:
From: client
To: server
Mssg: Hello server!
Then the server replies with another message:
From: server
To: client
Mssg: Hello client!\n
The server has to be run on some SUN workstation (because of the call bcopy,
replace it eventually with the ANSI-C call memcpy, but beware, the order of the
parameters is different; also there is a function bzero to clear the memory locations
over some range - if not present, deactivate it: the locations will be overwritten by the
message anyhow!) The client has been compiled with THINK-C (4.0) compiler. The
THINK-C project must also contain the ANSI, MacTraps and UNIX libraries.
The client-program can also be run on a SUN workstation. Just activate the
SUN_C macro.
Of course you can change client and server as you like. You can maintain the
connection until the client sends some bye-bye message, messages can be sent in chunks
etc. etc. Do as you like.
Diagram 1: Sequence for communication between some server and one or more clients.
Some notes on the library
MacTCP provides the use of an asynchronous notification routine. This means
that when some exception occurs, a user specified function can be activated. This is
here implemented by the function SockNotify, which is hidden to the user. It calls the
function DoAlert that draws an alert box on the screen displaying the appropriate
massage for some time. The default showing time is 4 seconds, but can be changed with
the function SetDialogShowTime. When the time is zero or negative, no DialogBox will
be shown. The function DoAlert is also used by the function perror. In this case a
stopIcon and an OK-button are added. The DialogBox shows the error message until the
user clicks on the OK- button. As the function cannot be sure that a resource for the
DialogBox and its DialogItemList is available, it is built up in memory. Note that to use
it, you must initialize the DialogManager etc., otherwise your Mac behaves like the
mother of all field-battles. All this can be switched off by deactivating the MAC_APPL
macro. In this case the messages will be shown on stderr, using the console-library
from THINK-C.
There are some extra routines added: close_MacSocket, exit_MacSocket,
perror_MacSocket, and strerror_MacSocket,. They are used to prevent direct calls to
their UNIX and ANSI-C equivalents close, exit, perror and strerror. In the file
MacSocket.h there are macros that make sure that these calls will be diverted to their
equivalents in the MacSocket library. e.g., if you call close, close_MacSocket first tries
to close a socket-based connection. If the socket doesn’t exist, it calls the standard
function close. This is a particular noteworthy example: you might have a file
descriptor with number 5 and a socket with number 5. The only way to close the file
with file-descriptor 5 while keeping the socket with number 5 in good shape is calling
close directly:
/* 1 */
#undef close /* to dactivate a call to close_MacSocket */
close(5);
#define close close_MacSocket /* to reactivate it */
The program includes some files that are not provided here: “types.h”,
“socket.h, “in.h”, “netdb.h” and “TCPPB.h”. The first four come from the SUNOS 4.1
operating system and are copyrighted by the Regents of the University of California I
am not sure if I can redistribute them, so they are not included. But if you want to port
software from a UNIX-platform to the Mac, you must have access to them anyhow. The
file “TCPPB.h” is copyrighted by Apple Computer, Inc.
Last note: the program was written using SUNOS 4.0. Now we are using version
4.1. There they don’t speak about “address families” but about “address protocols”.
Hence the macro AF_INET has been renamed to AP_INET, but AF_INET still works.
Listings server.c:
/***
***
*** To send data between stream sockets (having communication
*** style SOCK_STREAM)
*** Code based on an example taken from SUN® Network Program
*** ming Guide,chapter 10, A Socket-Based Interprocess
*** Communications Tutorial,Revision A, of 27 March 1990,
*** Fig. 10-11, pp. 270-271.
***/
#include
#include
#include
#include
#include
#include
#define TRUE (1)
#define DATA "\nFrom: server\nTo: client\nMssg: Hello client!\n
#define Port (31768)
main()
{
int sock, length;
struct sockaddr_in server;
int msgsock;
char buf[1024];
fd_set ready;
struct timeval to;
/* Create socket. */
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 0)
{
perror("Server--> opening stream socket");
exit(1);
}
/* Name socket using wildcards. */
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = Port;
if (bind(sock, (struct sockadd *)&server, sizeof server) < 0)
{
perror("Server--> getting socket name");
exit(1);
}
printf("Server--> Socket port #%d\n", ntohs(server.sin_port));
/* Start accepting connections. */
listen(sock, 5);
do
{
FD_ZERO(&ready);
FD_SET(sock, &ready);
to.tv_sec = 5;
if (select(sock + 1, &ready, (fd_set *)0, (fd_set *)0, &to) < 0)
{
perror("Server--> select");
continue;
}
if (FD_ISSET(sock, &ready))
{
msgsock = accept(sock, (struct sockaddr *) 0, (int *) 0);
if (msgsock == -1)