200 likes | 466 Views
Berkeley Sockets An example. Carsten Griwodz (adapted from lecture by Olav Lysne). Read and Write. The call read(sock, buffer, n); Reads n characters From socket sock Stores them in the character array buffer The call write(sock, buffer, n); Writes n characters
E N D
Berkeley Sockets An example Carsten Griwodz (adapted from lecture by Olav Lysne) Socket example
Read and Write • The call read(sock, buffer, n); • Reads n characters • From socket sock • Stores them in the character array buffer • The call write(sock, buffer, n); • Writes n characters • From character array buffer • To the socket sock Socket example
Read and Write in TCP TCP sender • Typical • retval == -1 • Some kind of error, look at errno • Retval == 0 • The connection has been closed • Retval n < 2000 • You tried to send too fast • Only n bytes have been sent • Try sending the reset • Retval == 2000 • All bytes have been sent • Very untypical • Retval < -1 • Retval > 2000 • Both cases: • Have you used char retval instead of int retval? char buffer[2000]; int retval; buffer[0] = ‘a’; buffer[1] = ‘b’; buffer[2] = ‘c’; … /* write abcdefghij */ retval = write( sock, buffer, 2000 ); Socket example
Read and Write in TCP TCP receiver • Typical • retval == -1 • Some kind of error, look at errno • Retval == 0 • The connection has been closed • Retval n < 2000 • Only n bytes have been received • No new data has arrived recently • Try sending the rest • Retval == 2000 • All bytes have been received • Very untypical • Retval < -1 • Retval > 2000 • Both cases: • Have you used char retval instead of int retval? char buffer[2000]; int retval; … /* write abcdefghij */ retval = read( sock, buffer, 2000 ); Socket example
Application protocol chatting Chat client 1 Chat server Chat client 2 connection chat chat chat “#” disconnection Socket example
Some detail • Port numbers are sent as 4 ASCII characters • Machine names are sent as • First 2 ASCII characters as a two digits decimal number indicating the number of bytes in the name • Then the name, in the number of bytes indicated before • A ”chatted” line is sent as 80 ASCII characters • When a user chats a line that starts with the character ’#’ the chat session ends Socket example
Helper function for communication • It is worthwhile to put some socket functions into functions calls such that it becomes easier to use them • A call that provides a socket that listens to a given port • Hides bind, listen, struct sockaddr_in • A call that provides a socket that is connection to a given hostname and port number • Hides connect, name resolution, etc. • Several read and write operations Socket example
Creation of a listen socket int TCPListenSocket(int port_number) { struct sockaddr_in serveraddr, clientaddr; int clientaddrlen; int request_sock; int i; /* Create the request socket. */ request_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (request_sock < 0) { printf("Creation of a socket failed.\n"); exit(1); } /* Fill in the address structure */ bzero((void *) &serveraddr, sizeof(serveraddr)); serveraddr.sin_family = AF_INET; serveraddr.sin_addr.s_addr = INADDR_ANY; serveraddr.sin_port = htons(port_number); /* Allow that the socket to reuse a port that the server * has also used when it was started before. Otherwise * TCP waits for a few minutes before allowing reuse. */ i = 1; setsockopt( request_sock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(i)); /* Bind the address to the socket. */ if (bind(request_sock, (struct sockaddr *)&serveraddr, sizeof serveraddr) < 0) { printf("Binding address to socket failed\n"); exit(1); } /* Start listening to the socket */ if (listen(request_sock, SOMAXCONN) < 0) { printf("Can't listen to the socket\n"); exit(1); } return request_sock; } Socket example
Connection from the client side int TCPClientSocket(char machine[], int port_number) { struct hostent *hostp; struct sockaddr_in serveraddr; int sock; /* Create a socket */ if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { printf("Creation of a socket failed.\n"); exit(1); } /* Clean the serveraddr structure */ bzero((void *) &serveraddr, sizeof(serveraddr)); /* Initialize the serveraddr structure for the machine and port */ serveraddr.sin_family = AF_INET; /* Look in DNS for the IP address of the name */ if ((hostp = gethostbyname(machine)) == 0) { fprintf(stderr,"Ukjent machine %s\n",machine); exit(1); } /* Put the address into the serveraddr structure */ memcpy(&serveraddr.sin_addr, hostp->h_addr, hostp->h_length); /* Add the port number */ serveraddr.sin_port = htons(port_number); /* Connect to the other machine */ if (connect(sock, (struct sockaddr *)&serveraddr, sizeof serveraddr) < 0) { close(sock); printf("Can't connect to %s\n",machine); exit(1); } return sock; } Socket example
Safe reading and writing /* Reads exactly l bytes from the socket */ int saferead(int so, char buf[], int l) { int i; for (i=0; i<l; i++) { buf[i]=safereadbyte(so); } return l; } /* Write to a socket in the same way as write, but returns an error message if the socket has been closed in the meantime */ int safewrite(int so, char buf[], int l) { int i; if (i=write(so, buf, l)==0) { printf("Can't write to socket, connection is closed" ); exit(1); } return i; } /* Reads exactly one byte from a socket connection */ char safereadbyte(int so) { int bytes; char buf[1]; bytes = read(so, buf, 1); /* Check whether the read worked */ if (bytes<0) { perror("Error in saferead"); if (close(so)) perror("close"); exit(1); } /* Check whether the connection is still open */ if (bytes==0) { printf("server: end of file on %d\n",so); if (close(so)) perror("close"); exit(1); } return buf[0]; } Socket example
Server: connecting two clients int connect_two_clients(int listen_socket) { int client_socket1, client_socket2; struct sockaddr_in clientaddr1, clientaddr2; int clientaddrlen1, clientaddrlen2; char client_name1[80], client_name2[80]; char client_port1[5], client_port2[5]; char name_length1[3], name_length2[3]; memset( client_name1, 0, 80 ); memset( client_name2, 0, 80 ); memset( client_port1, 0, 5 ); memset( client_port2, 0, 5 ); memset( name_length1, 0, 3 ); memset( name_length2, 0, 3 ); /* Accept a connection from a first client */ clientaddrlen1 = sizeof(clientaddr1); client_socket1 = accept(listen_socket, (struct sockaddr *)&clientaddr1, &clientaddrlen1); if (client_socket1 < 0) perror("Can't accept connection from a first chat client\n" ); /* Read machine name and port number that client sends * as its contact information */ saferead(client_socket1, name_length1, 2); saferead(client_socket1, client_name1, atoi(name_length1)); saferead(client_socket1, client_port1, 4); /* Accept a connection from second client */ clientaddrlen2 = sizeof(clientaddr2); client_socket2 = accept(listen_socket, (struct sockaddr *)&clientaddr2, &clientaddrlen2); if (client_socket2 < 0) perror("Can't accept connection “ “from a second chat client\n" ); /* Read machine name and port number that client sends * as its contact information */ saferead(client_socket2, name_length2, 2); saferead(client_socket2, client_name2, atoi(name_length2)); saferead(client_socket2, client_port2, 4); Socket example
Server: connecting two clients cntd. /* Tell the first client that it is the server in the chat * connection, and to wait for connection by a chat partner */ safewrite(client_socket1, "T", 1); /* Tell the second client that it is the client in the chat * connection, and to connect to the given name and port */ safewrite(client_socket2, "K", 1); safewrite(client_socket2, name_length1, 2); safewrite(client_socket2, client_name1, atoi(name_length1)); safewrite(client_socket2, client_port1, 4); /* Close the sockets for both clients */ close(client_socket1); close(client_socket2); return 0; } Socket example
Server’s main function int main(int argc, char *argv[]) { int listen_socket; if( argc != 2 ) { fprintf(stderr,"Usage: %s <port number>\n", argv[0]); exit(1); } /* Start listening to the port number from the command * line */ listen_socket = TCPListenSocket(atoi(argv[1])); /* Connect pairs of clients forever */ while (1) connect_two_clients(listen_socket); /* We don't ever come here */ close(listen_socket); } Socket example
Client: contacting the server int getchatpartner( char servername[], char serverport[], char myname[], char myport[] ) { int sock, listen_socket, partneraddresslength; struct sockaddr partneraddress; char name_length[3]; char client_or_server[2]; char chatpartnername[80]; char chatpartnerport[5]; char buf[80]; memset( name_length, 0, 3 ); memset( client_or_server, 0, 2 ); memset( chatpartnername, 0, 80 ); memset( chatpartnerport, 0, 5 ); /* Create a socket that is read to accept connections from * a chat partner */ listen_socket = TCPListenSocket(atoi(myport)); /* Connect to a chat server */ sock = TCPClientSocket(servername,atoi(serverport)); /* Tell the server how you can be contacted by a chat * partner */ sprintf(name_length, "%d", strlen(myname)); safewrite(sock, name_length, 2); safewrite(sock, myname, strlen(myname)); safewrite(sock, myport, 4); /* Receive from the server whether you will act as a client * or as a server in the chat */ saferead(sock, client_or_server, 1); Socket example
Client: contacting the server cntd. if (client_or_server[0] == 'K') { /* You will be the client in the chat */ /* Close the socket that you would have * needed as a chat server */ close(listen_socket); /* Read the length of the chat partner's hostname */ saferead(sock, name_length, 2); /* Read the chat partner's hostname */ saferead(sock, chatpartnername, atoi(name_length)); /* Read the chat partner's port number */ saferead(sock, chatpartnerport, 4); /* Close the connection to the server */ close(sock); /* Connect to the chat partner */ sock = TCPClientSocket(chatpartnername, atoi(chatpartnerport)); /* Write to the screen that the user can talk first */ printf("Connection create, you talk first!!\n"); } else { /* Act as the server in the chat, no more information * is coming from the server */ close(sock); /* Accept a connection from a chat partner */ partneraddresslength = sizeof(partneraddress); sock = accept(listen_socket, (struct sockaddr *)&partneraddress, &partneraddresslength); if (sock < 0) perror("Error accepting connection from chat partner" ); /* Write to the screen that the chat partner talks first */ printf("Connection create, partner talks first!!\n"); saferead(sock, buf,80); printf(buf); } return sock; } Socket example
Client: chatting and main /* A function that alternately reads a text string from * standard input and sends it over the TCP connection, and * receive a text string and prints it on the screen. */ int chat(int socket) { char text[80]; while (1) { fgets(text,80,stdin); write(socket,text, 80); if (text[0]=='#') return 0; saferead(socket, text,80); if (text[0]=='#') return 0; printf(text); } } int main(int argc, char *argv[]) { int sock; if( argc != 5 ) { fprintf( stderr, "Usage: %s <servername> <serverport> “ “<myname> <myport>\n", argv[0]); exit(1); } /* ask for a chart partner */ sock = getchatpartner(argv[1], argv[2], argv[3], argv[4]); /* chat along!*/ chat(sock); /* Close the chat socket */ close(sock); } Socket example
Weaksnesses of the implementation • Server accounts hardly for wrong client behaviour • Sending characters when a post number is required • Client connects but does not send data, chat server ends • Server forwards simple what it receives, without checking that the machine name is the calling client’s • Chat client can only send in turns • That is different in a real chat session, or in real life • Everything that is typed should be sent, without an understanding of turns • Much more…! Socket example
”getchatpartner” begins as before… int getchatpartner( char servername[], char serverport[], char myname[], char myport[] ) { int sock, listen_socket, partneraddresslength; struct sockaddr partneraddress; char name_length[3]; char client_or_server[2]; char chatpartnername[80]; char chatpartnerport[5]; char buf[80]; memset( name_length, 0, 3 ); memset( client_or_server, 0, 2 ); memset( chatpartnername, 0, 80 ); memset( chatpartnerport, 0, 5 ); /* Create a socket that is read to accept connections from * a chat partner */ listen_socket = TCPListenSocket(atoi(myport)); /* Connect to a chat server */ sock = TCPClientSocket(servername,atoi(serverport)); /* Tell the server how you can be contacted by a chat * partner */ sprintf(name_length, "%d", strlen(myname)); safewrite(sock, name_length, 2); safewrite(sock, myname, strlen(myname)); safewrite(sock, myport, 4); /* Receive from the server whether you will act as a client * or as a server in the chat */ saferead(sock, client_or_server, 1); Socket example
...buts ends differently if (client_or_server[0] == 'K') { /* You will be the client in the chat */ /* Close the socket that you would have needed * as a chat server */ close(listen_socket); /* Read the length of the chat partner's hostname */ saferead(sock, name_length, 2); /* Read the chat partner's hostname */ saferead(sock, chatpartnername, atoi(name_length)); /* Read the chat partner's port number */ saferead(sock, chatpartnerport, 4); /* Close the connection to the server */ close(sock); /* Connect to the chat partner */ sock = TCPClientSocket(chatpartnername, atoi(chatpartnerport)); } else { /* Act as the server in the chat, no more information * is coming from the server */ close(sock); /* Accept a connection from a chat partner */ partneraddresslength = sizeof(partneraddress); sock = accept(listen_socket, (struct sockaddr *)&partneraddress, &partneraddresslength); if (sock < 0) perror("Error accepting connection from chat partner" ); } printf("Connection create, chat along!!\n"); return sock; } Socket example
”chat” function used separate processes for reading and writing, so that the don’t block each other: int chat(int socket) { int status; char text[10]; if (safefork()==0) { while (text[0] != '#') { fgets(text,80,stdin); write(socket,text, 80); } } else { while (text[0] != '#') { saferead(socket, text,80); printf(text); } } } int main(int argc, char *argv[]) { int sock; /* get a chat partner */ sock = getchatpartner(argv[1], argv[2], argv[3], argv[4]); /* start chatting */ chat(sock); /* close the chat socket */ close(sock); } Socket example