unix interprocess communication


Published on

Published in: Technology
  • Be the first to comment

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

unix interprocess communication

  1. 1. POSIX IPC Following three IPC mechanism are collectively known as posix ipc o POSIX Message queue o POSIX Semaphores o POSIX Shared memory They share some similarity in the functions that access them, and in the information that describes them. Following are the common properties The pathnames used for identification The flags specified when opening or creating Access permissions Message Semaphores Shared Queue Memory Header <mqueue.h> <semaphore.h> <sys/mman.h> Functions to mq_open sem_open shm_open create, open, or mq_close sem_close shm_unlink delete mq_unlink sem_unlink sem_init sem_destroy Functions for mq_getattr ftruncate control mq_setattr fstat operations Functions for mq_send sem_wait mmap IPC operations mq_receive sem_trywait munmap mq_notify sem_post sem_getvalue POSIX IPC functions IPC Names: The first argument to open functions above three PIC technique must be an identification name, which may or mayn’t be real path name in a file system. Creating and Opening IPC channels The three functions that create or open an IPC object, mq_open, sem_open and shm_open, all take a second argument named oflag that specifies how to open the requested object. The various constants that can be used to form this argument are: Description mq_open sem_open shm_open Read-only O_RDONLY O_RDONLY Write-only O_WRONLY Read- write O_RDWR O_RDWR 1
  2. 2. Create if it does not already O_CREAT O_CREAT O_CREAT exist O_EXCL O_EXCL O_EXCL exclusive create Nonblocking mode O_NONBLOCK Truncate if it already exists O_TRUNC A message queue can be opened in any of the three modes: read-only, write-only and read-write. Read and write access both are required for any semaphore operation so it is default for semaphore. A shared memory object cannot be write only. Remaining flags are optional O_CREAT – Creates the message queue, semaphore, or shared memory object if it doesnot already exist. O_EXCL – if this flag and O_CREAT are both specified, then the function creates a new message queue, semaphore, or shared memory object only if it doesnot already exist. If it already exist, an error of EEXIST is returned. Note: The check for the existence of an IPC and its creation must be an atomic operation. O_NONBLOCK- This flag makes a message queue nonblocking with regard to a read on an empty queue or a write to a full queue. O_TRUC- if an existing shared memory object is opened read-write, this flag specifies that the object be truncated to 0 length. 2
  3. 3. System tables full? New object is created does object O_CREAT set? already exist Are both O_CREAT and O_EXEL set? Existin object is ref. Are the access permission OK? IPC Permissions When creating new message queue, semaphore, or shared memory object at least one additional argument is required, called mode. This argument specifies the permission bits and is formed as the bitwise – OR of the constants. Constant Description S_ISUSR User read S_IWUSR User write S_IRGRP Group read S_IWGRP Group write S_IROTH Other read S_IWOTH Other write These constants are defined in <sys/stat.h> header. Whenever an access to IPC object is requested following testing is performed • If the effective user ID of the process is 0, access is allowed. 3
  4. 4. • If the effective user ID of the process equals the owner id of the IPC object: if the appropriate user access permission bit it set, access is allowed, else access is denied. • If the effective group ID of the process or one of the supplemenatary group IDs of the process equals the group ID of the IPC object: if the appropriate group access permission bit is set, access is allowed, else permission is denied. • If the appropriate other access permission bit is set, access is allowed, else permission is denied. These four steps are tried in sequence in the order listed. Therefore, if the process own the IPC object, then access is granted or denied based only on the user access permissions – the group permissions are never considered. Similarly, if the process does not own the IPC object, but the process belongs to an appropriate group, then access is granted or denied based only on the group access permissions – the other permissions are not considered. POSIX Message queue A message queue canbe thought of as a linked list of messages. Thread with adequate permission can put messages onto the queue, and threads with adequate permission can remove message from the queue. Each message is assigned some priority. No requirement exist that someone be waiting for a message to arrive on a queue before some process writes a message to that queue. This is in contrast to Pipes and FIFOs. A process can write some messages to a queue, terminate and have the messages read by another process at a later time. Message queues have kernel persistence in contrast to pipes and fifo, which are only process persistent. Any data remaining in a pipe or FIFO when the last close of the pipe or FIFO takes place, is discarded. POSIX MQ vs System V MQ A read on a Posix message queue always returns the oldest message of the highest priority, where as a read on a system V message queue can return a message of any desired priority. Posix message queue allow the generation of a signal or the notification of a thread when a message is placed onto an empty queue, whereas nothing similar is provided by system V message queue. Every message on a message queue has following attributes • A unsigned integer priority • The length of the data portion of the message • The data itself The POSIX message queue functions are: 4
  5. 5. mq_open() – Creates a new message queue or opens an existing message queue. #include<mqueue.h> mqd_t mq_open(const char *name, int oflag, . . . /* mode_t mode, struct mq_attr *attr */ ); name – Posix IPC name usually unix path name. oflag – one or more flag constants described before. mode & attr – required when new queue is created. mq_close() -- Ends the connection to an open message queue. mq_unlink() -- Ends the connection to an open message queue and causes the queue to be removed when the last process closes it. Message queue have a reference count of how many times they are currently open. mq_unlink call decrements ref count by 1 and causes a queue to be removed from the system when this ref count becomes zero. mq_send() -- Places a message in the queue. mq_receive() -- Receives (removes) the oldest, highest priority message from the queue. mq_notify() -- Notifies a process or thread that a message is available in the queue. mq_setattr() -- Set or get message queue attributes. IPC: Semaphore Semaphores are a programming construct designed by E. W. Dijkstra in the late 1960s. A semaphore appears to be a simple integer. A process (or a thread) waits for permission to proceed by waiting for the integer to become 0. The signal if it proceeds signals that this by performing incrementing the integer by 1. When it is finished, the process changes the semaphore's value by subtracting one from it. 5
  6. 6. Posix semaphores need not to be maintained in the kernel. Also, Posix semaphores are identified by names that might correspond to pathnames in the file system. PROCESS A PROCESS B Function to create, wait and process post to semaphore kernel Binary semaphore is a File system file whose contents are 0/1 Three basic operations that a process can perform on a semaphore Create a semaphore - This also requires a caller to specify the initial value which for a binary semaphore is often 1 but can be 0. Wait for a semaphore – This tests the value of the semaphore, waits if the value is less than or equal to 0, and then decrements the semaphore value once it is greater than 0. This can be summarized by the pseudocode. while(semaphore_value<=0) ; /* wait; i.e. block the tread or process*/ semaphore_value--; Post to a semaphore. This increments the value of the semaphore and can be summarized by the pseudocode semaphore_value ++; if any processes are blocked, waiting for this semaphores value to become greater than 0, one of the processes can now be awoken. There are other common names for this operation: up, unlock and signal. Common Posix Semaphore functions sem_open() -- Connects to, and optionally creates, a named semaphore sem_close() -- Ends the connection to an open semaphore. 6
  7. 7. sem_unlink() -- Ends the connection to an open semaphore and causes the semaphore to be removed when the last process closes it. sem_destroy() -- Initializes a semaphore structure (internal to the calling program, so not a named semaphore). sem_wait(), sem_trywait() -- Blocks while the semaphore is held by other processes or returns an error if the semaphore is held by another process. sem_post() -- Increments the count of the semaphore. IPC: Message Queue The basic idea of a message queue is a simple one. two (or more) processes can exchange information via access to a common system message queue. The sending process places via some (OS) message-passing module a message onto a queue which can be read by another process (Figure 24.1). Each message is given an identification or type so that processes can select the appropriate message. POSIX messages The POSIX message queue functions are: mq_open() -- Connects to, and optionally creates, a named message queue. mq_close() -- Ends the connection to an open message queue. mq_unlink() -- Ends the connection to an open message queue and causes the queue to be removed when the last process closes it. mq_send() -- Places a message in the queue. mq_receive() -- Receives (removes) the oldest, highest priority message from the queue. mq_notify() -- Notifies a process or thread that a message is available in the queue. mq_setattr() -- Set or get message queue attributes. 7
  8. 8. Shared Memory IPC Shared is fastest form of IPC available. Once the memory is mapped into the address space of the processes that are sharing the memory region, no kernel involvement occurs in passing data between the processes. Synchronization is required between processes between processes that are storing and fetching information to and from the shared memory region. we discussed various form of synchronization : mutexes, condition variables, locks and semaphores. Common steps in using shared memory  The server gets access to a shared memory object using a semaphore.  The server reads from the input file into the shared memory object. The second argument to the read, the address space of the data buffer, points to the shared memory object.  When the read is complete, the server notifies the client, using a semaphore.  The client writes the data from shared memory object to the o/p file. Common function for shared memory IPC shm_open – specifies a name argument (unix pathname) , to either create a new shared memory object or to open an existing shared memory object. mmap – maps either a file or a Posix shared memory object into the address space of a process. It is defined in <sys/mman.h> munmap – removes a mapping from the address space of the process. msync – the kernel’s virtual memory algorithm keeps the memory mapped file (on disk) synchronized with the memory mapped region in memory. This function makes certain that both copies synchronized. shm_unlink – removes the name of a shared memory object. ftruncate – to specify the size of a newly created shared memory object or to change the size of an existing object. fstat - when we open an existing shared memory object,we call fstat to obtain information about the object. System V IPC 8
  9. 9. Three types of IPC • System V message queue • System V semaphores • System V shared memory are collectively known as “System V IPC”. They share many similarities in the functions that access them, and in the information that the kernel maintains on them. Message Semaphores Shared Queue memory Header <sys/msg.h> <sys/sem.h> <sys/shm.h> Function to create or msgget semget shmget open Functions for msgctl semctl shmctl control operation Functions for IPC msgsnd semop shmat operations msgrcv shmdt System V IPC Name: The there types of IPC are noted as using key_t values for their names. The header <sys/types.h> defines the key_t datatype, as an integer, normally at least a 32-bit integer. These integer values are normally assigned by the ftok function. The function ftok converts an existing pathname and an integer identifier into a key_t value. #include<sys/ipc.h> key_t ftok (const char *pathname, int id); returns: IPC key if OK, -1 on error This function takes information derived from the pathname and the low order 8 bits of id and combines them into an integer IPC key. Here, the id argument is used to create multiple IPC channels of same path name. For example, if client and server need two IPC channels say one from client to server and one from server to client, then one channel can use an id of one and another can use id of two. • Typical implementations of ftok call the stat function and then combines • Information about the file system on which pathname resides • The file’i-node number within the file system • The low order 8 bits of the id. The combination of these three values normally produces a 32-bit key. ipc_perm structure The kernel maintains a structure of information for each IPC object, similar to the information it maintains for files #include<sys/ipc.h> struct ipc_perm{ uid_t uid; /* owner’s user id */ gid_t gid; /* owner’s group id */ uid_t cuid; /* creator’s user id */ 9
  10. 10. gid_t cgid; /* creator’s group id */ mode_t mode; /* read-write perm */ ulong_t seq; /* slot usage seq number */ key_t key; /* IPC key */ }; Creating and Opening IPC channels The three getXXX functions (msgget, semget and shmget etc.) that create or open an IPC object all takes an IPC key value whose type is key_t and return an integer identifier. If IPC key isn’t already available, pass IPC_PRIVATE as key to getXXX function, a unique IPC object is created. Specifying a key of IPC_PRIVATE guarantees that a unique IPC object is created. No combinations of pathname and id exist that cause ftok to generate a key value of IPC_PRIVATE. All three getXXX functions also take an oflag argument that specifies the read-write permission bits for the IPC object and whether a new IPC object is being created or an existing one is being referenced. We obtain an IPC identifier from one of the get function: msgget, semget, shmget. These identifier are integers, but their meaning applies to all processes unlike file descriptors. If two unrelated processes, a client and a server, use a single message queue identifier returned by megget function must be the same integer in order to access the same message queue. This feature means that a rogue process could try a read a message from some other’s application’s message queue by trying different small integer identifier, hoping find one that is currently in use that allows world read access. If the potential values of these were small integers then the probability of finding a valid identifier would be about 1 in 50. To avoid this problem, the designers of these IPC facilities decided to increase the possible range of identifier to include all integers, not just small integers. System V message Queue System V message Queue are identified by a message queue identifier. Any process with adequate privileges can place a message onto a given queue or read a message from a given queue. msgget function – A new message queue is created, or an existing message queue is accessed . msgsnd function – put a message string pointed by its 2nd argument on to the queue. msgrcv function – a message is read from a message queue using the msgrcv function. msgctl function- provides a variety of control operations on message queue. Like removing a queue from the system, setting and reading permission bits and mode. Multiplexing messages Two features are provided by the type field that is associated with each message on a queue: 1
  11. 11. The type field can be used to identify the message, allowing multiple processes to multiplex messages onto a single queue. One value of the type field is used for messages from the clients to the server, and a different value that is unique for each client is used form messages from the server to the clients. Naturally, the process ID of the client can be used as the type field that is unique for each client. The type field can be used as a priority field. This lets the receiver read the messages in an order other than FIFO. System V shared memory Similar in concept to Posix shared memory instead of calling shm_open followed by mmap, we call shget followed by shmat. Common functions shmget function- a shared memory segment is created or an existing one is accessed, by the shmget function. The return value is an integer called the shared memory identifier. That is used with the three other shmXXX functions to refer to this segment. #include<sys/shm.h> int shm_get(key_t key, size_t size, int oflags); key can be either a value returned by ftok or the constant IPC_PRIVATE. size – specifies the size of the segment, in bytes. oflag – combination of read-write permissions. shmat function – After a shared memory segment has been created or opened by shmget, we attach it to our address space by calling shmat. shmdt function – when a process is finished with a shared memory segment, it detaches the segment by calling shmdt. shmctl – provides a variety of control operation on a shared memory segment. like removing shared memory segment, setting permission bits and mode. System V semaphore The function semget() initializes or gains access to a semaphore. It is prototyped by: int semget(key_t key, int nsems, int semflg); When the call succeeds, it returns the semaphore ID (semid). The key argument is a access value associated with the semaphore ID semctl() changes permissions and other characteristics of a semaphore set. semop - operations are performed on one or more of the semaphore in the set using this function. 1
  12. 12. Doors Door calls are synchronous. Within a process, doors are identified by descriptors. Externally, doors may be identified by pathnames in the file system. A server creates a door by calling door_create, whose argument is a pointer to the procedure that will be associated with this door, and whose return value is a descriptor for the newly created door. The server then associates a pathname with the door descriptor by calling fattach. A client opens a door by calling open, whose argument is the pathname that the server associated with the door, and whose return value is the client’s descriptor for this door. The client then calls server procedure by calling door_call. A server for one door could be a client for another door. Door calls are synchronous: when the client calls door_call, this function does not return until the server procedure returns. When a server procedure is called, both data and descriptors can be passed form the client to the server. Both data and descriptor can also be passed back from the server to the client. Descriptor passing is inherent to doors. Since doors are identified by descriptors, this allows a process to pass a door to other process. Basic API functions: door_call function – is called by a client and it calls a server procedure that is executing in the address space of the server process. #include <door.h> int door_call(int fd,door_arg_t *arg); returns: 0 if ok , -1 on error The descriptor fd is normally returned by open. The pathname opened by the client for identifier fd, identifies the server procedure to be called by door_call. The 2nd argument argp points to a structure describing the arguments and the buffer to be used to hold the return values. door_create function – A server process establishes a server procedure by calling door_create. First arg to door_create function is the address of the server procedure that will be associated with the door descriptor. door_return – when a server procedure is done it returns by calling door_return. This causes the associated door_call in the client to return. 1
  13. 13. Remote Procedure call RPC provides an alternative way to write distributed application. It allows different pieces of an application to execute on different host and provides communication between them by calling procedure of one another. So in RPC, we code our application using the familiar procedure call( like local procedure call ), but the calling process(the client) and the process containing the procedure being called(server) can be executing on different host. Steps in writing RPC application Write RPC specification file has .x extension, define server procedure along with their arguments and results. struct square_in{ /* input(argument) */ long arg1; }; struct square_out{/* output(argument) */ long res1; }; program SQUARE_PROG{ version SQUARE_VERS{ square_out SQUAREPROC(square_in) = 1;/* procedure number=1*/ }=1; /* version number */ }=0x31230000; Step 2: Compiling RPC specification file (square.x file) using rpcgen program. It generates following files A header file that you will include in your programs: square.h • A server program which will call actual server procedure when request is • received): square_svc.c also called server skeleton A client stub (that client program can use to send an RPC) • square_clnt.c Internal function to convert the procedure parameters into network • messages and vice-versa: square_xdr.c. xdr(external data representation) must be shared by both server and client. Step 3: Write client program. Include header generated by rpcgen. Declaring and obtaining client handler using clnt_create function. #include<rpc/rpc.h> CLIENT *clnt_create(const char *host,unsigned long prognum, unsigned long versnum, const char *protocol); Returns: nonnull client handle if OK, NULL on error. host: hostname or ip address of the host running our server. 1
  14. 14. prognum: program name. versnum: version number. protocol: either TCP or UDP. Typical code on client side main function: CLIENT *cl;// declaring client handle. square_in in; square_out *outp; cl=clnt_create(“”,0x31230000,1,”TCP”);//obtaining client handle outp=squareproc_1(&in,cl);// calling server procedure Step 4: Call remote procedure and print result. We call procedure and first argument is a pointer to the input structure struct_in and the second argument is the client handle. The return value is a pointer to the result structure struct_out. In our specification file, we named our procedure SQUAREPROC, but from the client we call squareproc_1. The convension is that the name in the .x file is converted to lowercase and an underscore is appended, followed by the version number. Step 5: writing server procedure on the server side, all we write is our server procedure. rpcgen automatically generates the server main function. name of the server must be _svc appended following the version number. This allows two ANSI C function prototypes in the generated .h header file, one for the function called by the client and one for the actual server function. Server procedure has two argument: First is pointer to struct_in structure and Second argument is a structure passed by the RPC runtime that contains information about this invocation. Typical code for server procedure square_out squareproc_1_svc(square_in *inp, struct svc_req *rqstp) { static square_out out; out.res1= inp->arg1 * inp->arg1; return(&out); } RPC files To write a minimalist RPC program, we must write 1. A C procedure to be remotely called: remoteproc.c 2. A specification of the procedure: remoteproc.x 1
  15. 15. 3. A client program to request the procedure: client.c Based on specification file(remoteproc.x), rpcgen program generates: 1) A header file that you will include in your programs: remoteproc.h 2) A server program which will call actual server procedure when request is received): remoteproc_svc.c also called server skeleton 3) A client stub (that client program can use to send an RPC) remoteproc_clnt.c 4) Internal function to convert the procedure parameters into network messages and vice-versa: remoteproc_xdr.c xdr(external data representation) must be shared by both server and client. Client needs with following files to compile: 1. client.c – client code, containing calling statement. 2. procedure_clnt.c – client stub(generated) 3. procedure_xdr.c – handles parameter and return value marshalling or conversion 4. procedure.h – containing prototype of procedure that is called by client. Server needs with following files to compile: 1) serverprocedure.c – contain actual remote procedure code. 2) procedur_xdr.c – shared by both client and server 3) procedure_svc.c – server stub 1
  16. 16. XTI –x/open transport interface XTI uses the term communication provider to describe the protocol implementation. The commonly available communication provider are the internet protocols, that is, TCP or UDP. The term communication end point refers to an object that is created and maintained by a communication provider and then used by an application. These end point are referred to by file descriptor. All Xti function begins with t_. The header that the application includes to obtain all the Xti definition is <xti.h>. some internet-specific definitions are obtained by including <xti_inet.h>. t_open function #include<xti.h> #include<fcntl.h> int t_open(const char *pathname, int oflag, struct t_info *info); establishes a communication end-point is to open the UNIX device that identifies the particular communication provider. This function returns a descriptor that is used by other XTI functions. The actual pathname to use depends on the implementation typical values for TCP/IP endpoints are /dev/tcp, /dev/udp or /dev/icmp. 1
  17. 17. Oflag argument specifies the open flags. Its value is O_RDWR. For a non-blocking end point the flag O_NONBLOCK is logically OR’ed with O_RDWR. The tinfo structure is a collection of integer values that describe the protocol dependent features of the provider. struct t_info{ t_scalar_t addr; t_scalar_t options; t_scalar_t tsdu; t_scalar_t etsdu; t_scalar_t connect; t_scalar_t discon; t_scalar_t servtype; t_scalar_t flags; }; addr – This specifies the max size in bytes of a protocol specific address. A value of -1 indicates that there is no limit to the size. For TCP or UDP is size of sockaddr_in structure. Options – Size in bytes of protocol specific functions. tsdu – stands for “transport service data unit”. This variable specifies the max size in bytes of record whose boundaries are preserved from one endpoint to other. A value of 0 indicates that the communications provider doesnot support the concept of a TSDU. For tcp value is always 0, since TCP provides a byte stream service without any record boundary. etsdu – ETSDU stands for “expedited transport service data unit” and this variable specifies the max size in bytes of an ETSDU. This is what we call out of band data. connect – some connection oriented protocols support the transfer of user data along with a connection request. This variable specifies the maximum amount of this data. TCP doesnot support this feature so its value is always -2. and udp is not connection oriented protocol, its value is also -2. discon – size of the data that can be passed along with a dis-connection request. servtype – This specifies the type of service provided by the communication provider . T_COTS – connection oriented service, without orderly release ex. TCP T_COTS_ORD - connection oriented service, with orderly release T_CLTS – connectionless service flags – additional flag for communication provider T_SENDZERO – provider supports 0-length writes T_ORDRELDATA – provider supports orderly release data TCP doesn’t support 0-length writes but UDP does. t_error and t_strerror functions 1
  18. 18. The xti functions normally return -1 on an error and set the variable t_errno to provide additional information about the error. t_errno is similar to errno in that it is set only when an error occurs and it is not cleared on successful calls. All error constants are defined <xti.h> and begins with T. One special error constant is TSYSERR – when it is returned in t_errno, it tells the application to look at the value in errno for the system error indication. Functions to format error messages t_error and t_strerror #include<xti.h> int t_error(const char *msg); returns:0 produces a message on the standard error output. This message consist of the string pointed to by msg followed by a colon and a space, followed by a message string corresponding to the current value of t_errno. If t_errno equals T_SYSERR, then a message is also output corresponding to the current value of errno. const char *t_strerror(int errno); returns: pointer to message returns a string describing the value of errnum, which is assumed to be one of the possible t_errno values. Unlike t_error, t_strerror does nothing special if this value is TSYSERR. Netbuf structures and XTI structures XTI defines seven structures that are used to pass information between the application and the XTI functions. One of these is t_info described earlier. The remaining six each contain between one and three netbuf structures. The netbuf structure defines a buffer that is used to pass data from the application to the XTI function and vice-versa. struct netbuf{ unsigned int maxlen;/* max size of buf*/ unsigned int len; /* actual amount of data in buf */ void *buf; /* data (char* before posix.1g )*/ }; following table shows six structures that contain one or more netbuf structures, and the various other members of the XTI structure. DataType t_bind t_call t_discon t_optmgmt t_uderr t_unitdata struct netbuf addr addr addr addr struct netbuf opt opt opt opt struct netbuf udata udata udata t_scalar_t error t_scalar_t flags unsigned int glen 1
  19. 19. int int reason sequence sequence Six XTI structure and their members. Since the address of the netbuf structure is always passed to an Xti function and since the structure contains both the size of the buffer (maxlen) and the amount of data actually stored in the buffer (len), there is no need in XTI for all the value arguments used in the socket. t_bind function This function assigns the local address to an endpoint and activates the endpoint. In the case of TCP or UDP the local address is an IP address and a port. #include<xti.h> int t_bind(int fd,const struct t_bind *request, struct t_bind *return); returns : 0 if OK, -1 on error. The second and third argument point to t_bind structure struct t_bind{ struct netbuf addr;/* protocol specific address */ unsigned int len; /*max# of outstanding connection (if server)*/ }; The endpoint is specified by fd. There are three cases to consider for the request argument. request = = NULL the caller doesnot care what local address gets assigned to the endpoint. The provider selects an address. The value of glen argument is assumed to be zero. request !=null but request->addr.len == 0 The caller doesn’t care what protocol address get assigned to the endpoint and again the provider selects an address. Unlike the previous case, the caller can now specify a nonzero value for the glen member of the request structure. request !=null and request->addr.len > 0 The caller specifies a local address for the communication provider to assign to the communication endpoint. The value of glen has meaning only for connection oriented server: it specifies the maximum number of connection to queue for this endpoint. A connection oriented XTI client must call t_bind before calling t_connect. This differs from connect, which calls bind internally, if the socket has not bound. t_connect function A connection oriented client initiates a connection with a server by calling t_connect. The client specifies the server’s protocol address. #include<xti.h> int t_connect(int fd,const struct t_call *sendcall, struct t_call *recvcall); The second and third arguments points to a t_call structure. 1
  20. 20. struct t_call{ struct netbuf addr; /* protocol specific address*/ struct netbuf opt; /* protocol specific address */ struct netbuf udata; /* user data to accompany conn. request */ int sequence; /* for t_listen and t_accept function */ }; addr: specifies the server’s address. opt: specifies the protocol specific options desired by the caller. udata: Any user data to be transferred to the server during connection establishment. sequence: It has no significance for this function but is used when this structure is used with the t_accept function. On return from this structure, the t_call structure pointed by the recvcall argument contains information associated with the connection that is returned by the communication provider to the caller. The addr structure contains the address of the peer process, opt contains any protocol dependent options associated with the connection and udata contains any user data returned by the peers during connection establishment. Sequence number has no meaning. By default, this function does not return until the connection is completed or an error occurs. When an error occurs during connection establishment t_connect returns -1, but t_errno is set to TLOOK, requiring more code to determine exact reason. t_rcv and t_snd function By default, XTI applications can’t call normal read and write functions. Instead XTI applications must call t_rcv and t_snd. #include<xti.h> int t_rcv (int fd, void *buff, unsigned int nbytes, int *flags); int t_snd(int fd, const void *buff, unsigned int nbytes, int flags); both return: no of bytes read or written of OK, -1 on error. The first three argument are similar to first three argument to read or write: descriptor, buffer pointer and number of bytes to read or write. The flags argument to t_snd is either zero, or some combination of the following constants T_EXPEDITED – send or receive expedited (out of band) data. T_MORE – there is no more data to send or receive. This flag is provided so that multiple t_rcv or t_snd function calls can read or write what the protocol considers a logical record. XTI response to FIN or RST segment • When a TCP FIN is received for an XTI endpoint, t_rcv returns -1 with t_errno set to TLOOK. The XTI function t_look must then be called, and it returns T_ORDREL. This is called an orderly release indication. 2
  21. 21. • When a TCP RST is received for an XTI endpoint, t_rcv returns -1 with t_errno set to TLOOK. The XTI function t_look must then be called and it returns T_DISCONNECT. This is called a disconnect or an abortive release. t_look function Various events can occur for an XTI endpoint and these events can occur asynchronously. By that we mean that the application can be performing some task when an related event occurs on the endpoint. Many of such event indicate with TLOOK error constant assigned to t_errno. And to get information about event occurred, one has to call t_look on that endpoint. #include<xti.h> int t_look(int fd); return: event if OK, -1 on error. The integer value returned by this function is one of the nine events shown here. Event Description T_CONNECT Connection confirmation received T_DATA Normal data received T_DISCONNECT Disconnect received T_EXDATA Expedited data received T_GODATA Flow control restriction on normal data lifted T_GOEXDATA Flow control restriction on expedited data lifted T_LISTEN Connection indication received T_ORDREL Orderly release indication received T_UDERR Error in previously sent datagram. t_sndrel and t_rcvrel function XTI supports two ways of releasing a connection: an orderly release and an abortive release. The differences are that an abortive release does not guarantee the delivery of any outstanding data, while the orderly release guarantee this. We can send and receive an orderly release with the following functions: #include<xti.h> int t_sndrei(int fd); int t_rcvrel(int fd); both return:0 if OK, -1 on error. A process issues an orderly release by calling t_sndrel. This tells the provider that the application has no more data to send on this endpoint. For a TCP endpoint, TCP sends a FIN to the peer. The process that calls t_sndrel can continue to receive data, but it can no longer write to the descriptor. 2
  22. 22. A process acknowledges the receipt of a connection release by calling the t_rcvrel function. This process can still write to the descriptor but it can no longer read from the descriptor. t_snddis and t_rcvdis function The following two functions handle an abortive release #include<xti.h> int t_snddis(int fd, const struct t_call *call); int t_rcvdis(int fd, struct t_discon *discon); Both return: 0 if ok , -1 on error. The t_snddis function is used for two different purposes: • To perform an abortive release of an existing connection, which in terms of TCP causes an RST to be sent , and • To reject a connection request. For an abortive release of an existing connection, call argument can be a null pointer. When a T_DISCONNECT event occurs on an XTI endpoint (e.g. an RST is received on TCP), the application must receive the abortive release by calling t_rcvdis. If the discon argument is a nonnull pointer, a t_discon structure is filled in with the reason for the abortive release. struct t_discon{ struct netbuf udata; /*user data*/ int reason;/* protocol specific reason code */ int sequence; /*applicable only for server receiving connection */ }; XTI and Socket interoperability The interoperability is provided by the internet protocol suite and has nothing to do with socket or XTI.A client written using TCP or UDP interoperates with a server using the same transport protocol if the client and server speak the same application protocol. Regardless of what API is used to write either the client or the server. It is the protocol that determine the interoperability not the API we use to write the server and client. XTI Name and address conversion functions The network services library provides numerous functions to read the netconfig file. The setnetconfig function opens the file and the function getnetconfig then read the next entry in the file. endnetconfig closes the file and releases any memory that was allocated. #include <netconfig.h> void *setnetconfig(void); returns- nonnull pointer if OK, NULL on error struct netconfig *getnetconfig(void *handle); returns: nonnull pointer if OK, NULL on end of file. int endnetconfig(void *handle); return: 0 if OK, -1 on error. The pointer returned by setnetconfig is then used as the argument to the remaining two functions. Each entry in the file is returned as a netconfig structure. struct netconfig{ char nc_netid; /*”tcp”,”udp”, etc. */ 2
  23. 23. unsigned long nc_semantics; /*NC_TPI_CLTS, etc. */ unsigned long nc_flag;/* NC_VISIBLE, etc. */ char *nc_protofmly;/*”inet”, “loopback”, etc. */ char *nc_proto;/*”tcp”,”udp”, etc. */ char *nc_device;/*device name for network id */ unsigned long nc_nlookups; /* # of entries in nc_lookups */ char nc_lookups; /* list of lookup libraries */ unsigned long nc_unused[8]; }; Example using netconfig void *handle; struct netconfig *nc; handle = setnetconfig(); while((nc = getnetconfig(handle))!=NULL) /* print netconfig structure */ } endnetconfig(handle); NETPATH variable and netpath Functions The getnetconfig function returns the next entry in the file, letting us go through the entire file line by line. But for interactive programs(typically) we want the searching of the file limited only to the protocols that the user interested in. This is done by allowing the user to set an environment variable named NETPATH and then using the following functions instead of the netconfig functions described in the process section. #include<netconfig.h> void *setnetconfig(void); Returns: nonnull pointer if OK, NULL on error struct netconfig *getnetpath(void *handle); Returns: nonnull pointer if OK, NULL on end of file. int endconfig(void *handle); Returns: 0 if OK, -1 on error For example, we could set the environment variable with the shell as export NETPATH= udp:tcp; with this setting, if we coded our program as shown void *handle; struct netconfig *nc; handle = setnetconfig(); while(nc = getnetconfig(handle))!=NULL){ /* print net config structure */ } only two entries would be printed, one for UDP followed by one for TCP. The order of the two structure returned now corresponds to the order of the protocols in the environment variable, and not to the order in the netconfig file. If the NETPATH environment variable is not set, all visible entries are returned, in the order in the netconfig file. 2
  24. 24. Netdir Functions The netconfig and netpath functions let us find a desired protocol. We also need to look up a hostname and a service name, based on the protocol that we choose with the netconfig and netpath functions. This is provided by the netdir_getbyname function. #include<netdir.h> int netdir_getbyname(const struct netconfig *ncp, const struct nd_hostsery *hsp, struct nd_addrlist **alpp); returns: 0 if OK, non-zero on error void netdir_free(void *ptr, int type); we have to pass host name and service name in the nd_hostsery structure as second argument. struct nd_hostery{ char *h_host; char *h_serv; }; The third argument points to a pointer to an nd_addrlist structure and on success *alpp contains a pointer to one of these structures: struct nd_addrlist{ int n_cnt;/* no of netbuffs */ struct netbuf n_addrs; / array of netbuffs containing the addrs */ }; this nd_addrlist structure points to an array of one or more netbuf structures, each of which contains one of the host address and are dynamically allocated. To this dynamically allocated array of nd_addrlist structure, we call netdir_free with ptr pointing to the nd_addrlist structure and type set to ND_ADDRLIST. The reverse conversion- given a netbuff structure containing an address, return the host name and service name – is provided through netdir_getbyaddr. #include<netdir.h> int netdir_getbyaddr( const struct netconfig *ncp, struct nd_hostservlist *hslpp, const struct netbuf *addr); Returns: 0 if OK, nonzero on error The result is a pointer to an nd_hostservlist structure and this pointer is stored in *hslpp. Struct nd_hostservlist{ int h_cnt;/* number of hostservs */ struct nd_hostsery h_hostservs; /* the hostname/service name pairs */ }; this structures is allocated dynamically and must be freed by calling netdir_free with the type of ND_HOSTSERVLIST. t_alloc and t_free function As we have seen previously with XTI there are six structures each of which contains one ore more netbuf structures. The netbuf structures points to a buffer whose size depends on the size of the protocol address. To simplify the dynamica memory allocation of these XTI structures and the netbuf structures that they contain, t_alloc and t_free are provided. 2
  25. 25. #include<xti.h> void *t_alloc(int fd, int structtype, int fields); returns: nonnull pointer if OK, NULL on error int t_free(void *ptr, int structtype); returns: 0 if OK, -1 on error. The struct type argument specifies which of the seven XTI structures is to be allocated or freed and must be one of the constants shown here The fields argument let us specify that space for one or more netbuf strucutures should alos be allocated and initialized. fields is the bitwise OR of the constants shown here The intent of XTI model is to allow the transport layer to tell the server process when a SYN arrives from a client. Struct type Type of structure T_BIND Struct t_bind T_CALL struct t_call T_DIS struct t_dis T_INFO struct t_info T_OPTMGMT struct t_optmgmt T_UDERROR struct t_uderr T_UNITDATA struct t_unitdata Fields Allocate and initialize T_ALL All relevant fields of the structure T_ADDR addr field of t_bind, t_call T_OPT opt field of t_optmgmt, t_call T_UDATA udata field of t_call, t_discon or t_unitdata. t_getprotaddr function returns both the local and foreign protocol address associated with an endpoint. #include<xti.h> int t_getprotaddr(int fd, struct t_bind *localaddr, struct t_bind *peeraddr); returns: 0 if OK, -1 on error. The address is returned in addr member ( netbuf structure) of two t_bind structure. TCP Server using XTI XTI model allows the transport layer to tell the server process when a SYN arrives, passing client protocol address. The server process is then allowed to send either accept or reject the request. The server's TCP would not send its SYN/ACK or RST until the server process tells it what to do. 2
  26. 26. Notice the server' function calls: t_bind – indicates that end point will be accepting connection t_listen – returns when connection is available. t_accept – to accept the connection t_snddis – to reject the request. t_listen function #include<xti.h> int t_listen(int fd, struct t_call *call); returns: 0 if OK, -1 on error struct t_call{ struct netbuf addr; struct netbuf opt; struct netbuf udata; int sequence; }; The structure returned through the call pointer contains relevant parameters of the connection: addr: contains the protocol address of the client. Opt: contains any protocol specific address. Udata: contains any user data sent along with connection request. Sequence: contains unique value that identifies this connection request.this value will be used when we call t_accept or t_snddis to identifiy which connection to accept or reject. t_accept function Once the t_listen function indicates that a connection has arrived, we choose whether to accept the request or not. To accept the request the t_accept is called #include<xti.h> int t_accept ( int listenfd, int connfd, struct t_call *call); returns: 0 if OK, -1 on error. 2