Object-Oriented Interprocess Communication

Stephen shows how to place IPC services in C++ wrappers. He then turns his attention to Java socket classes, which illustrate an alternative object oriented interface.


August 01, 1996
URL:http://www.drdobbs.com/cpp/object-oriented-interprocess-communicati/184409933

August 1996: Object Oriented Interprocess Communication

Object-Oriented Interprocess Communication

Client/server development in C++ and Java

Stephen Blaha

Stephen is an associate of the Harvard University faculty, president of Bali Software, and author of C++ for Professional Programmers with PC and UNIX Applications (International Thomson Computer Press, 1995). He can be contacted at 76070 [email protected].


Processes access shared memory as if it were an ordinary array

Interprocess communication (IPC) is a key feature of multitasking operating systems such as UNIX, Windows NT, Windows 95, and OS/2. Despite its importance, however, standard approaches to IPC coding can be time-consuming and difficult. Consequently, many programmers end up copying code that works, then adapting it to suit their particular needs. A better approach is to encapsulate IPCs within classes and access IPC features through simple methods (functions). In this article, I describe one way to do this in C++. Additionally, I'll discuss Java socket classes, which illustrate an alternative object-oriented interface to IPC services.

In the strategy presented here, I'll place IPCs in wrappers classes that provide an interface to a set of functions in some library. Wrapped functions are as efficient as calling the functions directly because the wrapper can use inline member functions. Wrappers can provide a simple interface to functions with complex parameter lists. One immediate result of this C++-based, object-oriented approach is to reduce sockets (which are notoriously tricky to deal with) to reading and writing to the terminal screen using the C++ overloaded shift operators, and . I will also show how to handle semaphores and shared memory.

The C++ code works in most UNIX and PC (Windows NT 3.5 or later, Windows 95, and OS/2) environments with little or no change. The C++ examples conform to UNIX SVID standards, so they are portable between UNIX operating-system environments. The Java code is portable to all environments that support Java. The code examples do not have all the bells and whistles of a robust implementation. The primary omissions are error testing for return values, testing for incorrect end-user input, and testing for file and device I/O status.

Shared Memory

My object-oriented design approach makes shared memory as simple as an array. A shared-memory region is created and a number of processes access it as if it were an ordinary array. After the last process finishes accessing it, the shared memory is automatically deallocated by a destructor. Since the shared-memory objects can be automatic objects, access to the shared memory of a given process may only exist for part of the lifetime of the process. Figure 1 illustrates how processes access shared memory.

Listing One is the Shar_mem template class, which manages objects with an associated shared-memory segment. Each time an object of this class is created, its constructor will create or access a shared-memory segment (depending on the value of the shmflags element type) with the shmget() function call, then attach the shared-memory segment to the current process. Each shared-memory segment is labeled with a unique IPC key value, so a process can create several objects' each accessing a different shared-memory segment.

Each shared-memory segment will store an array. The data type of the array is specified by the element type T of the template class Shar_mem. The other template element types specify the IPC key identifying the IPC, and the number of units of storage (of the specified data type) in the shared-memory array.

The layout of the shared-memory region is mapped in units of the specified type of the data array it contains. The first byte of the first unit of storage specifies the number of processes currently accessing the shared-memory segment. When a shared-memory segment is created, the value in this byte is set to 1. When an existing shared-memory segment is attached (accessed), the value in this byte is incremented by 1. When a process detaches from the shared-memory segment, the value in the first byte is decremented.

The data in the array in the shared-memory segment is stored at locations beginning with the second unit of shared memory. The array interface to the data is implemented with a [] overload, which fetches and sets individual array elements in shared memory with normal array indexingthe first array element has index 0, and so on.

The set_mem() function places the values of a normal input array into shared memory. The get_arr() function fetches values from the shared-memory segment and places the values in a normal array.

The Shar_mem destructor detaches the process from the shared-memory segment using shmdt(). After detaching, the destructor checks if any processes are still attached to the shared memory; if none are, the shared-memory segment is removed.

Listings Two and Three create and use two shared-memory segments; see Figure 2. The program sm1.cpp creates two shared segments and places data values in each. It then sleeps for 60 seconds. The program sm2.cpp accesses the shared-memory segments, fetches values from them, and then displays the values on screen. The programs could be executed in UNIX with the command sm1 &; sm2. (The & at the end of the first command runs the command in the background.) The names of the objects associated with the shared memory are the same in each program. The names could have been different, since the IPC key value is the only relevant factor in linking an object to its shared-memory IPC. The typedef New_shar_int, defined in the header file, allows the application code in the sm1.cpp main() to be simplified.

Semaphores

Semaphores are used to synchronize processes. Listing Four, the Sema template class, creates and manages a single semaphore. The template element types are the IPC key value and a semaphore flags value specifying the creation of and/or access to the semaphore.

The constructor for the template class, Sema, creates a single semaphore IPC with semget(). The destructor removes the semaphore if it is unlocked. The semaphore example could be generalized to have one class responsible for creating and removing the semaphore IPC, and another class responsible for accessing and using the resource. The member functions of class Sema manage the locking and unlocking capability. The locking mechanism can also be used to synchronize access to another resource, such as shared memory, files, interprocess messaging, and databases.

Examples 1 and 2 demonstrate the Sema template class. The programs should be run simultaneously in separate windows of a UNIX windowing environment or on separate terminals.

Synchronized Shared Memory

As an obvious extension of the previous discussions, I derived a class that inherits both the shared memory and semaphore classes to support synchronized access to shared memory by multiple processes. The template class Sync_mem (Listing Five) provides a shared-memory segment and a semaphore IPC to synchronize access to the shared memory for multiple processes reading and writing.

The Sync_mem template class is derived from the Shar_mem and Sema templates. Figure 3 is the derivation hierarchy. When a Sync_mem object is created, the Shar_mem constructor creates or accesses a shared-memory segment, and the Sema constructor creates or accesses a semaphore that will be used to provide a synchronization mechanism. The Sync_mem member functions read and write the shared-memory segment. Most of the functions come in pairs: One function reads or writes if the shared memory is unlocked, the other one (with a name ending in a w) waits until the shared memory is unlocked and then reads/writes the shared memory. The Sync_mem [] operator overload waits until the shared memory is unlocked before fetching or assigning a value.

The public functions of the base classes Sema and Shar_mem can also be used in application code to manipulate locks and shared memory.

If you compile Listings Six and Seven and run shmem1 in the background and shmem2 in the foreground, you will see this output:

Shared Memory locked.

arr: 1 99 3 4 5

si: 1 99 3 4 5

The program shmem1 creates a shared-memory segment and places five integer values in it. The shared memory is then locked for 45 seconds. The program shmem2 attaches the shared-memory segment, then tests whether the memory is locked. It should find the memory locked due to the 45-second delay. After the lock is removed by shmem1, the program shmem2 fetches five integer values, placing them in the arr array. The values in arr are then displayed on screen. Then the Sync_mem [] overload fetches values from shared memory, which are displayed.

Sockets

Wrappers make it possible for you to reduce socket communications code to a level approaching the simplicity of terminal I/O. The class Soc (see Listing Eight) lets you send messages over sockets using and overloads, or with simple function calls. The Soc constructor initializes a socket with the socket() function call. An application can then bind, listen, and accept connections from other computers in the network, or an application can connect to a socket on another computer that is listening.

Listings Nine and Ten demonstrate the Soc class. To compile them, you'll need to set a few values. Insert a #define UNIX statement at the top of the header file for UNIX, or a #define OS2 statement for OS/2. Substitute the name of one of the computers (the one that will run soc1) into the main() function of both soc1.cpp and soc2.cpp. Then run the two programs on different computers on the same network. (The programs will communicate with each other only if sockets are supported by your operating system and network.)

Listing Nine, soc1.cpp, creates soc1, which opens a socket with port number 2222. Then soc_listen() sets up queuing for the socket, enabling up to five processes to be queued. Next, soc_accept() blocks (waits) until a connect request is received from another process. The connection is accepted from the other program (produced from soc2.cpp, Listing Ten) on the other computer. Then the programs exchange messages using soc_send(), soc_receive(), , and . The and soc_receive() functions block until the other process sends a message. The first program then breaks the connection with soc_decline().

On the second computer, soc2.cpp instantiates a Soc object, which creates a socket and attempts to connect to the first computer through the specified port number. After the connection is established, the program exchanges messages using soc_send(), soc_receive(), , and . The connection is broken at the end of main() by the ~Soc() destructor.

If you contrast the low-level socket function calls that are embedded in class Soc and compare them with the function calls in the main() functions of the preceding two programs, you can see how the simple, object-oriented IPCs compare to normal procedural approaches. The low-level system calls offer many opportunities for error.

Java Sockets

The Java socket classes provide an alternative object-oriented approach to interfacing to IPC services. Essentially, Java places a wrapper around sockets to simplify their handling. Java 1.0 has classes that support TCP and UDP sockets. UDP sockets transfer data in individual packets, while TCP sockets transfer variable-length strings of data without packet boundaries. Other types of IPCs are not currently supported in Java.

Javas support for TCP stream sockets is in its Java.net package with the Socket and ServerSocket classes. Java's support for UDP sockets, also known as Datagram sockets, is based on the DatagramSocket and DatagramPacket classes. Figure 4 illustrates the client/server model for TCP socket communications.

The Java ServerSocket class supports TCP/IP network sockets. Java uses several methods in TCP/IP socket communications:

The Socket class also supports TCP/IP network sockets. Some of the important methods used in TCP/IP socket communications are:

The server and client programs in Examples 3 and 4 illustrate the simplicity of object-oriented client/server programming in Java. The server program defines a class with a main() method that opens a socket, accepts a connection, and then sends a message using a DataOutputStream file-writing method. The client program accesses the server, reads data from the server, and prints the data on the terminal screen. Both programs are stand-alone Java programs. They could easily be rewritten to be Java applets that could be embedded within HTML Web page files.

References

Blaha, Stephen. C++ for Professional Programmers with PC and UNIX Applications. Boston, MA: International Thomson Computer Press, 1995.

Lam, Richard B. Cross-Platform Communication Classes. DDJ, March 1995.

. Shared Memory and Message Queues. DDJ, May 1995.

Rodley, John. OS/2 and UnixWare

Interprocess Communication. DDJ, May 1994.

Figure 1: Processes accessing a shared-memory segment.

Figure 2: The relationships between the programs and shared-memory segments.

Figure 3: Derivation hierarchy for Sync_mem.

Figure 4: Diagram of Java client/server TCP socket model.

Example 1: Sema1.cpp semaphore demonstration program.

/
/* Program Creating Semaphore - File: sema1.cpp */
#include <iostream.h>
extern "C" {
#include <sys/ipc.h>
#include <sys/sem.h>
}
#include <types.h>
#include "sema.h"

int main() {
    Sema <IPC_CREAT|0666, 123456> sem1;
    sem1.lockit();
    cout  "Resource locked."  endl;
    sleep(45);
    sem1.unlockit();
    cout  "Resource unlocked."  endl;
    sleep(30);
    return 0;
}

Example 2: Sema2.cpp semaphore demonstration program.

* Program Accessing Semaphore - File: sema2.cpp */
#include <iostream.h>
extern "C" {
#include <sys/ipc.h>
#include <sys/sem.h>
}
#include <types.h>
#include "sema.h"

int main() {
    Sema <0, 123456> sem1;
    if(sem1.islocked())
        cout  "Process 2: Resource locked."
              endl;
    sem1.lock_wait();
    if(sem1.isunlocked())
        cout  "Process 2: Resource unlocked."
              endl;
    return 0;
}

Example 3: myClient.Java socket client program.

/* Client Program:  myClient.Java */
import Java.net.*;
import Java.io.*;
import Java.awt.*;

public class myClient
{
        public static void main(String args[]) throws IOException
        {
                Socket s = new Socket("server", 2001);
                DataInputStream is;
                byte barr [] = new byte[50];

                is = (DataInputStream) s.getInputStream();
                is.read(barr);

                System.out.write(barr);
                System.out.flush();

                s.close();
        }
}

Example 4: myServer.Java socket server program.

/* Server Program:  myServer.Java */
import Java.net.*;
import Java.io.*;
import Java.awt.*;

public class myServer
{
        public static void main(String args[]) throws IOException
        {
                ServerSocket ss = new ServerSocket(2001, 600);
                Socket s;
                DataOutputStream os;

                s = ss.accept();
                os = (DataOutputStream) s.getOutputStream();
                os.writeChars("Message received by server.");

                s.close();
        }
}


Listing One

 /* Header File: sm.h */
template <int shmflags, class T, int key, int size> class Shar_mem
{
  private:
    int shm_id;
    T * ptr;
  public:
    void get_mem()
    {
      if((shm_id=shmget(key,size*sizeof(T),shmflags)) >= 0) // create or access
            {                                               // shared memory.
                ptr = (T *) shmat( shm_id, 0, SHM_MEM);     // attach memory.
                if(shmflags > 0) 
                    *((char *) ptr) = 1;  // create: specify one process accessing.    
                else
                   (*((char *) ptr))++;     // access: increment count of processes.
            }
            else
            {
                cerr << "Can't obtain shared memory.\n";
                exit(1);
            }
            ptr++;            // increment pointer to "array" data area.
        }
        Shar_mem()
        {
            get_mem();
        }
        Shar_mem(const T * input, int num)
        {
            get_mem();
            for(int i=0; i < num; i++)
                ptr[i] = input[i];
        }
        T * get_addr() { return ptr; }      
        void get_arr(T * arr, int len)
        {
            for(int i = 0; i < len; i++)
                arr[i] = ptr[i];
        }
        void set_mem(T * arr, int len)
        {
            for(int i = 0; i < len; i++)
                ptr[i] = arr[i];
        }
        T & operator [ ] (int loc)
        {
            return ptr[loc];
        }
        ~Shar_mem()
        {
            --ptr;
            int j = --(*((char *) ptr));
            shmdt(ptr);            // Detaches process from shared memory
            if( j <=  0)           // Checks if any processes still attached.
                shmctl(shm_id, IPC_RMID, 0);
                                   // Removes shared memory segment
        }
};

Listing Two

/* Program Creating Shared Memory - File: sm1.cpp */
#include <iostream.h>
#include <stdio.h>
#include <string.h>

extern "C"
{
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
}
#include "sm.h"
#define size 256
#define key1 123456
#define key2 654321

typedef Shar_mem<IPC_CREAT | 0666,int, key2, size> New_shar_int;
int main()                      
{
    Shar_mem <IPC_CREAT|0666, double, key1, size> m1; // create shared memory 
                                                      // with  double array.
    int arr[ ] = { 1, 2, 3 , 4, 5};
    New_shar_int m2(arr, 5);   // create another shared memory region with
                               // int array. 
    double ad[3] = {1.1, 2.2, 3.3} ;

    m1.set_mem( ad, 3); // Places data in shared memory containing double array
    m1[3] = 4.4;
    m1[4] = 5.5;
    m1[5] = 6.6;

    m2[0] = 99;         // Places data in shared memory containing int array.
    m2[2] = 77;
    sleep(60);

    return 0;
}

Listing Three

/* Program Accessing Shared Memory - File: sm2.cpp */

#include <iostream.h>
#include <stdio.h>
#include <string.h>
extern "C"
{
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
}
#include "sm.h"
#define size 256
#define key1 123456
#define key2 654321

int main()                      
{
    Shar_mem <0, double, key1, size> m1; // accesses double array shared memory
    Shar_mem <0, int, key2, size> m2;    // accesses int array shared memory.
    double ad[6];

    m1.get_arr( ad2, 3);
    cout << "ad: " << ad[0] << " " << ad[1] << " " << ad[2];
    cout << " " << ad[3] << " " << ad[4] << " " << ad[5] << endl;

    cout << "m1: " << m1[0] << " " << m1[1] << " " << m1[2];
    cout << " " << m1[3] << " " << m1[4] << " " << m1[5] << endl;

    cout << "m2: " << m2[0] << " " << m2[1]  << " " << m2[2];
    cout << " " << m2[3] << " " << m2[4] << endl;

    return 0;
}

Listing Four

/* Header File: sema.h */
template <int semflgs, int key> class Sema
{
        private:
                int sem_id;
        public:
                Sema()
                {
                        union semun sem_arg;
                        sem_arg.val = 0;
                        sem_id = semget(key, 1, semflgs);
                                          // Gets one semaphore
                        semctl(sem_id, 0, SETVAL, sem_arg);
                                          // Sets semaphore value = 0
                }
                void lockit()             // locks by setting semaphore = 1
                {
                        sembuf s[1] = {0,0,IPC_NOWAIT|SEM_UNDO};

                        if(semop(sem_id, s, 1) == 0) 
                                          // Check if not locked
                        {
                                s.sem_op = 1;
                                if(semop(sem_id, s, 1) < 0)
                                        perror("Can't lock.");
                        }
                }
                void unlockit()           // unlocks by setting semaphore = 0
                {
                        sembuf s[1] = {0,0,IPC_NOWAIT|SEM_UNDO};
                        if(semop(sem_id, s, 1) == -1)     // Check if locked
                        {
                                s.sem_op = -1;
                                if(semop(sem_id, s, 1) < 0)
                                        perror("Can't lock.");
                        }
                }
                int islocked()             // Returns true if semaphore locked
                {
                        sembuf s[1] = {0,0, IPC_NOWAIT|SEM_UNDO};
                        return semop(sem_id, s, 1) == 0;
                }
                int isunlocked()          // Returns true if semaphore unlocked
                {
                        sembuf s[1] = {0,0,IPC_NOWAIT|SEM_UNDO};
                        return semop(sem_id, s, 1) == -1;
                }             
                void lock_wait()       // Causes pause until semaphore unlocked
                {
                        sembuf s[1] = {0,0, 0};
                        semop(sem_id, s, 1);
                }
                ~Sema()                    // Removes semaphore
                {
                        semctl(sem_id, 0, IPC_RMID, 0);
                }
};

Listing Five

/* Header File: shmem.h */
template <class T, int shmflags, int semflags, int mkey, int skey, 
                                                 int size>  class Sync_mem
: public Shar_mem<shmflags,T,mkey,size>, public Sema<semflags,skey>
{                
    public:
        Sync_mem() { }   // Default constructors initialize base class parts.
        Sync_mem(const T * input, int num)
        : Shar_mem<shmflags,T,mkey,size>(input, num)
        { }
        T * get_addr()
        {
            if(isunlocked())
                return Shar_mem<shmflags,T,mkey,size>::get_addr();
            else
                return (T *) 0;

        }
        T * get_addrw()
        {
            lock_wait();                   // wait until semaphore unlocked.
            return Shar_mem<shmflags,T,mkey,size>::get_addr();
        }
        void get_arr(T * arr, int len)
        {
            if(isunlocked())
                Shar_mem<shmflags,T,mkey,size>::get_arr(arr, len);
        }
        void get_arrw(T * arr, int len)
        {
            lock_wait();
            Shar_mem<shmflags,T,mkey,size>::get_arr(arr, len);
        }
        void set_mem(T * arr, int len)
        {
            if(isunlocked())
                Shar_mem<shmflags,T,mkey,size>::set_mem(arr, len);
        }
        void set_memw(T * arr, int len)
        {
            lock_wait();
            Shar_mem<shmflags,T,mkey,size>::set_mem(arr, len);
        }
        T & operator [ ] (int loc)
        {
            lock_wait();
            return Shar_mem<shmflags,T,mkey,size>::operator[ ](loc);
        }
};

Listing Six

/* Program Creating Shared Memory and Semaphore - File: shmem1.cpp */

#include <iostream.h>
#include <stdio.h>
#include <string.h>
#include <types.h>

extern "C"
{
#include <sys/types.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
}
#include "sm.h"
#include "sema.h"
#include "shmem.h"
#define size1 256
#define skey1 123456
#define mkey1 654321
typedef Sync_mem<int,IPC_CREAT|0666,IPC_CREAT|0666,mkey1,skey1,size1> 
        New_sync_int;
int main()
{
    int arr[ ] = { 1, 2, 3 , 4, 5};
    New_sync_int si(arr, 5);

    si[1] = 99;

    si.lockit();
    sleep(45);
    si.unlockit();

    sleep(60);

    return 0;
}

Listing Seven

/* Program Accessing Shared Memory and Semaphore - File: shmem2.cpp */

#include <iostream.h>
#include <stdio.h>
#include <string.h>
#include <types.h>

extern "C"
{
#include <sys/types.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
}
#include "sm.h"
#include "sema.h"
#include "shmem.h"
#define size1 256
#define skey1 123456
#define mkey1 654321
typedef Sync_mem<int, 0, 0, mkey1, skey1, size1> Sync_int;

int main()
{
    Sync_int si;
    int arr[5];
    if(si.islocked())
    {
        cout << "Shared Memory locked." << endl;
        lock_wait();
    }
    else
        cout << "Shared Memory not locked." << endl;
    si.get_arr(arr, 5);

    cout << "arr: " << arr[0] << " " << arr[1] << " " << arr[2];
    cout << arr[3] << " " << arr[4] << endl;

    cout << "si: " << si[0] << " " << si[1] << " " << si[2];
    cout << si[3] << " " << si[4] << endl;

    return 0;
}

Listing Eight

/* Header File: soc.h */

#ifndef __SOC_H__
#define __SOC_H__

class Soc
{
    private:
        int portnum;
        int soc_id;
        int accept_id;
        struct hostent *phost;
        struct timeval timeout;
        struct sockaddr_in sockname;
        struct sockaddr_in acceptname;
        int size_acceptname;
        fd_set idset;
public:
        Soc(const char * hostname, int port)
        {
            portnum = port;
            if((phost = gethostbyname((char *) hostname)) == -1)
                perror("Can't get host information.");
            if((soc_id = socket(AF_INET, SOCK_STREAM, 0)) == -1)
                perror("Can't open socket.");
            bzero((char *) &sockname, sizeof(sockname));
            sockname.sin_family = AF_INET;
            sockname.sin_port = htons(portnum);
            bcopy(phost->h_addr, (char *) &sockname.sin_addr, phost->h_length);
            FD_ZERO(&idset);
            FD_SET(soc_id, &idset);
            accept_id = -2;
            timeout.tv_sec = 0;
            timeout.tv_usec = 0;
        }
        int soc_bind()                  // binds socket
        { 
            return bind(soc_id, (struct sockaddr *) &sockname,
                                sizeof(sockname)); 
        }
        int soc_connect()               // Blocks until connection 
                                        // established with remote computer.
        { 
            return connect(soc_id, (struct sockaddr *) & sockname,
                                   sizeof(sockname)); 
        }
        int soc_listen()                // Queues up to 5 requests 
                                        // for connections
        { 
            return listen(soc_id, 5);   // queues up to 5
        }                               // connect requests
        int soc_accept()                // Blocks until a connect is requested
                                        // and then accepts a connection.
        {
            size_acceptname = sizeof(acceptname);
            accept_id = accept(soc_id, (struct sockaddr *) &acceptname,
                                                           &size_acceptname);
            FD_SET(accept_id, &idset);
            return accept_id;
        }
        void soc_decline()             // Closes a connection
        {
#ifdef UNIX
            close(accept_id);
#endif
#ifdef OS2
            soclose(accept_id);
#endif
            accept_id = -2;
        }
        int soc_send(const char * buf)
        {
            if(accept_id > -1)
                return send(accept_id, buf, strlen(buf) + 1, 0);
            else
                return send(soc_id, buf, strlen(buf) + 1, 0);
        }
        int soc_receive(char * buf, int num_chars)  
                              // Blocks until connection has sent a message
        {
            select(FD_SETSIZE, &idset, (fd_set *) 0, (fd_set *) 0, &timeout);
                              // blocks indefinitely until ready to read
            if(FD_ISSET(soc_id, &idset))
                return recv(soc_id, buf, num_chars, 0);
            else if(FD_ISSET(accept_id, &idset))
                return recv(accept_id, buf, num_chars, 0);
            else
                return -2;
        }
        Soc & operator<<(const char *buf)
        {
            soc_send(buf);
            return *this;
        }
        Soc & operator>>(char *buf)
        {
            soc_receive(buf, 256);
            return *this;
        }
        ~Soc()                       // closes conections
        {
            if(accept_id > -1)
#ifdef UNIX
                close(accept_id);
            close(soc_id);
#endif
#ifdef OS2
                soclose(accept_id);
            soclose(soc_id);
#endif
        }
};
#endif

Listing Nine

/* Program on Server Computer - File: soc1.cpp */

#include <iostream.h>             

#define UNIX

#ifdef OS2
#define BSD_SELECT
#endif

extern "C"
{
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/time.h>
#include <netdb.h>
#include <netinet/in.h>
#include <errno.h>

#ifdef UNIX
#include <sys/param.h>
#include <sys/stream.h>
#include <sys/unistd.h>
#endif

#ifdef OS2
#include <utils.h>
#endif
}
#include "soc.h"
#define PORT_NUM 2222

int main()      // Server machine program.  Machine name = mymachine
{
    Soc soc1("mymachine", PORT_NUM);
    char buf[128];

    if(soc1.soc_bind() == -1)
        perror("Can't bind.");
    soc1.soc_listen();

    if(soc1.soc_accept() == -1)
        perror("Accept error.");
    soc1.soc_receive(buf, 128);
    cout << buf << endl;

    strcpy(buf, "First Message from mymachine.");
    soc1.soc_send(buf);

    soc1 >> buf;
    cout << buf << endl;

    strcpy(buf, "Second Message from mymachine.");
    soc1 << buf;

    soc1.soc_decline();
    return 0;
}


Listing Ten

/* Program on Client Computer - File: soc2.cpp */

#include <iostream.h>
#define UNIX
#ifdef OS2
#define BSD_SELECT
#endif

extern "C"
{
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/select.h>
#include <sys/time.h>
#include <netdb.h>
#include <netinet/in.h>
#include <errno.h>

#ifdef UNIX
#include <sys/param.h>
#include <sys/stream.h>
#include <sys/unistd.h>
#endif

#ifdef OS2
#include <utils.h>
#endif
}
#include "soc.h"
#define PORT_NUM 2222

int main()      // client machine program. Machine name = yourmachine
{
    Soc soc1("mymachine", PORT_NUM);
    char buf[128];

    soc1.soc_connect();

    strcpy(buf, "First Message from yourmachine.");
    soc1.soc_send(buf);

    soc1.soc_receive(buf, 128);
    cout << buf << endl;

    strcpy(buf, "Second Message from yourmachine.");
    soc1 << buf;

    soc1 >> buf;
    cout << buf << endl;

    return 0;
}

Terms of Service | Privacy Statement | Copyright © 2024 UBM Tech, All rights reserved.