FIT2100 Practical
FIT2100 Practical #5
Interprocess Communication in Unix/Linux
Week 9 Semester 2 2018
Dr Jojo Wong
Lecturer, Faculty of IT
Email: Jojo.Wong@monash.edu
© 2016-2018, Monash University
September 11, 2018
© 2016-2018, Faculty of IT, Monash University
2
Revision Status:
$Id: FIT2100-Practical-05.tex, Version 2.0 2018/09/11 16:50 Jojo $
The contents presented in this practical (including the practical tasks) were adapted from
David Curry’s texts.
• David A. Curry (1989). C on the UNIX System, O’Reilly.
• David A. Curry (1996). UNIX Systems Programming for SVR4, O’Reilly.
© 2016-2018, Faculty of IT, Monash University
CONTENTS 3
Contents
1 Background 5
2 Pipes 5
2.1 Creating a pipe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.2 Closing the pipe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.3 Try It Yourself (0.5 marks) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
3 Named Pipes (FIFO) 8
3.1 Creating a named pipe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
3.2 Using a named pipe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
3.3 Try It Yourself (1.5 marks) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
4 Message Queues 11
4.1 Setting up a message queue . . . . . . . . . . . . . . . . . . . . . . . . . . . 12
4.2 Exchanging data with a message queue . . . . . . . . . . . . . . . . . . . . . 12
4.3 Practical Task 1 (3 marks) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
5 Sockets 17
5.1 How does the socket work? . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
5.2 Creating a socket . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
5.2.1 What does the server process need to do? . . . . . . . . . . . . . . . 18
5.2.2 What does the client process need to do? . . . . . . . . . . . . . . . . 20
5.3 Transferring data with a socket . . . . . . . . . . . . . . . . . . . . . . . . . 20
© 2016-2018, Faculty of IT, Monash University
CONTENTS 4
5.4 Practical Task 2 (3 marks) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
© 2016-2018, Faculty of IT, Monash University
1 Background 5
1 Background
This practical is aimed to extend your knowledge on the concepts of interprocess communica-
tion in the Unix/Linux environment. We will explore four different mechanisms which enable
two processes executing on the same computer system to communicate with each other: basic
pipes, named pipes (FIFO), message queues, and sockets.
The pre-lab preparation (under the Try It Yourself subsections in Section 2.3 and Section
3.3) should be completed before attending the lab session. The two practical tasks presented
under Section 4.3 and Section 5.4 are to be assessed in the lab.
Before you attempt any of the tasks in this prac, create a folder named PRAC05 under the
FIT2100 folder (~/Document/FIT2100). Save all your source files under this PRAC05 folder.
2 Pipes
A pipe is the most basic form of interprocess communication that can be used to join two
processes together. It is setup by a special pair of file descriptors which connect the two
processes in communication.
When Process A writes data to its pipe file descriptor, Process B can read that data from its
pipe file descriptor. Likewise, when Process B writes to its pipe file dsecriptor, Process A can
then read the data from its pipe file descriptor.
In the Unix shell, you would have used the pipeline command (|). For example, the following
Unix command sends the output of ls to wc -l to indicate the number of files or directories
found in the current directory.
1 $ l s | wc − l
2.1 Creating a pipe
Programmatically, a pipe is created with the built-in function pipe() defined under the
otherwise indicating a pipe cannot be created, and the reason for failure is stored in the
external variable errno.
© 2016-2018, Faculty of IT, Monash University
2.2 Closing the pipe 6
1 #i n c l u d e
2
3 i n t p i p e ( i n t p i p e f d [ 2 ] ) ;
By invoking the pipe() function, two file descriptors are created: (i) pipefd[0] is open for
reading; (ii) pipefd[1] is open for writing. Basically, the two file descriptors are connected as
a pipe — which allows data written at the write end of the pipe (pipefd[1]) to be readable
from the read end of the pipe (pipefd[0]).
After creating a pipe, the calling process (who makes the call to pipe()) usually creates a
child process by calling fork(). The two related processes can then communicate using the
pipe in one direction. Thus, either the parent may send data to the child or the child may send
data to the parent; but not in both directions. If a bi-directional communication is required,
two pipes must be setup: one for the parent to use to send data to the child; and one for the
child to use to send data to the parent.
Note: Each pipe has a limited buffer size described by the constant PIPE_BUF defined under
the
tations; for Linux, the minimum capacity is 4096 bytes. Also, it is possible to have more than
one process writing to a pipe by applying the function dup() or dup2() (from
on the file descriptor.
2.2 Closing the pipe
Communication can take place as long as both the read and write ends of a pipe are open. If
one end of a pipe is closed, that would affect the communication between the two processes:
• If the read end of a pipe has been closed, any attempt to write to the pipe will result in
a SIGPIPE signal being sent to the process attempting to write.
• If the write end of the pipe has been closed, any further reads from the pipe will return
0 as an indicator of the end-of-file.
2.3 Try It Yourself (0.5 marks)
Create the program “pipecat.c” presented in the next page and run it. Your task is to
describe what the program does by adding a comment at the beginning of the source file.
© 2016-2018, Faculty of IT, Monash University
2.3 Try It Yourself (0.5 marks) 7
1 #i n c l u d e
2 #i n c l u d e
3 #i n c l u d e
4 #i n c l u d e
5 #i n c l u d e
6
7
8 i n t main ( i n t argc , cha r ∗ argv [ ] ) {
9
10 pid_t p id ;
11 i n t p i p e f d [ 2 ] ;
12 i n t s t a t u s ;
13
14 /∗
15 ∗ Crea te a p i p e .
16 ∗/
17 i f ( p i p e ( p i p e f d ) < 0) {
18 p e r r o r ( " p i p e " ) ;
19 e x i t ( 1 ) ;
20 }
21
22 /∗
23 ∗ Crea te a c h i l d p r o c e s s .
24 ∗/
25 i f ( ( p i d = f o r k ( ) ) < 0) {
26 p e r r o r ( " f o r k " ) ;
27 e x i t ( 1 ) ;
28 }
29
30 /∗
31 ∗ The c h i l d p r o c e s s e x e cu t e s " ca t " .
32 ∗/
33 i f ( p i d == 0) {
34 /∗
35 ∗ Attach the s t anda rd i npu t to the p i p e .
36 ∗/
37 dup2 ( p i p e f d [ 0 ] , 0) ;
38 c l o s e ( p i p e f d [ 1 ] ) ;
39
40 e x e c l ( "/ b in / ca t " , " ca t " , NULL) ;
41 p e r r o r ( " exec " ) ;
42 _ex i t (127) ;
43 }
44
45 /∗
46 ∗ The pa r en t p r o c e s s i s not r e a d i n g from the p i p e .
47 ∗/
48 c l o s e ( p i p e f d [ 0 ] ) ;
49
50
51
© 2016-2018, Faculty of IT, Monash University
3 Named Pipes (FIFO) 8
52 /∗
53 ∗ Write the ma i l message to the p i p e .
54 ∗/
55 w r i t e ( p i p e f d [ 1 ] , " G r e e t i n g s . \ n\n" , 12) ;
56 w r i t e ( p i p e f d [ 1 ] , " Th i s i s your program sa y i n g h e l l o . \ n" , 35) ;
57 w r i t e ( p i p e f d [ 1 ] , "Hope you en j oy t h i s week ' s p rac . \ n\n" , 34) ;
58
59 /∗
60 ∗ C lo s e the p i p e and wa i t f o r the c h i l d to e x i t .
61 ∗/
62 c l o s e ( p i p e f d [ 1 ] ) ;
63 wa i t p i d ( p id , &s t a t u s , 0) ;
64
65 e x i t ( 0 ) ;
66 }
3 Named Pipes (FIFO)
Pipes introduced in the previous section work just like files. They are associated with file
descriptors and are accessed by the low-level I/O functions — read() and write(). However,
pipes do not exist in the file system and hence they do not have filenames or path names. One
major limitation of pipes is that they can only be used between related processes (parent and
child pairs).
Named pipes, also known as FIFO special files, are a variation that does associate with an
entry in the file system. With a name attached, named pipes can then be used by processes
that are unrelated processes (not parent and child pairs) for both reading and writing.
3.1 Creating a named pipe
To create a named pipe, the built-in function mkfifo() is used.
1 #i n c l u d e
2 #i n c l u d e
3
4 i n t mk f i f o ( con s t cha r ∗pathname , mode_t mode ) ;
Upon successful creation of a named pipe, 0 is returned. In the case of a failure, -1 is returned
(and errno is set accordingly to indicate the error).
© 2016-2018, Faculty of IT, Monash University
3.2 Using a named pipe 9
The first parameter pathname specifies the path name of the named pipe to be created, where
the path name must not already exist. The second parameter mode indicates the set of file
permissions for the named pipe. For example, the following statement creates a named pipe
“/etc/mypipe” that is readable and writable by all users.
1 #i n c l u d e
2 #i n c l u d e
3
4 mkf i f o ( “/ e t c /mypipe” , 0666) ;
3.2 Using a named pipe
Once a named pipe (FIFO) has been created, it must be opened for use with the open()
function which we have been using for opening or creating a file for reading and/or writing
(recall the tasks that we have done in Practical 2). Once the named pipe has been opened,
the pipe can be accessed for reading and writing with the low-level I/O functions — read()
and write() .
Just like the unnamed pipes, an attempt to read from an empty FIFO will cause the process
to block and waiting for a write. An attempt to write to a FIFO that has no processes reading
it will result in a SIGPIPE signal. When the last writer on a FIFO closes it, the reader will
receive an end-of-file indication.
3.3 Try It Yourself (1.5 marks)
The following two programs deploy a FIFO (named pipe) for communication as a server and
a client. First, understand what the server program “fifoserver.c” does. Your task then
is to complete the partial code presented for the client program “fifoclient.c” and submit
the completed program as part of the pre-lab submission.
The server program creates a FIFO for reading and displays anything it receives from the
named pipe to the standard output. The client program, on the other hand, opens the FIFO
created by the server program for writing, and writes anything that it reads from the standard
input to the named pipe.
© 2016-2018, Faculty of IT, Monash University
3.3 Try It Yourself (1.5 marks) 10
The server program “fifoserver.c”:
1 #i n c l u d e
2 #i n c l u d e
3 #i n c l u d e
4 #i n c l u d e
5 #i n c l u d e
6 #i n c l u d e
7
8 #de f i n e FIFONAME ” my f i f o ”
9
10 i n t main ( i n t argc , cha r ∗ argv [ ] )
11 {
12 i n t n , fd ;
13 char b u f f e r [ 1 0 2 4 ] ;
14
15 /∗
16 ∗ Remove any p r e v i o u s FIFO .
17 ∗/
18 u n l i n k (FIFONAME) ;
19
20 /∗
21 ∗ Crea te the FIFO .
22 ∗/
23 i f ( mk f i f o (FIFONAME, 0666) < 0) {
24 p e r r o r ( " s e r v e r : mk f i f o " ) ;
25 e x i t ( 1 ) ;
26 }
27
28 /∗
29 ∗ Open the FIFO f o r r e a d i n g .
30 ∗/
31 i f ( ( fd = open (FIFONAME, O_RDONLY) ) < 0) {
32 p e r r o r ( " s e r v e r : open" ) ;
33 e x i t ( 1 ) ;
34 }
35
36 /∗
37 ∗ Read from the FIFO u n t i l end−of− f i l e and
38 ∗ p r i n t what we get on the s t anda rd i npu t .
39 ∗/
40 wh i l e ( ( n = read ( fd , b u f f e r , s i z e o f ( b u f f e r ) ) ) > 0) {
41 w r i t e (1 , b u f f e r , n ) ;
42 }
43
44 c l o s e ( fd ) ;
45 e x i t ( 0 ) ;
46 }
© 2016-2018, Faculty of IT, Monash University
4 Message Queues 11
The client program “fifoclient.c”:
1 #i n c l u d e
2 #i n c l u d e
3 #i n c l u d e
4 #i n c l u d e
5 #i n c l u d e
6 #i n c l u d e
7
8 #de f i n e FIFONAME ” my f i f o ”
9
10 i n t main ( i n t argc , cha r ∗ argv [ ] )
11 {
12 i n t n , fd ;
13 char b u f f e r [ 1 0 2 4 ] ;
14
15 /∗
16 ∗ Open the FIFO f o r w r i t i n g . I t was c r e a t e d by the s e r v e r .
17 ∗/
18 i f ( ( fd = open (FIFONAME, O_WRONLY) ) < 0) {
19 p e r r o r ( " c l i e n t : open" ) ;
20 e x i t ( 1 ) ;
21 }
22
23 /∗
24 ∗ YOUR TASK:
25 ∗ Read from the s t anda rd input , and copy the data to the FIFO .
26 ∗/
27
28 c l o s e ( fd ) ;
29 e x i t ( 0 ) ;
30 }
To execute these two programs, run the server program in the background before running the
client program. For example:
1 $ . / f i f o s e r v e r &
2 $ . / f i f o c l i e n t < / e t c /passwd
4 Message Queues
Message queues is another mechanism for interprocess communication in Unix. A message
queue is represented as a linked list of message “packets” exchanged between processes, where
each of a fixed maximum size. To preserve the order in which messages arrive, messages are
© 2016-2018, Faculty of IT, Monash University
4.1 Setting up a message queue 12
added to the end of the queue. However, messages can be received in any order determined
by the receiving processes based on the message type.
4.1 Setting up a message queue
A message queue is described as a structure of type struct msqid_ds defined in the
library and each queue is defined by a unique identifier (queue id). Before a process attempts
to use a message queue, it must obtain the queue identifier by calling the msgget() function.
1 #i n c l u d e
2 #i n c l u d e
3 #i n c l u d e
4
5 i n t msgget ( key_t key , i n t msg f l g ) ;
The first parameter key specifies the key to be associated with the message queue. If the key
contains the value of zero (defined by the constant IPC_PRIVATE), a new message queue is
always created. If the key contains a non-zero value, either a new queue is created, or the
identifier of an existing queue is returned. This depends on whether the second parameter
msgflg is set to the constant IPC_CREAT and whether the given key already exists.
If the msgflg parameter is set to both IPC_CREAT and IPC_EXCL and a message queue already
exists with the given key, -1 is returned indicating that the message queue cannot be created
and the error that occurred is set in errno. However, if the creation is successful, the message
queue identifier is returned.
4.2 Exchanging data with a message queue
Two functions are used for sending and receiving messages on a message queue — msgsnd()
and msgrcv().
1 #i n c l u d e
2 #i n c l u d e
3 #i n c l u d e
4
5 i n t msgsnd ( i n t msqid , con s t vo i d ∗msgbuf , s i z e_t msgsz , i n t msg f l g ) ;
6 i n t msgrcv ( i n t msqid , v o i d ∗msgbuf , s i z e_t msgsz , l ong msgtype , i n t
msg f l g ) ;
© 2016-2018, Faculty of IT, Monash University
4.2 Exchanging data with a message queue 13
Sending data on a message queue
The msgsnd() function takes on four arguments: a message queue identifier (queue id), a
reference to a user-defined message buffer, an integer indicating the size of the message,
and a flag option. A message pointed by msgbuf (the second parameter) is sent on the
message queue identified by msqid (the first parameter). If the message is successfully sent,
the function return 0; -1 is returned otherwise highlighting an error has occurred.
Messages exchanged on a message queue defined using the following structure:
1 s t r u c t msgbuf {
2 l ong mtype ; /∗ message type , must be > 0 ∗/
3 char mtext [ ] ; /∗ message data ∗/
4 }
The mtype field of the structure contains a positive integer value which can be used by the
receiving process to select which type of message to receive. The mtext field represents the
message buffer whose size is defined by msgsz in terms of bytes (the third parameter of the
msgsnd() function).
Note: By default, if the message queue is full, the msgsnd() call will block until the queue
becomes empty. If the msgflg parameter is set to IPC_NOWAIT, the msgsnd() call will fail
with a failure code being returned.
Receiving data from a message queue
The msgrcv() function takes on five arguments: a message queue identifier (queue id), a
reference to a user-defined message buffer, an integer indicating the size of the message re-
ceived, an integer specifying the message type, and a flag option. If the message is successfully
received, the number of bytes stored in the message buffer referenced by msgbuf is returned.
If an error occurs, -1 is returned and the error is set in errno accordingly.
When receiving messages, the receiving process must specify the message type (msgtype) in
the msgrcv() function.
• If msgtype is set to the value of 0, the first message in the queue will be read.
• If msgtype is set to a value greater than 0, the first message in the queue of type
msgtype will be read.
• If msgtype is set to a value less than 0, the first message in the queue with the lowest
type less than or equal to the absolute value of msgtype will be read.
© 2016-2018, Faculty of IT, Monash University
4.3 Practical Task 1 (3 marks) 14
Note: If no message of the requested type is available in the message queue and the parameter
msgflg is set to IPC_NOWAIT, the msgrcv() call will fail with a failure code being returned.
Otherwise, the msgrcv() call will block until a message of the specified type arrives in the
message queue or the queue is removed from the system.
4.3 Practical Task 1 (3 marks)
In this first practical task, we will re-implement the client-server program from Section 3.3
using a message queue. Your task is to complete the partial code given for the server
(“msqserver.c”) program and the client program (“msqclient.c”).
The server program creates a message queue in order to receive messages sent from the
client program. Messages of “Type 1” received by the server program will be displayed on the
standard output. A message of “Type 2” is used by the client to inform the server that there
are no more messages. Note that the client program reads from the standard input and sends
the data as messages of “Type 1” to the server.
Note: You should run these two programs as before with the server program first running at
the background. You should also find out the purpose of calling the msgctl() function in the
server program.
© 2016-2018, Faculty of IT, Monash University
4.3 Practical Task 1 (3 marks) 15
The server program “msqserver.c”:
1 #i n c l u d e
2 #i n c l u d e
3 #i n c l u d e
4 #i n c l u d e
5 #i n c l u d e
6 #i n c l u d e
7
8 #de f i n e MSQKEY 34858
9 #de f i n e MSQSIZE 32
10
11 s t r u c t msgbuf {
12 l ong mtype ;
13 char mtext [MSQSIZE ] ;
14 } ;
15
16
17 i n t main ( i n t argc , cha r ∗ argv [ ] )
18 {
19 key_t key ;
20 i n t n , msqid ;
21 s t r u c t msgbuf mbuf ;
22
23 /∗
24 ∗ Crea te a new message queue . IPC_CREAT i s used to c r e a t e i t ,
25 ∗ and IPC_EXCL to make s u r e i t does not e x i s t a l r e a d y .
26 ∗ I f you get an e r r o r on t h i s , someth ing on your system i s
27 ∗ u s i n g the same key −−− change MSQKEY to someth ing e l s e .
28 ∗/
29
30 key = MSQKEY;
31 i f ( ( msqid = msgget ( key , IPC_CREAT | IPC_EXCL | 0666) ) < 0) {
32 p e r r o r ( " s e r v e r : msgget " ) ;
33 e x i t ( 1 ) ;
34 }
35
36 /∗
37 ∗ Rece i v e messages from the queue .
38 ∗ Messages o f type 1 a r e to be p r i n t e d on the s t anda rd output ;
39 ∗ a message o f type 2 i n d i c a t e s t ha t no more data .
40 ∗/
41
42 wh i l e ( ( n = msgrcv ( msqid , &mbuf , MSQSIZE , 0 , 0) ) > 0) {
43 i f (mbuf . mtype == 1) {
44 /∗
45 ∗ YOUR TASK:
46 ∗ Write messages o f type 1 to the s t anda rd output .
47 ∗/
48 }
49
© 2016-2018, Faculty of IT, Monash University
4.3 Practical Task 1 (3 marks) 16
50 e l s e i f (mbuf . mtype == 2) {
51 /∗
52 ∗ Remove the message queue from the system .
53 ∗/
54 i f ( msgc t l ( msqid , IPC_RMID , ( s t r u c t msqid_ds ∗) 0) < 0) {
55 p e r r o r ( " s e r v e r : msgc t l " ) ;
56 e x i t ( 1 ) ;
57 }
58 }
59 }
60 e x i t ( 0 ) ;
61 }
The client program “msqclient.c”:
1 #i n c l u d e
2 #i n c l u d e
3 #i n c l u d e
4 #i n c l u d e
5 #i n c l u d e
6 #i n c l u d e
7 #i n c l u d e
8
9 #de f i n e MSQKEY 34858
10 #de f i n e MSQSIZE 32
11
12 s t r u c t msgbuf {
13 l ong mtype ;
14 char mtext [MSQSIZE ] ;
15 } ;
16
17
18 i n t main ( i n t argc , cha r ∗ argv [ ] )
19 {
20 key_t key ;
21 i n t n , msqid ;
22 s t r u c t msgbuf mbuf ;
23
24 /∗
25 ∗ Get a message queue . The s e r v e r must have c r e a t e d i t .
26 ∗/
27 key = MSQKEY;
28 i f ( ( msqid = msgget ( key , 0666) ) < 0) {
29 p e r r o r ( " c l i e n t : msgget " ) ;
30 e x i t ( 1 ) ;
31 }
32
33
34
© 2016-2018, Faculty of IT, Monash University
5 Sockets 17
35 /∗
36 ∗ YOUR TASK:
37 ∗ Read data from the s t anda rd i npu t and send as messages o f type 1 .
38 ∗/
39
40 /∗
41 ∗ Send a message o f type 2 to i n d i c a t e no more data .
42 ∗/
43 mbuf . mtype = 2 ;
44 memset (mbuf . mtext , 0 , MSQSIZE) ;
45
46 i f ( msgsnd ( msqid , &mbuf , MSQSIZE , 0) < 0) {
47 p e r r o r ( " c l i e n t : msgsnd" ) ;
48 e x i t ( 1 ) ;
49 }
50 e x i t ( 0 ) ;
51 }
5 Sockets
Sockets are similar to named pipes in which they provide an address in the file system where
unrelated processes may use for communication. However, sockets are different from named
pipes in terms of how they are accessed. Sockets are implemented using the socket interface
which consists of a set of functions to create, destroy and transfer data.
Interprocess communication with sockets is often described in terms of the client-server model
which we have seen. The server program first requests the operating system (OS) for a socket.
Once it is given with a socket, it then assigns a well-known name (address) to that socket.
The name should always be the same, such that the client programs will know how to contact
to the server program via that socket.
5.1 How does the socket work?
After the socket has been named, the server process listens on the socket for any connection
requests from the client processes. When a connection request arrives, the server can choose
to accept or reject. If the server accepted the connection, the OS connects the server and the
client together at the socket.
The client process, on the other hand, begins by requesting for a socket from the OS. It then
asks the OS to join its socket to a socket of a specific name that it would want to establish
a connection. The OS attempts to search for a socket with the specific name. If such socket
© 2016-2018, Faculty of IT, Monash University
5.2 Creating a socket 18
was found, the OS sends a connection request to the process which is listening on that socket
(the server process).
Once the server and the client are connected at the socket, they can then communicate with
each other by reading and writing data to and from the socket, just like a pipe.
5.2 Creating a socket
A socket is created using the built-in function socket() defined in the
library. A socket descriptor represented as a small non-negative integer (similar to a file
descriptor) is returned when a socket is successfully created. If the function failed to create
the socket, -1 is returned and the reason for the failure is indicated in errno.
1 #i n c l u d e
2 #i n c l u d e
3
4 i n t s o c k e t ( i n t domain , i n t type , i n t p r o t o c o l ) ;
The first parameter domain specifies the address family in which the address of the socket
should be interpreted. For communication between processes on the same computer system,
the domain parameter is assigned with the constant AF_UNIX (the Unix domain) — in which
the addresses are treated as Unix path names.
The second parameter type defines which type of communication semantic (channel) that
the socket supports. The two types which are of interest are SOCK_STREAM and SOCK_DGRAM.
SOCK_STREAM supports a bi-directional communication with continuous byte streams — it is
guaranteed that the data is delivered in the order it was sent, and no data can be sent until
the connection is established. SOCK_DGRAM supports datagrams which are distinct packets of
data — it is not guaranteed that the data is delivered in the order it was sent, and it is even
not guaranteed that the data is delivered at all.
The last parameter protocol specifies a particular protocol to be used with the socket. The
protocol number to use is specific to the communication domain (the address family). For
the Unix domain (AF_UNIX), the corresponding protocol family is defined by the constant
PF_UNIX. However, if the protocol parameter is set to 0, the OS will determine which is the
suitable protocol.
5.2.1 What does the server process need to do?
In order for the server process to exchange data with the client process, it has to invoke the
following set of functions.
© 2016-2018, Faculty of IT, Monash University
5.2 Creating a socket 19
Naming a socket Once the socket has been created, the server process must provide a name
(or a known address) to the socket by using the bind() function; otherwise the socket cannot
be used by the client processes. If binding is successful, 0 is returned; if it fails to bind (often
due to the address has already been used), -1 is returned and the errno is set accordingly.
1 #i n c l u d e
2 #i n c l u d e
3
4 i n t b ind ( i n t sock fd , con s t s t r u c t sockaddr ∗name , i n t a dd r l e n ) ;
Once the binding has been completed, the communication channel referenced by the first
parameter sockfd is assigned with the address described by the second parameter name. The
length of the address is indicated by the last parameter addrlen.
Note: The name parameter is a structure of a generic type (sockaddr) for socket addresses.
In the Unix domain, the socket address is actually of type sockaddr_un with the following
structure — where the sun_family represents the address family (set to AF_UNIX) and the
sun_path represents the path name of the socket.
1 s t r u c t sockaddr_un {
2 s h o r t sun_fami ly ; /∗ AF_UNIX ∗/
3 char sun_path [ 1 0 8 ] ; /∗ pathname ∗/
4 }
Waiting for connections On a stream-based communication via a socket, the server process
must notify the OS when it is ready to accept any connection requests from client processes.
The function listen() is used for this purpose. On success, the function returns 0; -1 is
returned otherwise.
1 #i n c l u d e
2 #i n c l u d e
3
4 i n t l i s t e n ( i n t sock fd , i n t back l og ) ;
The socket that the server is listening on for connection requests is referenced by the first
parameter sockfd. The second parameter backlog indicates the number of pending connec-
tion requests allowed. If a connection request arrives when the queue of pending connections
is full, the client process will received an error indicating that the connection is refused.
Accepting connections When the server process has decided to accept the connection
request, it uses the function accept() to achieve this. A new socket descriptor (a non-negative
integer) will be returned, in which the server process can use this new socket descriptor to
© 2016-2018, Faculty of IT, Monash University
5.3 Transferring data with a socket 20
communicate with the client process. The existing socket descriptor from the server — the
one with a known address assigned — is used for accepting other connection requests.
1 #i n c l u d e
2 #i n c l u d e
3
4 i n t accep t ( i n t sock fd , s t r u c t sockaddr ∗name , i n t ∗ add r l e n ) ;
If the connection is accepted successfully, the socket address of the client process will be
referenced by the second parameter name and its address length is stored in the last parameter
addrlen. If unsuccessful, -1 is returned and the reason for the failure is set in errno.
5.2.2 What does the client process need to do?
The client process, on the other hand, is required to connect to the server process that it
intends to communicate on a stream-based socket (SOCK_STREAM). To do this, the client
process should invoke the connect() function.
1 #i n c l u d e
2 #i n c l u d e
3
4 i n t connect ( i n t sock fd , con s t s t r u c t sockaddr ∗name , i n t a dd r l e n ) ;
The first parameter sockfd refers to a socket to be connected to the server process which the
client intends to communicate with. The known address of the socket from the server end is
referenced by the second paramater (name) where the length of the address is specified by the
last parameter (addrlen). When the connection is successful, 0 is returned; -1 is returned
otherwise and the error is set in errno accordingly.
5.3 Transferring data with a socket
On a stream-based communcation via a socket, the low-level I/O functions read() and
write() can be used by the client and server processes. However, there are two functions
specifically used with stream-based sockets — recv() and send().
1 #i n c l u d e
2 #i n c l u d e
3
4 i n t r e c v ( i n t sock fd , cha r ∗ bu f f e r , i n t l en , i n t f l a g s ) ;
5 i n t send ( i n t scok fd , con s t cha r ∗ bu f f e r , i n t l en , i n t f l a g s ) ;
© 2016-2018, Faculty of IT, Monash University
5.4 Practical Task 2 (3 marks) 21
These two functions are in fact identical to read() and write(). However, there is a fourth
argument which allows the program to specify options (described by flags) that would affect
the way in which the data is sent or received.
One such option is defined by the constant MSG_PEEK — if this option is set in the first call
to recv(), the data is copied to the buffer as usual but it is not “consumed” (the data is
not yet read). The next call to recv() will return the same data. In a way, this enables a
program to have a peek at the received data and can then decide what to do with the data
without having to actually “read” it.
Destroying the socket The communication channel established by a socket can be termi-
nated by using the close() system call, except that if the socket is stream-based, the closure
on the socket will block until all the data has been transmitted. Alternatively, the shutdown()
function can be used.
1 #i n c l u d e
2 #i n c l u d e
3
4 i n t shutdown ( i n t sock fd , i n t how) ;
The first parameter sockfd refers to the socket to be shut down and the second parameter
how indicates the way the socket should be shut down. If how is set to 0, further reads will be
disallowed; if how is 1, further writes will be disallowed; if how is 2, further reads and writes
will be disallowed.
5.4 Practical Task 2 (3 marks)
In this practical task, we will re-implement the client-server program from Section 3.3 and
Section 4.3. Complete the partial code given for the server program (“socketserver.c”) and
the client program (“socketclient.c”), and run both programs as before with the server
program first running at the background.
© 2016-2018, Faculty of IT, Monash University
5.4 Practical Task 2 (3 marks) 22
The server program “socketserver.c”:
1 #i n c l u d e
2 #i n c l u d e
3 #i n c l u d e
4 #i n c l u d e
5 #i n c l u d e
6 #i n c l u d e
7 #i n c l u d e
8
9 #de f i n e SOCKETNAME “mynewsocket ”
10
11
12 i n t main ( i n t argc , cha r ∗ argv [ ] )
13 {
14 char b u f f e r [ 1 0 2 4 ] ;
15 i n t n , sock , nsock , l e n ;
16 s t r u c t sockaddr_un name ;
17
18 /∗
19 ∗ Crea te a s o ck e t .
20 ∗/
21 i f ( ( sock = sock e t (AF_UNIX, SOCK_STREAM, 0) ) < 0) {
22 p e r r o r ( " s e r v e r : s o c k e t " ) ;
23 e x i t ( 1 ) ;
24 }
25
26
27 /∗
28 ∗ Crea te the add r e s s o f the s e r v e r .
29 ∗/
30 memset(&name , 0 , s i z e o f ( s t r u c t sockaddr_un ) ) ;
31 name . sun_fami ly = AF_UNIX ;
32 s t r c p y (name . sun_path , SOCKETNAME) ;
33 l e n = s i z e o f ( name . sun_fami ly ) + s t r l e n (name . sun_path ) ;
34
35
36 /∗
37 ∗ Remove any p e r v i o u s s o ck e t .
38 ∗/
39 u n l i n k (name . sun_path ) ;
40
41
42 /∗
43 ∗ Bind the s o ck e t to the add r e s s .
44 ∗/
45 i f ( b ind ( sock , ( s t r u c t sockaddr ∗) &name , SUN_LEN(&name) ) < 0) {
46 p e r r o r ( " s e r v e r : b ind " ) ;
47 e x i t ( 1 ) ;
48 }
49
© 2016-2018, Faculty of IT, Monash University
5.4 Practical Task 2 (3 marks) 23
50 /∗
51 ∗ L i s t e n f o r c onn e c t i o n s .
52 ∗/
53 i f ( l i s t e n ( sock , 5) < 0) {
54 p e r r o r ( " s e r v e r : l i s t e n " ) ;
55 e x i t ( 1 ) ;
56 }
57
58
59 /∗
60 ∗ Accept a connec t i on .
61 ∗/
62 i f ( ( nsock = accep t ( sock , ( s t r u c t sockaddr ∗) &name , &l e n ) ) < 0) {
63 p e r r o r ( " s e r v e r : a ccep t " ) ;
64 e x i t ( 1 ) ;
65 }
66
67
68 /∗
69 ∗ YOUR TASK:
70 ∗ Read from the new sock e t u n t i l end−of− f i l e and
71 ∗ p r i n t a l l the data r e c e i v e d on the s t anda rd output .
72 ∗/
73
74
75 c l o s e ( nsock ) ;
76 c l o s e ( sock ) ;
77 e x i t ( 0 ) ;
78 }
The client program “socketclient.c”:
1 #i n c l u d e
2 #i n c l u d e
3 #i n c l u d e
4 #i n c l u d e
5 #i n c l u d e
6 #i n c l u d e
7 #i n c l u d e
8
9 #de f i n e SOCKETNAME “mynewsocket ”
10
11 i n t main ( i n t argc , cha r ∗ argv [ ] )
12 {
13 char b u f f e r [ 1 0 2 4 ] ;
14 i n t n , sock , l e n ;
15 s t r u c t sockaddr_un name ;
16
17
© 2016-2018, Faculty of IT, Monash University
5.4 Practical Task 2 (3 marks) 24
18 /∗
19 ∗ Crea te a s o ck e t .
20 ∗/
21 i f ( ( sock = sock e t (AF_UNIX, SOCK_STREAM, 0) ) < 0) {
22 p e r r o r ( " c l i e n t : s o c k e t " ) ;
23 e x i t ( 1 ) ;
24 }
25
26
27 /∗
28 ∗ Crea te the add r e s s o f the s e r v e r .
29 ∗/
30 memset(&name , 0 , s i z e o f ( s t r u c t sockaddr_un ) ) ;
31 name . sun_fami ly = AF_UNIX ;
32 s t r c p y (name . sun_path , SOCKETNAME) ;
33 l e n = s i z e o f ( name . sun_fami ly ) + s t r l e n (name . sun_path ) ;
34
35
36 /∗
37 ∗ Connect to the s e r v e r .
38 ∗/
39 i f ( connect ( sock , ( s t r u c t sockaddr ∗) &name , SUN_LEN(&name) ) < 0) {
40 p e r r o r ( " c l i e n t : connect " ) ;
41 e x i t ( 1 ) ;
42 }
43
44
45 /∗
46 ∗ YOUR TASK:
47 ∗ Read from the s t anda rd i npu t and send the data to the s o ck e t .
48 ∗/
49
50
51 c l o s e ( sock ) ;
52 e x i t ( 0 ) ;
53 }
© 2016-2018, Faculty of IT, Monash University
Background
Pipes
Creating a pipe
Closing the pipe
Try It Yourself (0.5 marks)
Named Pipes (FIFO)
Creating a named pipe
Using a named pipe
Try It Yourself (1.5 marks)
Message Queues
Setting up a message queue
Exchanging data with a message queue
Practical Task 1 (3 marks)
Sockets
How does the socket work?
Creating a socket
What does the server process need to do?
What does the client process need to do?
Transferring data with a socket
Practical Task 2 (3 marks)