|
|||||
| | |||||
chapter 18 USP
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
/* 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
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)
sockets
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);
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.
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.
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;
}
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 */
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
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
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);
error returned when name doesn't correspond to an address
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 |