The Open Source Swiss Army Knife

/code/c/unix_c/
/code/c/unix_c/ + sub-categories
http://www.sirfsup.com/
web directory content
    
      

Not logged in
Chat Register Login
return to:  http:/www.sirfsup.com      /code   /c   /unix_c 
Permalink: connection_communicationtwo.txt
Title: add
article options : please login   |  raw source view  

chapter 18 USP


this operating systems course is only concerned with the theories not with the code in this section

0. client-server introduction
1. UICI
2. behind the source code of the UICI
3. host names and ip addresses

we wrote a program which uses pipes as interprocess communication
here, this program we wrote is held up as an example of a client/server application which needs to be used on the same machine
network communication which uses client/server model includes things like mail,ftp,telnet,rlogon,http and nfs

in connectionless the server responds to client's endpoint
endpoint: the software in the client which does the communication
connection-oriented communication: client initiates tcp/ip handshake and open up a connection using normal file descriptors.

doesn't server initiate new connection on higher port?
no, it doensn't.
client uses the higher port number

client and server share file descriptors. really?

separate communicaton channels betweein different client and
UICI: a separate interface to deal with client/server solutions
written by dr.steven robbins utsa computer science as a final exam project to simplify client/server functions and programs
has simplified system calls

the UICI API

/* all return -1 on error */
int u_open(u_port port)
-- associates a port number (a type of service)
-- u_accept
int u_accept(int fd, char * hosts, int hostnsize);
-- returns a fd to be used for reading and writing
-- after u_accept can use another accept on the same fd doing multiple connectionsa nd multiple clients
int u_connect(u_port_t port, char * hostn); /* returns a fd for read/write */
-- port and hostname
-- if server exists waiting for connections on that port, returns a fd which can be used for communicating with that server
server


-- accepts requests
-- handles the requests
so
1. obtain a fd from a port for listening 2. wait for a connection request on that fd 3. when a request comes in, return a communication fd (different from above one) 4. handle the connection 5. can be done serially or by a child

client

  1. request a port on another machine
  2. communication by doing reads and writes on this file descriptor u_connect is only thing the client needs The server needs two fds. One to listen on the port number, u_accept returns a file descriptor.

server: u_open
client:u_connect
server: u_accept
--
client: write
server: read
server: write
client: read
--
server: close
client: close
(server can then accept more connections)

recommends r_read and r_write as these handle signals better ("more robust"), and r_write will write the number of bytes and report an error if unsuccessful

<url:server.c:server.c>
int main() {

        u_port portnumber;
        listenfd = u-open(portnumber);
        gets process id of server
        for (; ; ) {
                communfd = u_accept /* accept connection
                bytescopied = copyfile(communfd, STDOUT_FILENO);        
                r_close(communfd);
                /* returns to loop and returns again to wait again */
        }

}

this is a serial server because it copies bytes in serial order
handles one connection at a time so it is called a serial server

this next example allows child to wait on an unfinished request so that backend is not blocked
<url:serverp.c:serverp.c>
int main() {

        u_port portnumber;
        listenfd = u-open(portnumber);
        gets process id of server
        /* loop to process requests */
        for (; ; ) {
                communfd = u_accept /* accept connection
                fork();
                child () {   /* does all data communication with client */
                        r_close(listenfd) -- child will not need to go back
                        bytescopied = copyfile(communfd, STDOUT_FILENO);                                return 0;
                }
                /* parent: 
                r_close(communfd);
                r_waitpid(-1, NULL, WNOHANG) > 0) ; /* clean up zombies */
                /* second arg is for the status */
                /* WNOHANG: if active children who have not completed, return immediately  */
                /* child processes will be cleaned up in sequence */
        }

}

this server uses threads to handle requests from parent process
if in this one thread, problem: how is SIGPIPE handled? signals go to the process which has all the threads in it, so the SIGPIPE shuts down the main process which shuts down all the child threads.

CLIENT CODE

int main() {

        portnumber = (u_port_t) atoi (argv[2]);
        communfd = u_connect(portnumber, argv[1]));
        bytescopied = copyfile(STDIN_FILENO, communfd);
        return 0;

}

<url:reflectclient.c:reflectclient.c>
/* tests the connection */
posrtnumber = (u_port_t) atoi(argv[w]);

        communfd = u_connect(portnumber, argv[1]));
        r_write(communfd, bufsend, BUFSIZE) != BUFSIZE);
        totalrecvd - 0;
        while (totalrecvd < BUFSIZE) {
                bytesrecvd = r_read(communfd, bufrecv+totalrecv ...

}

client.c: a bidirectional client which instead of copytofile uses copy2files(communfd, STDOUT_FILENO, STDIN_FILENO, communfd)

behind the source code of the UICI

sockets


based on socketbind, listen, accept, connect
u_open: socket, bind, listen
u_accept: accept
u_connect: socket, connect

socket: creates a communication endpoint
#include <sys/socket.h>
int socket(intdomain, int type, int protocol);
int bind(int s, const struct sockadr * address, size_t address_len); /* associates with a port */
int listen (int s, int backlog);
int accept(int s, struct sockaddr * restrict adddress, int * restrict address_len);

socket
domain is AF_UNIX (unix sockets) or AF_INET type is SOCK_STREAM for a connection-oriented TCP or SOCK_DGRAM for connectionless UDP protocol is usually 0 - use the only protocol associated with SOCK_STREAM (ip) or udp for SOCK_DGRAM

bind:
associates a port number with a fd
s is the filedes returned by socket
know the address family (struct sockaddr contains info about the family, port, and machine)
address_len is the size of the structure used for the address

the format of the address is determined by the address family (domain).
for AF_INET is a struct sockaddr_in (a type of sockaddr used for the internet)
sa_family_t sin_family;
in_port_t sin_port;
struct in_addr sin_addr;

the sin_family should be AF_INET
sin_port == the port number in network byte order

you can convert a port number to network byte order using uint18_t htons (uint16_t port);

80 * 256 = converting from little to big or vice-versa
for a server, the sin_addr can be set to INADDR_ANY to accept connections on any interface card; doesn't have to do with network interfaces
for aclient, it must be filled in with the IP address of the remote machine in network byte order.

setsockopt

not allowed to have 2 different processes listening on the same port on the same machine. even if process dies, OS doesn't find out it's cleared for a while (10 seconds, 1 minute).
SO_REUSEADDR: says reuse this address immediately when the server crashes or I terminate it.

listen

sets a number of unrequested, outstanding requests
if write a forking request server, request comes in while server process is in the ready queue or is forking.
Then the request goes into a buffer. how many get queued up before being thrown away?

<url:u_open.c:u_open.c>

u_open(u_port_t port) {

        struct sockaddr_in server
        if ( u_ignore_sigpipe == -1) /* if sigpipe is given will ignore it */
                sock = socket(AF_INET, SOCK_STREAM,0)
        /* SIGPIPE: writing to a pipe which has no readers */
        /* SIGPIPE: default action is to terminate the process */
        /* ... before we have a chance to handle it */
        setsockeopt(sock, SOL_SOCKET, SO_REUSEADDR, (char * )&true)
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = htonl(INADDR_ANY)  /* INADDR_ANY == 0 */
        server.sin_port = htons((short)port)
        bind(sock, (struct sockaddr *)&server, sizeof(server))
        return sock;

}

more notes on SIGPIPE

network communication sockets behave like pipes soSIGPIPE is generated when there are no readers for output on a socket
this can happen if the client crashes/responds and blocks recv --> no readers on other end
error is handled as errror returned from writing to broken pipe

child () { /* does all data communication with client */ */ in here will SIGPIPE give a response */

accept

u_accept remember fills a buffer with name of machine making a connection
in internet, the name of a machine is really the internet address
accept fills in a structure with info about the client
u_accept would then need routine to convert the internet address to a string
u_accept does this

u_connect

makes socket then connect system calls
u_connect () {

        name2addr(hostn, &(server.sin_addr.s_addr))
        server.sin_port = htnos(short(port))
        server.sin_family = AF_INET
        u_ignore_sigpipe
        connect(sock, (struct sockaddr *)&server, sizeof(server))
        /synchronous/
        while((retval = select(sock+1, NULL,&socket,NULL,NULL)

problem with receiving a signal when making a connection request
if the connection setiup is (handshaking) is done by network services, the connect has already started, if connect handles the signal:
1. nothing happened at all (this really doens' thapen)
2. connection setup and finished by time signal is received
3. started connection request, but the connection request hasn't finished yet. Have to wait for it to finish with using select, which will tell me when the file descriptor is available

HOST NAMES AND IP ADDRESSES

traditional way of doing it has problems.
int name2addr(const char * name, in_addr_t * addrp);
void addr2name(struct in_addr addr, char * name, int namelen);

name2addr

error returned when name doesn't correspond to an address

addr2name

returns error if buffer is toosmall and had to a
these routines name2addr and name2addr (teacher's routines)

#include <arpa/inet.h>
in_=addr_t inet_addr(const char * cp);
char * inet_ntoa(const struct in_addr in);

threads: static no
child processes: static in funcction ok

there is however a way to use different static storage differently in different threads


Leave a Reply
Your Name:     anonymous
Your Email:
Website:  
Comments:

The author will be notified of your reply.
return to top