Socket Programming Example Amir R. Khakpour CSE422 Dept. of Computer Science and Engineering Michigan State University January 25, 2009
What is the problem? Message type=1, Message content= Message type=2, Message content=random_port Message type=3, Message content=client_msg Message type=4, Message content= neg_port random_port client server Two phase communication - Port number negotiation (using UDP sockets) - Message transaction (using TCP socket) Each message has three fields: - message type: integer - message content length: integer - message content: character [128]
Solution Command line executables - Client side:./client -s 35.9.26.200 -p 12014 -m mymessage! - Server side:./server Source Code files - client.cc - server.cc - packet.h struct message packet { int type; int msg len; int port num; char message[128]; };
[client.cc] Taking care of input arguments const char* server host = "127.0.0.1"; unsigned short neg port = 0; const char * MESSAGE=""; if (( argc!= 7 ) && ( argc!= 5 )){ cout << " Usage: " << argv[0] << " -s <hostname> -p <udp port> -m <message>" << endl; exit(0); } else for (int i=1; i<argc; ++i) if (strcmp(argv[i], "-s") == 0) server host = argv[++i]; else if (strcmp(argv[i], "-p") == 0) neg port = (short) atoi(argv[++i]); else if (strcmp(argv[i], "-m") == 0) MESSAGE=argv[++i];
[client.cc] Creating the UDP socket client-side Create a socket and store its descriptor int udp fd; // socket descriptor // create the socket if ( (udp fd = socket(af INET,SOCK DGRAM,0)) < 0 ) { cerr << "udp error!" << endl; exit(0); } Store the server s address in an object the network client can understand sockaddr in server; socklen t slen=sizeof(server); // length of remote address // designate the addressing family server.sin family = AF INET; server.sin port = neg port; hostent *hp = gethostbyname(server host); memcpy(&server.sin addr,hp->h addr, hp->h length); Important note: based on the operating system that you run your program on you may need to use htons() function to parse a number to server.sin port. htonl() and htons() convert long integer and short integer (respectively) from host byte order (on x86 this is Least Significant Byte First) to standard network byte order (Most Significant Byte First).
[server.cc] Creating the UDP socket on the server side Similar to client.cc we have: int udp fd; // udp file descriptor // create the UDP socket if ( (udp fd = socket(af INET,SOCK DGRAM,0)) < 0 ) { cerr << "udp error!" << endl; exit(0); } sockaddr in server udp; // socket address for us socklen t len = sizeof(server udp); // set up the UDP socket server udp.sin family = AF INET; // internet family server udp.sin port = 0; // system set server udp.sin addr.s addr = INADDR ANY; // wild card machine address
[server.cc] Creating the UDP socket on the server side (cont.) We will need to bind the socket we just created to the port // bind the name (address) to a port if ((bind(udp fd,(struct sockaddr *)&server udp, sizeof(server udp))) < 0 ) { cerr << "udp binding Error!" << endl; exit(0); } Then we need to output the port the OS selected for us to use getsockname(udp fd, (struct sockaddr *)&server udp, &len); cout << "Negotiation Port: " << server udp.sin port << endl;
[server.cc] Creating the TCP socket for server Similar to UDP, for TCP socketr generation we need to change SOCK DGRAM to SOCK STREAM to make the socket connection oriented. We will also have to bind the socket to the server port, like we did for the server s UDP socket. //create the TCP Socket int tcp fd; // tcp file descriptor if ( (tcp fd = socket(af INET,SOCK STREAM,0))<0) { cerr << "tcp error!" << endl; exit(0); } sockaddr in server tcp; //setupf the TCP Socket server tcp.sin family = AF INET; // internet family server tcp.sin port = 0; // get the socket port server tcp.sin addr.s addr = INADDR ANY; // wild card machine address if ((bind(tcp fd,(struct sockaddr *)&server tcp, sizeof(server tcp))) < 0) { cerr << "tcp bind error!" << endl; exit(0); } len = sizeof(server tcp); getsockname(tcp fd, (struct sockaddr *)&server tcp, &len);
[client.cc] Sending the first packet Create the packet using packet.h message packet rqst packet; rqst packet.type=1; rqst packet.msg len=0; bzero(rqst packet.message,128); To send the packet to the server we use command sendto(). It takes the file descriptor, the address of the packet, the size of the packet, and flag (typically always zero), the pointer to the server socket address, and the size of the address. sendto(udp fd,&rqst packet,sizeof(rqst packet),0,(struct sockaddr *)&server, slen); cout <<">> [UDP] Sent: Message Type: "<< rqst packet.type<<" Message Length: " <<rqst packet.msg len<<" Message Content: "<< rqst packet.message <<endl;
[server.cc] Receiving the first packet To receive from the client we use the function recvfrom(), which takes a file descriptor, the pointer to where to store the message, the max size received, a flag (typically zero), a pointer to a socket address for the sender, and the size of address as arguments. We will want to store the address for the client to respond back with the TCP port. sockaddr in client; // socket address for client socklen t clen = sizeof(client); message packet rqst packet; //receive connect request int mesglen = recvfrom(udp fd,&rqst packet,sizeof(rqst packet),0, (struct sockaddr *)&client, &clen); cout <<"<< [UDP] Received: Message Type: "<< rqst packet.type <<" Message Length: " <<rqst packet.msg len <<" Message Content: "<< rqst packet.message <<endl;
[server.cc] Responding to the client Once the server side receives the message it creates a message with type=2 and fill the content by TCP port number. message packet response packet; response packet.type=2; ostringstream random port; random port << server tcp.sin port; bzero(response packet.message,128); response packet.msg len=random port.str().length(); strncpy(response packet.message,random port.str().c str(), response packet.msg len); Send back the response to the client. In the same command we used for sending to the server: sendto(udp fd, &response packet, sizeof(response packet), 0,(struct sockaddr *)&client, clen); cout <<">> [UDP] Sent: Message Type: "<< response packet.type <<" Message Length: " <<response packet.msg len <<" Message Content: "<< response packet.message <<endl; We are done with the UDP file descriptor so close it. close(udp fd);
[client.cc] Receiving the response Like we did with the server we need to receive the packet being sent to us. message packet response packet; int mesglen = recvfrom(udp fd, &response packet,sizeof(response packet),0, (struct sockaddr *)&server, &slen); And like the server we are done with the file descriptor. close(udp fd); And we print out the received packet. char out1[128]=""; strncpy(out1,response packet.message,response packet.msg len); cout <<"<< [UDP] Received: Message Type: "<< response packet.type <<" Message Length: "<<response packet.msg len <<" Message Content: "<< out1 <<endl; cout << "So the Random port (random port) is: "<< out1 << endl;
[client.cc] Creating the TCP socket for client Just like the server we will need to create a socket for the TCP connection. int tcp fd; if ( (tcp fd = socket(af INET, SOCK STREAM, 0)) < 0) { cerr << "tcp error!" << endl; exit(0); }
[server.cc] Wait for a connection request and accept Now that we have a socket and the client matches the username, we need to listen for their connection and accept the connection. Also we should close the first TCP file descriptor because we no longer are listening for connections. listen(tcp fd, 1); int acc tcp fd = accept(tcp fd, (struct sockaddr *)0, (socklen t *)0); close(tcp fd); Make sure the file descriptor exists, otherwise quit. if(acc tcp fd == -1) { cout << "accept failure" << endl; exit(0); }
[client.cc] Connecting to the server We update the port number by the random port and then connect to the server with the address and port we received the response packet from. // update the server socket port number server.sin port = atoi(out1); if(connect(tcp fd, (struct sockaddr*)&server, slen) < 0) { cout << "connection error" << endl; close(tcp fd); exit(0); }
[client.cc] Sending a message to the server Now that we are connected, let s send messages to the server. char out2[128]=""; message packet client msg; client msg.type=3; client msg.msg len=strlen(message); bzero(client msg.message,128); strncpy(client msg.message,message,client msg.msg write(tcp fd, &client msg, sizeof(client msg)); cout <<">> [TCP] Sent: Message Type: "<< client msg.type <<" Message Length: " <<client msg.msg len <<" Message Content: "<< MESSAGE <<endl;
[server.cc] Receiving a message from client message packet client msg; // receive the message mesglen = read(acc tcp fd, &client msg, sizeof(client msg)); cout <<"<< [TCP] Received: Message Type: "<< client msg.type <<" Message Length: " <<client msg.msg len <<" Message Content: "<< client msg.message <<endl;
[server.cc] Sending a message to the client The server writes on the socket and the client will read that message packet rcv msg; rcv msg.type=4; rcv msg.msg len=0; write(acc tcp fd, &rcv msg, sizeof(rcv msg)); char out3[128]=""; strncpy(out3,rcv msg.message,rcv msg.msg len); cout <<">> [TCP] Sent: Message Type: "<< rcv msg.type <<" Message Length: " <<rcv msg.msg len <<" Message Content: "<< out3 <<endl;
[client.cc] Receiving a message from server char out3[128]=""; message packet rcv msg; read(tcp fd, &rcv msg, sizeof(rcv msg)); strncpy(out3,rcv msg.message,rcv msg.msg len); cout <<"<< [TCP] Received: Message Type: "<< rcv msg.type <<" Message Length: " <<rcv msg.msg len <<" Message Content: "<< out3 <<endl;
Cleaning up We need to close all the open file descriptors before we exit. server.cc: // close the socket close(acc tcp fd); client.cc: close(tcp fd);