ref: 87288afa5ac476efb3ef1ed40df658427339f13e
dir: /ch6.ms/
.so tmacs .BC 6 "Networking .BS 2 "Network connections .LP .ix "network connection Plan 9 is a distributed system. But even if it was as its ancestor, UNIX, a .ix "distributed system .ix "UNIX centralized system that was designed just for one machine, it is very important to be able to use the network to provide services for other machines and to use .ix "computer network .ix "network services services from others. All the operating systems that are in use today provide abstractions similar to the one whose interface is described here, to let you use the network. .PP This chapter may be hard to understand if you have not attended a computer networks course, but we try to do our best to explain how to use the network in any case. All the programs you have used to browse the Web, exchange electronic mail, etc. are implemented using .ix mail .ix web interfaces that are similar to the ones described below (they use to be more complex, though). .PP In general, things work as for any other service provided by the operating system. First, the system provides some abstraction for using the network. As we will be seeing, Plan 9 uses also the file abstraction as its primary interface for using networks. Of course, files used to represent a network have a special meaning, i.e., behave in a particular way, but they are still used like files. Other operating systems use a whole bunch of extra system calls instead, to provide the interface for their network abstraction. Nevertheless, the ideas, and the programmatic interface that we will see, are very similar. .PP Upon such system-provided abstraction, library functions may provide a more convenient .ix library interface for the application programmer. And of course, in the end, there are many programs already installed in the system that, using these libraries, provide some services for the user. .PP A network in Plan 9 is a set of devices that provide the ability to talk with .ix "network device other machines using some physical medium (e.g, some type of wire or the air for radio communication). .PP A network device in Plan 9 may be an actual piece of hardware, but it can also be a piece of software used to speak some protocol. For example, most likely, your PC includes an ethernet card. It uses an RJ45 connector to plug .ix RJ45 .ix ethernet your computer to an ethernet network (just some type of cabling and conventions). The interface for the ethernet device in Plan 9 is just a file tree, most likely found at .ix "[/net] file~system .ix [ether0] .CW /net/ether0 .P1 ; !!lc /net/ether0 0 1 2 addr clone ifstats stats .P2 .LP Machines attached to the wire have addresses, used by the network hardware to .ix "network address identify different machines attached to the wire. Networks using wireless communication are similar, but use the air as their “wire”. We can use the file interface provided by Plan 9 for our ethernet device to find out which one is its address: .P1 ; cat /net/ether0/addr 000c292839fc; .P2 .LP As you imagine, this file is just an interface for using your ethernet device, in this case, for asking for its address. .PP Once you have the hardware (e.g., the ethernet card) for exchanging messages with other machines attached to the same medium (wiring or air), your machine and exchange bytes with them. The problem remains of how to send messages to any machine in the Internet, even if it is not attached to the same wire your machine is attached at. One protocol very important to the Internet, IP (Internet .ix "internet protocol .ix IP Protocol), is provided in Plan 9 by a device driver called IP. This protocol is called a network protocol because it gives an address to each machine in the Internet, its IP-address, and it knows how to reach any machine, given its address. The interface for the IP network in Plan 9 is similar to the one we saw for Ethernet: .P1 ; lc /net/ipifc 0 1 clone stats .P2 .ix [/net/ipifc] .LP This is not yet enough for communicating with programs across the internet. Using IP, you may talk to one machine (and IP cares about how to reach that machine through the many different wires and machines you need to cross). But you need to be able to talk to one .I process . .ix protocol This is achieved by using another protocol, built upon the network protocol. This kind of protocol gives addresses for “mailboxes” within each machine, called .I ports . .ix "network port Therefore, an address for this protocol is a combination of a machine address (used to reach that machine through the underlying network protocol) and a .I port number. .ix "port number .PP In few words, the network protocol gives addresses for each machine and knows how to exchange messages between machines. Today, you are going to use IP as your network protocol. The transport protocol gives port numbers for processes to use, and knows how to deliver messages to a particular port at a particular machine. Think of the network address as the address for a building, and the port number as the number for a mailbox in the building. .PP Some transport protocols provide an abstraction similar to the postal service. .ix "transport protocol They deliver individual messages that may arrive out of order and may even get lost in the way. Each such message is called a .I datagram , .ix datagram which is the abstraction provided by this kind of transport. In the Internet, the datagram service is usually UDP. The IP device driver in Plan 9 provides an interface .ix UDP for using UDP, similar to the ones we saw for other protocols and network devices: .P1 ; lc /net/udp 0 1 clone stats .P2 .LP Other transports use the ability to send individual messages to build a more convenient abstraction for maintaining dialogs, similar to a pipe. This abstraction is called a .B connection . .ix "network connection .ix pipe It is similar to a pipe, but differs from it in that it can go from one port at one machine to another port at a different machine in the network. This type of communication is similar to a phone call. Each end has an address (a phone number), they must establish a connection (dial a number, pickup the phone), then they can speak to each other, and finally, they hangup. The analogy cannot be pushed too far, for example, a connection may be established if both ends call each other, which would not be feasible when making a phone call. But you get the idea. In the Internet, the most popular protocol that provides connections is TCP, it provides them using IP as the underlying transport protocol (hence the name TCP/IP for this suite of protocols). The IP device driver in Plan 9 provides the interface for using TCP. It has the now familiar file interface for using a network in Plan 9: .P1 ; lc /net/tcp 0 11 14 17 2 22 stats 1 12 15 18 20 23 26 10 13 16 19 21 24 clone .P2 .LP Each network is represented in Plan 9 as a directory, that has at least one .CW clone .ix "[clone] file file, and several other directories, called .I line .ix "line directory directories. Opening the .CW clone file reserves a new connection, and creates a directory that represents the interface for the new .I line used to establish a connection. Line directories are named with a number, and kept within the directory for the network. For example, .CW /net/tcp/14 is the interface for our TCP connection number 14. It doesn't need to be a fully established connection, it may be in the process of getting established. But in any case, the directory represents what can be a particular, individual, TCP connection. The program that opens .CW clone may read this file to discover the number assigned to the line directory just created. .PP As shown in figure [[!network interface!]], for each connection Plan 9 provides at least a .CW ctl .ix "network [ctl]~file file and a .CW data .ix "network [data] file file. For example, .P1 ; lc /net/tcp/14 ctl data err listen local remote status .P2 .LS .PS 5i circlerad=.15 define con { [ down L: circle invis $1 move [ right ; C: circle invis "ctl" ; move ; D: circle invis "data" ] arrow from L to last [].C chop arrow from L to last [].D chop ] } down T: circle invis "/net/tcp" above move L: [ right C: [ down C: circle invis "clone" move circle invis ] L0: con("0") ; move ; L1: con("1") ; move ; L2: con("2") move ; "..." ; move ; Ln: con("n"); ] arrow from T to L.C.n chop arrow from T to L.L0.n chop arrow from T to L.L1.n chop arrow from T to L.L2.n chop arrow from T to L.Ln.n chop .PE .LE F The file interface for a network (protocol) in Plan 9. .LP The file ctl can be used to perform control operations to the connection. For example, to hangup (break) this connection, we can just .ix "connection hangup .P1 ; echo hangup >/net/tcp/14 .P2 .LP The .CW data file is used to send and receive bytes through the connection. It can be used very much like one end of a pipe. Writing to the data file delivers bytes through the connection that are to be received at the other end. Reading from the data file retrieves bytes sent from the process writing at the other end. Just like a pipe. Only that, if a transport provides datagrams, each write to a .CW data file will send a different datagram, and it may arrive out of order or get lost. .PP There are more differences. An important one is that many transport protocols, including TCP, do not respect message boundaries. This means that data sent .ix "write boundaries .ix "message delimiters through a connection by several writes may be received at the other end by a single read. If your program has to receive messages from a network connection, it must know how much to read for each message. A single call to read may return either part of a message or perhaps more than one message. .PP In the line directory for our TCP connection, the .CW local .ix "[local] file file has the local address (including the port number) for the connection. This identifies the local end of the .I pipe . The .CW remote .ix "[remote] file file serves the same purpose for the other end of the connection. .PP A network address in Plan 9 is a string that specifies the network (e.g., the .ix "network address protocol) to use, the machine address, and the port number. For example, .CW tcp!193.147.81.86!564 is a network address that says: Using the TCP protocol, the machine address is 193.147.81.86, and the port number is 564. Fortunately, in most cases, we may use names as well. For example, the address .CW tcp!whale!9fs .ix [whale] is equivalent to the previous one, but uses the machine name, .CW whale , and the service name, .CW 9fs , .ix [9fs] instead of the raw addresses understood by the network software. Often, ports are used by programs to provide services to other programs in the network. As a result, a port name is also known as a .ix "service name .B "service name. .PP From the shell, it is very easy to create connections. The .CW srv .ix [srv] program dials a network address and, once it has established a connection to that address, posts a file descriptor for the connection at .CW /srv . .ix [/srv] This descriptor comes from opening the .CW data file in the directory for the connection, but you may even forget this. Therefore, .P1 ; srv tcp!whale!9fs post... .P2 .LP posts at .CW /srv/tcp!whale!9fs a file descriptor that corresponds to an open network connection from this machine to the port named .CW 9fs at the machine known as .CW whale , in the network speaking the protocol .CW tcp . .PP To connect to the web server for LSUB, we may just .P1 ; srv tcp!lsub.org!http post... .P2 .LP Here, .CW tcp is just a shorthand for .CW /net/tcp , which is the real (file) name for such network in Plan 9. Now we can see that .ix HTTP .CW /srv/tcp!lsub.org!http is indeed a connection to the web server at .CW lsub.org by writing an HTTP request to this file and reading the server's reply. .P1 .ps -2 ; echo GET /index.html >>/srv/tcp!lsub.org!http \fIGet the main web page\fP ; cat /srv/tcp!lsub.org!http <html> <head> <title> Laboratorio de Sistemas --- ls </title> <link rev="made" href="mailto:ls@plan9.escet.urjc.es"> </head> <body BGCOLOR=white> <h1> ls --- Laboratorio de Sistemas [ubicuos] del GSyC </h1> .I "...and more output omitted here... ; .ps +2 .P2 .LP If we try to do the same again, it will not work, because the web server hangs up the connection after attending a request: .P1 ; echo GET / >>/srv/tcp!lsub.org!http ; cat /srv/tcp!lsub.org!http cat: error reading /srv/tcp!lsub.org!http: Hangup ; echo GET / >>/srv/tcp!lsub.org!http echo: write error: Hangup .P2 .LP And, as you can see, it takes some time for our machine to notice. The first write seemed to succeed. Our machine was trying to send the string .CW GET ... to the web server, but it couldn't really send it. The connection was closed and declared as hung up. Any further attempt to use it will be futile. What remains is to remove the file from .CW /srv . .P1 ; rm /srv/tcp!lsub.org!http .P2 .LP There is a very popular command named .CW telnet , that can be used to connect to servers in the Internet and talk to them. It uses the, so called, .I "telnet protocol" . .ix [telnet] .ix "telnet protocol But in few words, it dials an address, and thereafter it sends text from your console to the remote process at the other end of the connection, and writes to your console the text received. For example, this command connects to the e-mail server running at .CW lsub.org , and we use our console to ask this server for help: .P1 ; telnet -r tcp!lsub.org!smtp connected to tcp!lsub.org!smtp on /net/tcp/52 220 lsub.org SMTP !!help 250 Read rfc821 and stop wasting my time \fBDelete\fP .P2 .LP We gave the option .CW -r to .CW telnet , .ix "[telnet] flag~[-r] to ask it not to print .I carriage-return .ix "carriage return characters (its protocol uses the same convention for new lines used by DOS). When telnet connected to the address we gave, it printed a diagnostic message to let us know, and entered a loop to send the text we type, and to print the text it receives from the other end. Our mail server wrote a salutation through the connection (the line starting \f(CW220\fP...), and then we typed .CW help , which put our mail server into a bad mood. We interrupted this program by hitting .I Delete in the terminal, and the connection was terminated when .CW telnet died. A somewhat abrupt termination. .PP It is interesting to open several windows, and connect from all of them to the same address. Try it. Do you see how .I each .CW telnet is using its own connection? Or, to put it another way, all the connections have the .I same address for the other end of the connection, yet they are .I different connections. .PP To name a connection, it does not suffice to name the address for one of its ends. You .I must give both addresses (for the two ends) to identify a connection. It is the four identifiers local address, local port, remote address, and remote port, what makes a connection unique. .ix "network address .ix "port number .PP It is very important to understand this clearly. For example, in our .CW telnet example, you cannot know which connection are you talking about just by saying “The connection to \f(CWtcp!lsub.org!smtp\fP”. There can be a dozen of such connections, all different, that happen to reach that particular address. They would differ in the addresses for their other extremes. .BS 2 "Names .LP .ix Above, we have been using names for machines and services (ports). However, these names must be translated into addresses that the network software could understand. For example, the machine name .CW whale must be translated to an IP address like .CW 193.147.81.86 . The network protocol (IP in Internet) knows nothing about names. It knows about machine .ix "network protocol addresses. In the same way, the transport protocol TCP knows nothing about the service with name .CW http . But it does know how to reach the port number .CW 80 , which is the one that corresponds to the HTTP service. .ix "name translation .ix "service name .PP Translating names into addresses (including machine and service names) is done in a different way for each kind of network. For example, the Internet has a name service known as DNS .ix DNS (domain name service) that knows how to translate from a name like .CW whale.lsub.org into an IP address and vice-versa. Besides, for some machines and services there may be names that exist only within a particular organization. Your local system administrator may have assigned names to machines that work only from within your department or laboratory. In any case, all the information about names, addresses, and how to reach the Internet DNS is kept in a (textual) database known as the .I "network database" , .ix "network database .ix [ndb] or just .CW ndb . For example, this is the entry in our .CW /lib/ndb/local file for .CW whale : .P1 dom=whale.lsub.org ip=193.147.81.86 sys=whale .P2 .LP When we used .CW whale in the examples above, that name was translated into .CW 193.147.81.86 and that was the address used. Also, this is the entry in our .CW /lib/ndb/common file for the service known as .CW 9fs when using the TCP protocol: .P1 tcp=9fs port=564 .P2 .LP When we used the service name .ix [9fs] .CW 9fs , this name was translated into the port number .CW 564 , that was the port number used. As a result, the address .CW tcp!whale!9fs was translated into .CW tcp!193.147.81.86!564 and this was used instead. Names are for humans, but (sadly) the actual network software prefers to use addresses. .PP All this is encapsulated into a program that does the translation by itself, relieving from the burden to all other programs. This program is known as the .I "connection server" , .ix "connection server .ix [ndb/cs] or .CW cs . We can query the connection server to know which address will indeed be used when we write a particular network address. The program .CW csquery .ix [csquery] .ix [ndb/csquery] does this. It is collected at .CW /bin/ndb along with other programs that operate with the network data base. .P1 ; ndb/csquery > tcp!whale!9fs /net/tcp/clone 193.147.81.86!564 > .P2 .LP The “\f(CW>\fP” sign is the prompt from .CW csquery , it suggests that we can type an address asking for its translation. As you can see, the connection server replied by giving the path for the .CW clone file that can be used to create a new TCP connection, and the address as understood by TCP that corresponds to the one we typed. No one else has to care about which particular network, address, or port number corresponds to a network address. .PP All the information regarding the connections in use at your machine can be obtained by looking at the files below .CW /net . Nevertheless, the program .CW netstat .ix [netstat] .ix "network status provides a convenient way for obtaining statistics about what is happening with the network. For example, this is what is happening now at my system: .P1 .ps -2 ; netstat tcp 0 nemo Listen audio 0 :: tcp 1 Established 5757 9fs whale.lsub.org tcp 2 nemo Established 5765 ads whale.lsub.org tcp 3 nemo Established 5759 9fs whale.lsub.org tcp 4 nemo Listen what 0 :: tcp 5 nemo Established 5761 ads whale.lsub.org tcp 6 nemo Established 5766 ads whale.lsub.org tcp 7 nemo Established 5763 9fs whale.lsub.org tcp 8 nemo Listen kbd 0 :: .I "...many other lines of output for tcp... udp 0 network Closed 0 0 :: udp 1 network Closed 0 0 :: .ps +2 .P2 .LP Each line of output shows information for a particular line directory. For example, the TCP connection number 1 (i.e., that in .CW /net/tcp/1 ) is established. Therefore, it is probably being used to exchange data. The local end for the connection is at port 5757, and the remote end for the connection is the port for service .CW 9fs at the machine with name .CW whale.lsub.org . This is a connection used by the local machine to access the 9P file server at .CW whale . It is being used to access our main file server from the terminal where I executed .CW netstat . The states for a connection may depend on the particular protocol, and we do not discuss them here. .PP In some cases, there may be problems to reach the name service for the Internet (our DNS server), and it is very useful to call .CW netstat with the .CW -n .ix "[netstat] flag~[-n] flag, which makes the program print just the addresses, without translating them into (more readable) names. For example, .P1 .ps -2 ; netstat -n tcp 0 nemo Listen 11004 0 :: tcp 1 Established 5757 564 193.147.71.86 tcp 2 nemo Established 5765 11010 193.147.71.86 tcp 3 nemo Established 5759 564 193.147.71.86 tcp 4 nemo Listen 11003 0 :: tcp 5 nemo Established 5761 11010 193.147.71.86 .I "...many other lines of output .ps +2 .P2 .LP It is very instructive to compare the time it takes for this program to complete with, and without using .CW -n . .PP To add yet another tool to your network survival kit, the .CW ip/ping .ix [ping] .ix [ip/ping] .ix "network connection program sends particular messages that behave like probes to a machine (to an IP address, which is for a network interface found at a machine, indeed), and prints one line for each probe reporting what happen. It is very useful because it lets you know if a particular machine seems to be alive. If it replies to a probe, the machine is alive, no doubt. If the machine does not reply to any of the probes, it might be either dead, or disconnected from the network. Or perhaps, it is your machine the one disconnected. If only some probes get replied, you are likely to have bad connectivity (your network is losing too many packets). Here is an example. .P1 ; ip/ping lsub.org sending 32 64 byte messages 1000 ms apart 0: rtt 152 µs, avg rtt 152 µs, ttl = 255 1: rtt 151 µs, avg rtt 151 µs, ttl = 255 2: rtt 149 µs, avg rtt 150 µs, ttl = 255 .I ... .P2 .LP In the output, .CW rtt is for .I "round trip time" , .ix "round~trip time .ix "RTT the time for getting in touch and receiving the reply. .BS 2 "Making calls .LP .ix "making calls For using the network from a C program, there is a simple library that provides a more convenient interface that the one provided by the file system from the network device. For example, this is our simplified version for .CW srv . It dials a given network address to establish a connection and posts a file descriptor for the open connection at .CW /srv . .ix [srv.c] .ix "dialing .so progs/srv.c.ms .LP Using .CW argv[1] verbatim as the network address to dial, would make the program work only when given a complete address. Including the network name and the service name. Like, for example, .P1 ; 8.srv tcp!whale!9fs .P2 .LP Instead, the program calls .CW netmkaddr .ix "[netmkaddr] .ix "address construction which is a standard Plan 9 function that may take an address with just the machine name, or perhaps the network name and the machine name. This function completes the address using default values for the network and the service, and returns a full address ready to use. We make .CW tcp the default value for the network (protocol) and .CW 9fs as the default value for the service name. Therefore, the program admits any of the following, with the same effect that the previous invocation: .P1 ; 8.srv tcp!whale ; 8.srv whale .P2 .LP The actual work is done by .CW dial . .ix [dial] This function dials the given address and returns an open file descriptor for the connection's data file. A write to this descriptor sends bytes through the connection, and a read can be used to receive bytes from it. The function is used in the same way for both datagram protocols and connection-oriented protocols. The connection will be open as long as the file descriptor returned remains open. .P1 .ps -1 ; sig dial int dial(char *addr, char *local, char *dir, int *cfdp) .ps +1 .P2 .LP The parameter .CW local permits specifying the local address (for network protocols that allow doing .ix "local address so). In most cases, given .CW nil suffices, and the network will choose a suitable (unused) local port for the connection. When .CW dir is not nil, it is used by the function as a buffer to copy the path for the line directory representing the connection. The buffer must be at least 40 bytes long. We changed the previous program to do print the path for the line directory used for the connection: .P1 fd = dial(addr, nil, dir, nil); if (fd < 0) sysfatal("dial: %s: %r", addr); print("dial: %s\n", dir); .P2 .LP And this is what it said: .P1 ; 8.srv tcp!whale!9fs dial: /net/tcp/24 .P2 .LP The last parameter for dial, .CW cfdp .ix "connection [ctl]~file points to an integer which, when passing a non-nil value, can be used to obtain an open file descriptor for the connection. In this case, the caller is responsible for closing this descriptor when appropriate. This can be used to write to the control file requests to tune properties for the connection, but is usually unnecessary. .PP There is a lot of useful information that we may obtain about a connection by calling .CW getnetconninfo . .ix [getnetconninfo] .ix "network connection information This function returns nothing that could not be obtained by reading files from files in the line directory of the connection, but it is a very nice wrap that makes things more convenient. In general, this is most useful in servers, to obtain information to try to identify the other end of the connection, (i.e., the client). However, because it is much easier to make a call than it is to receive one, we prefer to show this functionality here instead. .PP Parameters for .CW netconninfo are the path for a line directory, and one of the descriptors for either a control or a data file in the directory. When nil is given as a path, the function uses the file descriptor to locate the directory, and read all the information to be returned to the caller. The function allocates memory for a .CW NetConnInfo .ix [NetConnInfo] structure, fills it with relevant data, and returns a pointer to it .P1 typedef struct NetConnInfo NetConnInfo; struct NetConnInfo { char *dir; /* connection directory */ char *root; /* network root */ char *spec; /* binding spec */ char *lsys; /* local system */ char *lserv; /* local service */ char *rsys; /* remote system */ char *rserv; /* remote service */ char *laddr; /* local address */ char *raddr; /* remote address */ }; .P2 .LP This structure must be released by a call to .CW freenetconninfo .ix [freenetconninfo] once it is no longer necessary. As an example, this program dials the address given as a parameter, and prints all the information returned by .CW getnetconninfo . Its output for dialing .CW tcp!whale!9fs follows. .ix [conninfo.c] .so progs/conninfo.c.ms .P1 ; 8.out tcp!whale!9fs dir: /net/tcp/46 root: /net spec: #I0 lsys: 212.128.4.124 lserv: 6672 rsys: 193.147.71.86 rserv: 564 laddr: tcp!212.128.4.124!6672 raddr: tcp!193.147.71.86!564 .P2 .LP The line directory for this connection was .CW /net/tcp/46 , which belongs to the network interface at .CW /net . This connection was using .CW #I0 , which is the first IP interface for the machine. The remaining output should be easy to understand, given the declaration of the structure above, and the example output shown. .BS 2 "Providing services .LP .ix "providing services .ix "server .ix client We know how to connect to processes in the network that may be providing a particular service. However, it remains to be seen how to provide a service. In what follows, we are going to implement an echo server. A client for this program would be another process connecting to this service to obtain an .I "echo service" . This program provides the service (i.e., provides the echo) and is therefore a .I server . The echo service, surprisingly enough, consists on doing echo of what a client writes. When the echo program reads something, writes it back through the same connection, like a proper echo. .PP The first thing needed is to .B announce .ix [announce] .ix "port announce the new service to the system. Think about it. To allow other processes to .I connect to our process, it needs a port for itself. This is like allocating a “mailbox” in the “building” to be able to receive mail. The function .CW announce receives a network address and announces it as an existing place where others may send messages. For example, .P1 announce("tcp!alboran!echo", dir); .P2 would allocate the TCP port for the service named .CW echo and the machine named .CW alboran . This makes sense only when executed in that machine, because the port being created is an abstraction for getting in touch with a local process. To say it in another way, the address given to announce must be a local address. .ix "network port creation It is a better idea to use .P1 announce("tcp!*!echo", dir); .P2 .LP instead. The special machine name “\f(CW*\fP” refers to any local address for our machine. This call reserves the port .CW echo for any interface used by our machine (not just for the one named .CW alboran ). Besides, this call to .CW announce now works when used at any machine, no matter its name. .ix "service name" .PP This function returns an open file descriptor to the .CW ctl file of the line directory used to announce the port. The second parameter is updated with the path for the directory. Note that this line directory is an artifact which, although has the same interface, is .I not a connection. It is used just to maintain the reservation for the port and to prepare for receiving incoming calls. When the port obtained by a call to .CW announce is no longer necessary, we can close the file descriptor for the .CW ctl file that it returns, and the port will be released. .PP This program announces the port 9988, and sleeps forever to let us inspect what happen. .so progs/ann.c.ms .LP We may now do this .P1 ; 8.ann & ; announced in /net/tcp/52 \fI We typed return here, to let you see\fP ; netstat | grep 9988 tcp 52 nemo Listen 9988 0 :: .P2 .LP .ix [netstat] According to .CW netstat , the TCP port number 9988 is listening for incoming calls. Note how the path printed by our program corresponds to the TCP line number 52. .PP Now let's try to run the program again, without killing the previous process. .P1 ; 8.out announce: announce writing /net/tcp: address in use .P2 It fails! Of course, there is another process already using the TCP port number 9988. This new process cannot announce that port number again. It will be able to do so only when nobody else is using it: .P1 ; kill 8.ann|rc ; 8.ann & ; announced in /net/tcp/52 .P2 .LP Our program must now await for an incoming call, and accept it, before it could exchange data with the process at the other end of the connection. To wait for the next call, you may use .CW listen . .ix [listen] This name is perhaps misleading because, as you could see, after .CW announce , the TCP line is already listening for calls. Listen needs to know the line where it must wait for the call, and therefore it receives the directory for a previous announce. .PP Now comes an important point, to leave the line listening while we are attending a call, calls are attended at a .I different line than the one used to listen for them. This is like an automatic transfer of a call to another phone line, to leave the original line undisturbed and ready for a next call. So, after .CW listen .ix "line directory has received a call, it obtains a new line directory for the call and returns it. In particular, it returns an open file descriptor for its .CW ctl file and its path. .PP We have modified our program to wait for a single call. This is the result. .ix [listen.c] .so progs/listen.c.ms .LP When we run it, it waits until a call is received: .P1 ; 8.listen announced in /net/tcp/52 (cfd=10) .P2 .LP At this point, we can open a new window and run .CW telnet to connect to this address .P1 ; telnet tcp!$sysname!9988 connected to tcp!alboran!9988 on /net/tcp/46 .P2 .LP .ix "call receiving .ix "accept connection which makes our program receive the call: .P1 attending call in /net/tcp/54 (lfd=11) .P2 .LP You can see how there are two lines used. The line number 52 is still listening, and the call received is placed at line 54, where we might accept it. By the way, the line number 46 is the other end of the connection. .PP Now we can do something useful. If we accept the call by calling .CW accept , .ix [accept] this function will provide an open file descriptor for the .CW data file for the connection, and we can do our echo business. .so progs/netecho.c.ms .ix [netecho.c] .ix "network echo server" .LP If we do as before, and use .CW telnet to connect to our server and ask for a nice echo, we get the echo back. After quitting .CW telnet , we can connect again to our server and it attends the new call. .P1 ; telnet -r tcp!$sysname!9988 connected to tcp!alboran!9988 on /net/tcp/46 !!Hi there! Hi there! \fBDelete\fP ; telnet -r tcp!$sysname!9988 connected to tcp!alboran!9988 on /net/tcp/54 !!Echo echo... Echo echo... \fBDelete\fP ; .P2 .LP And this is what our server said in its standard output: .P1 ; 8.netecho announced tcp!*!9988 in /net/tcp/52 accepted call at /net/tcp/54 terminated call at /net/tcp/54 accepted call at /net/tcp/55 terminated call at /net/tcp/55 .P2 .LP The program is very simple. To announce our port, wait for call, and accept it, it has to call just .CW announce , .CW listen , and .CW accept . At that point, you have an open file descriptor that may be used as any other one. You just read and write as you please. When the other end of the connection gets closed, a reader receives an EOF indication in the conventional way. This means that connections are used like any other file. So, you already know how to use them. .PP Our program has one problem left to be addressed. When we connected to it using .CW telnet , there was only one client at a time. For this program, when one client is connected and using the echo, nobody else is attended. Other processes might connect, but they will be kept on hold waiting for this process to call .CW listen and .CW accept. .ix [listen] This is what is called a .B "sequential server" , because it attends one client after another. You can double check this by connecting from two different windows. Only the first one will be echoing. The echo for the second to arrive will not be done until you terminate the first client. .PP A sensible thing to do would be to fork a new process for each client that connects. The parent process may be kept listening, .ix "client connection .ix "listen waiting for a new client. When one arrives, a child may be spawned to serve it. This is called a .B "concurrent server" , .ix "threaded server" because it attends multiple clients concurrently. The resulting code is shown below. .PP There are some things to note. An important one is that, as you know, the child process has a copy of all the file descriptors open in the parent, by the time of the fork. Also, the parent has the descriptor open for the new call received after calling .CW listen , even though it is going to be used just by the child process. We close .CW lfd in the parent, and .CW cfd in the child. .PP We might have left .CW cfd open in the child, because it would be closed when the child terminates by calling .CW exits , .ix "connection close after having received an end of file indication for its connection. But in any case, it should be clear that the descriptor is open in the child too. .PP Another important detail is that the child now calls .CW exits after attending its connection, because that was its only purpose in life. Because this process has (initially) all the open file descriptors that the parent had, it may be a disaster if the child somehow terminates attending a client and goes back to call .CW listen . Well, it would be disaster because it is .I not what you expect when you write the program. .so progs/cecho.c.ms .ix [cecho.c] .BS 2 "System services .LP You know that certain machines provide several services. For example, the machine known as .CW lsub.org in the Internet is a Plan 9 system. The machine name is indeed .CW aquamar , but it is registered in DNS as .CW lsub.org . This particular machine provides web, mail, and several other services, including echo! .P1 ; telnet tcp!lsub.org!echo !!Hi Hi \fBDelete\fP ; .P2 .LP How can it be? Before reading this book, you might think that the operating system was arranging for this services to run at that machine. But now you know that the operating system is doing nothing, but for supplying the abstractions used to provide such services. .PP When this particular machine starts, Plan 9 executes an .CW rc .ix boot script as part of the normal boot process. This script runs the program .CW aux/listen , .ix [aux/listen] .ix "machine services .ix "network services which listens for incoming connections and executes programs to attend them. The machine provides services because certain programs are started to attend incoming connections targeted to ports. .PP Following the modular design of the rest of the system, .CW listen does not even decide which ports are to be listened. This program looks at the .CW /rc/bin/service .ix [/rc/bin/service] directory, for files with names like .CW tcp7 , .CW tcp25 , .ix "TCP echo service .ix [tcp7] and so on. Each file corresponds to a service provided by the machine, and has a name that corresponds to the protocol and port number where connections for the service may arrive. .P1 ; lc /rc/bin/service il17007 tcp17007 tcp220 tcp9 il17009 tcp17009 tcp25 tcp993 il17010 tcp17010 tcp53 tcp995 tcp113 tcp17013 tcp565 telcodata tcp143 tcp19 tcp7 .P2 .LP For many services, there are conventions for which ports to use for them in the Internet (you might call it a standard). For example, TCP port 7 corresponds to the echo service. And this is how it is implemented in Plan 9: .P1 ; cat /rc/bin/service/tcp7 #!/bin/rc /bin/cat ; .P2 .LP Indeed, each one of the files in the .CW service directory is an executable program that implements a service. All that .CW listen has to do, is to listen for calls to the ports determined by the file names, and execute the files to attend each incoming call. Listen arranges for the standard input and output of the process attending a call to be redirected to the connection itself. For a service, reading from standard input is reading from the connection, and writing to standard output is writing to the connection. .PP This is a nice example of how simple things can be. Listen is in charge .ix "[listen] command of listening and spawning processes for attending services. The directory keeps the set of files that corresponds to services. We can use familiar programs like .CW lc to list them! Each service is provided by a separate, independent program. And everything fits together. .PP By the way, there is an important lesson to be learned here. It is much more simple to use .CW cat to implement an echo server than it is to write our own program. If we do not search the manual and try to see if what we are trying to do is already done, we get a lot of extra work as a penitence for this sin. .BS 2 "Distributed computing .LP .ix "distributed computing The time has come to reveal another lie we told. There are .I three kind of machines in a Plan 9 network, not just two. You already know about terminals and file servers. There are also .B "CPU servers" . A CPU server is meant to let the user execute commands on it, in particular, commands that make intensive use of the processor. Today, with the powerful machines that we have available, most terminals can cope with anything you might want to execute on them. .PP But CPU servers have found their way in this new world and are still very useful for running the file server program (which used to be a different kernel), executing periodic user tasks automatically, and providing services like Web, mail, and the like. .ix "mail server .PP A CPU server runs the same system software used in a terminal, however, its kernel is compiled with the variable .CW cpuserver set to true, and it behaves slightly differently. The main difference is that the .CW boot .ix [boot] .ix "boot program program executes the script .CW /rc/bin/cpurc instead of .CW /rc/bin/termrc .ix "[termrc] .ix "machine start script to initialize the system for operation. You may remember that one of the things this script does is running .CW aux/listen to run several system services upon incoming calls from clients. .PP Other systems, most notably UNIX, start most existing system services .ix UNIX during the boot process, in a similar way. That is why you can connect to a UNIX machine to execute commands on it (e.g., using .CW telnet or .CW ssh ), but you cannot do the same to your Plan 9 terminal. If you want to connect to your terminal to use a particular service, you must start that service first (i.e., run .CW listen or its variant that listens just for one service, .CW listen1 ). .ix [listen1] .PP By the way, if you ever wondered what is the difference between the different flavors of Windows running on a PC, it is the same. They compiled the system with different parameters for “optimizing” the system for different kinds of usage. Also, they arranged for the system to start different services depending on the kind of edition. .PP The .CW cpu .ix "[cpu] command command makes a connection to a CPU server, using by default that named by .CW $cpu , .ix "[cpu] variable as set by your system administrator. The connection is used to run a program in the CPU server, which is .CW rc by default. The net effect is that you can connect to a shell at any CPU server, and run commands on it. This is an example: .P1 ; echo $sysname alboran ; cpu cpu% !!echo $sysname aquamar \fBcontrol-d\fP ; echo $sysname alboran .P2 .LP Your .CW profile , executed each time you enter the system, changes the prompt for the shell to advise you that it is not running at your terminal. When an initial shell is started for you at a machine (a CPU server, a terminal, etc.), it executes your .CW $home/lib/profile file. Now, the process that started the shell for you defined a environment variable to indicate which kind of session you are using. For terminals, the variable .CW service .ix [service] variable .ix profile has .CW terminal as its value. However, on CPU servers this variable may have .CW cpu or .CW rx as its value, depending on how you connected to the CPU server. Your profile may do different things (like adjusting the shell prompt), depending on .CW $terminal . .PP A more rudimentary alternative is provided, for those cases when you want to execute just one command at another machine. It is called .CW rx , .ix [rx] .ix "remote command execution and accepts a machine name and a command to run on it. .P1 ; rx aquamar 'echo $sysname' aquamar ; .P2 .LP Note how we had to quote the whole command, which is to be executed verbatim by the remote machine, .SH Problems .IP 1 Use .CW /net to see which networks are available at your terminal. Determine the local address for your terminal for each one of the networks. .IP 2 Repeat the second problem of chapter 1 for the terminals in your network. Use .CW /lib/ndb/local to locate other terminals. .IP 3 Start the echo server implemented in this chapter, and try to hangup its connection using the shell. .IP 4 Which processes are listening to the network in your terminal? What do they do? (use the manual) .IP 5 Which one is the IP address for .CW google.com ? Is the machine alive? Try to determine that in several different ways. .IP 6 Implement a time of day service. It must return the local time to any client. Use .CW telnet to test it. .IP 7 Implement a client program for the server from the previous problem. .IP 8 Print all the information you can determine for all clients connecting to your time of day server. .IP 9 Change your server so it could be started using .CW aux/listen1 . Test it. .IP 10 Change your profile to adjust the shell prompt according to the machine name. It must work both for terminals and connections to CPU servers. .ds CH .bp