Socket Programming (Programming A Simple Server) in C++: Int Socket (Int Domain, Int Type, Int Protocol)
Socket Programming (Programming A Simple Server) in C++: Int Socket (Int Domain, Int Type, Int Protocol)
Object 1
Before going to in depth details about how to program a socket, it is better to have some idea about what is a socket and how it operates when communication happens. Socketit is an abstract concept of communication end point. It can be identified by associated IP address and Port. Basically there are two types of internet sockets which we have to worry. One is STREAM SOCKET and other is DATAGRAM SOCKET. Stream socket operates as its name suggests. Create stream of bits at one end of the communication and transfer it to other end to receive as it is. Stream socket data transferring is reliable, connection oriented delivery. Data transmission is controlled by and quality is guaranteed by protocols like Transmission Control Protocol (TCP). In Datagram Sockets, packet is created by attaching the destination IP address and sends it without establishing physical or logical connection between source and destination. User Datagram Protocol (UDP) governs the communication in this mode. Also it is better to have some understanding about OSI 7 layer model which I do not try to include here. OK! Thats more than enough about basic theories. Lets start programming with famous and simple client-server model. In this model you have to first start up the server process and put it to a loop which never ends (Because web server should function 24*7 and serves to the clients). That server process should wait for client connections and after client connected; request handling need to be done. Generally client initiates the communication by sending request to the server; therefore client process is called as active participant while server is called as passive participant of the communication. Following diagrams shows what exactly we should do in order to make a client and server.
In C and C++ socket (), bind (), listen (), accept (), etc. kind of system calls are used to make a connection between client and server. Lets start with server side stuff. Socket () Exact system call is int socket (int domain, int type, int protocol) In order to use this system call it is required to include types.h and socket.h header files #include <sys/types.h> #include <sys/socket.h> OKwhat are those arguments then? int domain: - this refers to protocol family which this socket should created on. There are
many protocol families available such as AF_INET, AF_UNIX, AF_IPX, etc. If you are programming on IPv4 then use AF_INET or if it is IPv6 then use AF_INET6. int type: - type of socket (Stream Socket or Datagram Socket) you are going to create. That means whether it is a SOCK_STREAM or SOCK_DGRAM. For now just use SOCK_STREAM as the value for this argument. int protocol: - use 0 here. For socket type belongs to a particular protocol family has single protocol. You can get it by setting this value to 0 (zero) If socket function succeeds then it returns the socket descriptor (simply an integer value) or -1 if any error occurred. Therefore check whether return value is -1 (any error occurred) and if it is so then return immediately.
?
1#include <sys/types.h> 2#include <sys/socket.h> 3 4int iSocketFD; 5iSocketFD = socket(AF_INET, SOCK_STREAM, 0); 6if(iSocketFD == -1) 7{ 8 perror("Error ocurred while crearting socket!"); 9 exit(1); 10} Bind () After creating a socket we have to associate a port for that socket. bind () system call is used for that purpose. int bind(int sockfd, struct sockaddr *my_addr, int addrlen) int sockfd :- socket file descriptor which is returned by socket() system call. struct sockaddr *my_addr :- Pointer to a struct sockaddr. Int addrlen: - length of sockaddr. 2nd argument may little bit confusing. It is just a pointer to struct sockaddr which is given below. struct sockaddr { unsigned short sa_family; // address family, AF_xxx char sa_data[14]; // 14 bytes of protocol address }; This structure holds socket address information. To deal with this structure normally we used another structure. struct sockaddr_in { short sin_family; // Address family unsigned short sin_port; // Port number struct in_addr sin_addr; // Internet address char sin_zero[8]; // Same size as struct sockaddr }; It is important to remember that struct sockaddr_in* can be cast to struct sockaddr* and vice versa. As a programming practice first just set values for sockaddr_in structure and at
the time of binding, cast it to sockaddr structure. sin_port and sin_addr should be in network byte order (Ill give some details about this in later. For now if you supply port number as integer value then use htons() function to convert it to network byte order). OK..one more structure to go. As you see internet address (sin_addr) should be supply through another structure called in_addr which is given below. struct in_addr { unsigned long s_addr; // thats a 32-bit long, or 4 bytes }; Do you remember from where we reached to this point? We were in bind system calls 2nd argument. In this argument we had to supply address and port number to bind with socket, for that we use above 3 structures. Now its time to set values for structures. 1/* create a variable (really?) of type struct sockaddr_in. 2Using this variable it is possible to call 3to variables inside the struct 4(as similar way to create an object of a 5class and calling to class members using that object).*/ 6struct sockaddr_in sServerAddress; 7 8/*setting the address family*/ 9sServerAddress.sin_family = AF_INET; 10 11/*set the address of machine which server runs on. 12 If you dont know about the server address or if 13 server is connected and serves through multiple 14 network interfaces then use INADDR_ANY as the 15 value (this will automatically fills the IP 16 address which server runs on). Just look how 17 it calls to in_addr structs s_addr long variable.*/ 18sServerAddress.sin_addr.s_addr = INADDR_ANY; 19int iPortNumber = 2400; 20/*set the port number. htons() function 21 used to convert integer port number into 22 network byte order.*/ 23sServerAddress.sin_port = htons(iPortNumber); Those set of arguments are enough for normal usage of bind system call. Wait..we have one argument to fill in bind system call. Length of the sockaddr struct. Nothing to worry just use sizeof() operator. sizeof(sServerAddress); Following code segment shows the entire coding for bind system call.
? ?
1struct sockaddr_in sServerAddress; 2iPortNumber = 2500; 3sServerAddress.sin_family = AF_INET; 4sServerAddress.sin_addr.s_addr = INADDR_ANY; 5sServerAddress.sin_port = htons(iPortNumber); 6 7int iReturn = bind(iSocketFD, (struct sockaddr*) &sServerAddress,
sizeof(sServerAddress)); 8 if(iReturn == -1) 9 { 10 perror("Error ocurred while binding socket!"); 11 exit(1); 12 } So far we have created a socket and bind it to an IP address and port. As a server what it should do next? Any guesses? Waitingwaitingwaiting..yes thats the point. It should wait for incoming connections. In programming terms, it should listen to client connections and as soon as client connects, that connection should be accepted and serve to the client. For listening purpose listen () system call is used. listen() Exact listen () system call is as follows. int listen(int sockfd, int backlog); int sockfd :- file descriptor returned by socket() system call int backlog :- size of incoming connections queue. Incoming connections are waiting on this queue until those are accepted. Listen will return -1 if any error occurred.
?
1int iListenReturn = listen(iSocketFD, 5); 2if(iListenReturn == -1) 3{ 4 perror("Error ocurred while going to listen!"); 5 exit(1); 6} Accept() Oh..Client connection received to the server, what should I do now? Heyyou have to accept it and start your communication. To accept client incoming connections at server side, accept () system call is used. int accept(int sockfd, void *addr, int *addrlen); sockfd :- file descriptor returned by socket() system call addr :- pointer to the struct sockaddr_in of the client. This will contains the client address and port number. Addrlen :- Pointer to the size of clients sockaddr_in struct. Accept system call will return new file descriptor for accepted connection. That file descriptor should used for further operations of a particular connection (i.e. sending and receiving information). At this point server has two file descriptors. One is which returned by socket () system call which is now listening to incoming connections and other one is which returned by accept () system call.
?
struct sockaddr_in sClientAddress; //struct variable to hold client information 1 socklen_t iClientStructLength; // variable to hold client strcuct 2 sockaddr_ins length 3 iClientStructLength = sizeof(sClientAddress); 4 int iNewFileDesciptor = accept(iSocket, (struct sockaddr *) &sClientAddress, &iClientStructLength);
int iNewFileDesciptor; 1 struct sockaddr_in sClientAddress; 2 socklen_t iClientStructLength; 3 iClientStructLength = sizeof(sClientAddress); 4 iNewFileDesciptor = accept(iSocketFD, (struct sockaddr*) 5 &sClientAddress, &iClientStructLength); 6 if(iNewFileDesciptor == -1) 7 { 8 perror("Error ocurred while accepting the connection!"); 9 exit(1); 10 } Read and Write Now its time to read something from socket and write something to socket. After accepting client connection, client can send message/ information/ request to the server (which server reads from socket) and server can send message/ information/ response to the client (which server writes to the socket). For reading and writing purposes file descriptor which is returned by accept system call is used. #include <sys/unistd.> ssize_t read(int fd, void *buf, size_t count) int fd :- file descriptor returned by accept system call void* buf :- pointer to buffer to store read information size_t count :- this number of bytes are read and putted to the buffer On success read () will return number of bytes read from the socket and -1 will return if any error occurred. #include <sys/unistd.> ssize_t write(int fd, const void *buf, size_t count) int fd :- file descriptor returned by accept system call void* buf :- pointer to buffer to store information to be write to the socket. size_t count :- this number of bytes write to the socket from buffer On success write () will return the number of bytes written to the socket and -1 will return if any error occurred. Following code segment shows the exact usage of read () and write () functions. 1char zBuffer[256]; //create a buffer to store information read and 2write from/to socket 3bzero(zBuffer, 256);//Empty the buffer 4int iReadReturn = read(iNewFileDesciptor, zBuffer, 255); 5if(iReadReturn < 0) 6{ 7 perror("Error ocurred while reading from socket!"); 8} 9int iWriteReturn = write(iNewFileDesciptor, "Hi there, welcome to the 10simple server", 38); 11if(iWriteReturn < 0) 12{
?
perror("Error ocurred while writing to the socket!"); } After everything is done just called to close () function to close the connection. Remember to close both file descriptors. close(iNewSocket); close(iSocket); Complete server code can be download from here . Client side programming will be available in next post. Just try to understand the server code, compile and run it. Good luck..
All right now lets move to the second part of the basic client-server programming, that is programming the client. It is more similar to programming a server but its little bit easy. Because we only have to handle two system calls here; socket() and connect(). But if you did not read the post about programming a server then this post may confusing since Ill not cover anything state there, so first read it from here . Since clients are not listening to incoming connections hence not accepting any connections listen() and accept() system calls are not needed for client. Socket() Exact system int socket (int domain, int So, it is same as server programs socket system call.
?
call type,
int
is protocol)
1<font size="3">#include <sys/types.h> 2#include <sys/socket.h> 3 4int iSocketFD; 5iSocketFD = socket(AF_INET, SOCK_STREAM, 0); 6if(iSocketFD == -1) 7{ 8 perror("Error while creating socket!"); 9 exit(1); 10} 11</font>
Connect() Exact connect () system call is int connect(int sockfd, struct sockaddr *serv_addr, int addrlen) int sockfd :- Socket descriptor returned by socket() system call struct sockaddr *serv_addr :- this struct holds the information (IP and port number) about the destination server. int addrlen :- Length of the serv_addr If connect () fails due to any reason it will send -1.
Nothing much to do here also, you have already practiced how to fill above three parameters in server programs bind () system call. Sockaddr struct can be set via filling values to sockaddr_in struct and later casting to Sockaddr. struct sockaddr_in {
short sin_family; // Address family unsigned short sin_port; // Port number struct in_addr sin_addr; // Internet address char sin_zero[8]; // Same size as struct sockaddr }; As in server program let's set values for sockaddr_in
?
1//create a variable of type struct sockaddr_in 2struct sockaddr_in sServerAddress; 3 4int iPortNumber = 2500; 5 6sServerAddress.sin_family = AF_INET; 7 8/*Ill going to run both server and client on my local machine. 9 If you wish to run your server on remote machine then use 10 that machines IP address here. htons () function used to 11 convert address to network byte order */ 12sServerAddress.sin_addr.s_addr = inet_addr("127.0.0.1"); 13 14//convert integer port number in to network byte order. 15sServerAddress.sin_port = htons(iPortNumber); Complete connect system call is listed in following code segment iPortNumber = 2500; 1 sServerAddress.sin_family = AF_INET; 2sServerAddress.sin_addr.s_addr = inet_addr("127.0.0.1"); 3 sServerAddress.sin_port = htons(iPortNumber); 4 5 if (connect(iSocketFD, 6 (sockaddr*)&sServerAddress,sizeof(sServerAddress)) == -1) 7 { 8 perror("Error while connecting!"); 9 exit(1); 10 } Read and Write This is also similar to read and write we have done in server program. Just for fun lets prompt client to enter some message and send it to server.
? ?
1// Buffer to store send and receive information 2char zBuffer[256]; 3 4bzero(zBuffer,256); // Empty the buffer 5printf("Enter Message : "); // Prompt user to enter message 6 7// get user input and put it to the buffer 8fgets(zBuffer,255,stdin); 9 10//write number of bytes (specified by third argument) to the socket 11int iReturnValue = write(iSocketFD,zBuffer,strlen(zBuffer)); 12 13bzero(zBuffer,256); // Empty the buffer 14 15// Read number of bytes from the socket and put to the buffer. 16int iReturnValue2 = read(iSocketFD,zBuffer,255); Complete code for reading and writing is given below.
?
1char zBuffer[256]; 2bzero(zBuffer,256); 3 4printf("Enter Message : "); 5fgets(zBuffer,255,stdin); 6 7int iReturnValue = write(iSocketFD,zBuffer,strlen(zBuffer)); 8if(iReturnValue < 0) 9 perror("Error while writing to the server!"); 10 11bzero(zBuffer,256); 12int iReturnValue2 = read(iSocketFD,zBuffer,255); 13if(iReturnValue2 < 0) 14 perror("Error while reading from socket!"); 15 16printf("Client Received a Message: %s\n", zBuffer); To test the client you need to first run the server code and then client code. Server will first prompt that Clients IP address. Meantime client program will prompt user to enter the message and after entering the message it will send to the server. Server will respond to that message with welcome message. Complete client program can be download from here .
Server program which I have included in earlier post is really simple one. The main problem associated with that code is; it just accepts one client and serves to it and returns from the program. Generally servers are not like that. The next step of socket programming is that enhance the server to accept multiple client connections and function
continuously. Most simplest way to do this is by putting accept (), read () and write () system calls in endless while loop.
?
<font size="3">while (true) 1 { 2 iClientStructLength = sizeof(sClientAddress); 3 iNewFileDesciptor = accept(iSocketFD, (struct sockaddr*) 4 &sClientAddress, &iClientStructLength); 5 6 if(iNewFileDesciptor == -1) 7 { perror("Error ocurred while accepting the connection!"); 8 exit(1); 9 } 10 11 else printf("Client is: %s\n", inet_ntoa(sClientAddress.sin_addr)); 12 13 14 bzero(zBuffer, 256);//Empty the buffer 15 int iReadReturn = read(iNewFileDesciptor, zBuffer, 255); 16 if(iReadReturn < 0) 17 { 18 perror("Error ocurred while reading from socket!"); 19 } 20 printf("Simple server received a message: %s\n", zBuffer); 21 22 int iWriteReturn = write(iNewFileDesciptor, 23"Hi there, welcome to the simple server", 38); 24 if(iWriteReturn < 0) 25 { 26 perror("Error ocurred while writing to the socket!"); 27 } 28 close(iNewFileDesciptor); 29 } 30 31 </font>
This code segment shows the while loop part of the server which accepts multiple clients. All the other things are same as what I have included in earlier post . If you add this part to your server program and run it with simple client which we have already created you can see that it can accept multiple clients. Wait Theres a problem..Have you noticed it??? Ok..it can accept multiple clients but servicing to those clients are done as the same order as they have connected. That means if you run the server program and then client program (Client1) (Do not send anything to server) and again client program (Client 2). Client 2 cant send anything to server until client 1 sends something to server. Reason is that server program stuck in the while loop to complete read and write operations to already accepted client1. So server cant serves to client2 until it finishes servicing to client1. In order to overcome this issue we can take few approaches. Lets first see most simple approach of using threads. By using threads we let server program to accept client and assign a new thread to serve to that client until main thread accepting another client or doing some other useful task. In C++ fork() system call can be used to achieve this task. (fork() system call is not something use to create new thread but it creates new child process)
#include <sys/stdio.h> pid_t fork(void); fork() system call take no arguments and returns the process ID. After calling to fork() both parent and child processes executes the next instruction following the fork call. Therefore we need to distinguish parent and child processes by examining there PIDs.
When execuitng fork() if any error occurred then it will return -1. fork() returns 0 to the newly created child process. fork() returns positive value to the parent process. Following code segment shows how we can use fork() system call in our server program.
?
while(true) { 1 iClientStructLength = sizeof(sClientAddress); 2 iNewFileDesciptor = accept(iSocketFD, (struct sockaddr*) 3 &sClientAddress, &iClientStructLength); 4 if(iNewFileDesciptor == -1) 5 { 6 perror("Error ocurred while accepting the connection!"); 7 exit(1); 8 9 } 10 else printf("Client is: %s\n" ,inet_ntoa(sClientAddress.sin_addr)); 11 12 13 if(fork() == 0) 14 { bzero(zBuffer, 256); 15 int iReadReturn = read(iNewFileDesciptor, zBuffer, 255); 16 17 if(iReadReturn < 0) 18 { 19 perror("Error ocurred while reading from socket!"); 20 } 21 printf("Simple server received a message: %s\n", zBuffer); 22 23 int iWriteReturn = write(iNewFileDesciptor, "Hi there, welcome to 24the simple server", 38); 25 if(iWriteReturn < 0) 26 { 27 perror("Error ocurred while writing to the socket!"); 28 } 29 close(iNewFileDesciptor); 30 } 31 close(iNewFileDesciptor); } In here we let main program to accept the client connections and create separate child process to each accepted connection to reading and writing. Condition if(fork == 0) checks whether this process is child process and if it is so then let that child process to reading and writing to/from socket. How do we run this program in multiple client mode? Just run server program Run simple client program twice (or as many times as you want) Then type and enter anything in any client Server will reply to that particular client
So theres no any order of how server serves to the clients. Since each client is servicing through separate process, processing can be done in any order. Complete server program
can be foundhere . Next post will include how we use select() system call to achieve the same result without forking. Have a nice day