Socket Programming Continued, TCP Server. Down the rabbit hole we go.

1 reply [Last post]
revall
revall's picture
Offline
SX VIP
Joined: 2014/09/29

After discovering the beauty of making a TCP client that can talk to a server we will now delve into a brave new world. Today we will discover how to make a TCP server in c++. If you have not read the tutorial before this one it would be helpful because we will be building on topics discused there. So, here is a link to the first tutorial, Socket Programming: TCP Client

The server code looks very similar to the TCP Client. But instead of using connect() we will be implementing bind(), listen(), and accept(). Bind will "bind" our code to the socket we create. Then listen will "listen" on the bound port for incoming connections. And when we finnally see a client trying to connect we accept() the client and open a connection and listen for the client to send it a message. When it recieves the message it will then respond to the client acordingly.

Like I said in the last tut, read every line of code and the comments should explain what it does. If anything is unclear please leave a comment and ask for help. Plus, the IRC is always a great place to go for help. There is most likely someone there that can help you with your problem there. Fo instructions on connecting to the IRC see this link.

NOTE: This code bellow is for linux and compiled with gcc version 4.7.2.

Lets get Started:

server.cpp

#include <iostream>
#include <cstring>
#include <string>
#include <sys/socket.h> // Needed for the socket functions
#include <netdb.h> // Needed for the socket functions
#include <unistd.h> //This since gcc 4.7 is needed to close the socket

using namespace std;

int main()
 {

    int connStatus; //So we can check the status of our connections
    struct addrinfo host_info; // The struct that getaddrinfo() uses
    struct addrinfo *host_info_list; //List of host Sockets populated by getaddrinfo()

    //Just like the TCP client we fill the host_info with 0s
    memset(&host_info, , sizeof host_info);

    host_info.ai_family = AF_UNSPEC; // We use an UNSPECified ip type so it will accept both TCP and UDP
    host_info.ai_socktype = SOCK_STREAM; // Set up the socket to talk with TCP
    host_info.ai_flags = AI_PASSIVE; //Use this so we can pass NULL in the nodename because bind will fill it later. Thank you beej.us for that tip


    //Pass in the IP we want to bind the server (NULL in this case because bind will take care of it), the port we want bound, and then populate the host info structs.
    connStatus = getaddrinfo("0.0.0.0", "5555", &host_info, &host_info_list); //I just use 0.0.0.0 for testing. <a href="http://en.wikipedia.org/wiki/0.0.0.0" title="http://en.wikipedia.org/wiki/0.0.0.0">http://en.wikipedia.org/wiki/0.0.0.0</a>  
    //Check It the addreess info was populated.
    if (connStatus != )  cout << "Error Adding sever info: " << gai_strerror(connStatus) ;


    cout << "Setting up a socket: \n";
    int serv_sock; // Initialize the socket

    //set up the socket with the data from getaddrinfo() and return the status
    serv_sock = socket(host_info_list->ai_family, host_info_list->ai_socktype, host_info_list->ai_protocol);
    //Check if the socket was created successfully
    if (serv_sock == -1)  cout << "Error creating socket\n";
    else cout << "Socket Created Successfully. Smile\n\n";

    cout << "Binding server to port: \n";

    //Check if socket is in use if it is but not active we will use it for our server.
    setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, "1",sizeof(serv_sock));

    //Then Actually Bind the socket
    connStatus = bind(serv_sock, host_info_list->ai_addr, host_info_list->ai_addrlen);
    //Then if it did not bind send user error
    if (connStatus == -1) cout << "Could not bind\n";
    else cout << "Port Successfully bound.\n\n";

    cout << "Waiting for connections: \n";
    connStatus =  listen(serv_sock, 5); //listen and allow 5 connections
    if (connStatus == -1)  cout << "Error listening on port" ;


    int acceptedClient; //Initialize a client socket
    struct sockaddr_storage client; //Set a space in memory for the socket.
    socklen_t addressSize = sizeof(client); //save the size of the client to pass it into our accept command
    //Open a connection to the client socket.
    acceptedClient = accept(serv_sock, (struct sockaddr *)&client, &addressSize);
    if (acceptedClient == -1) cout << "Error Listening \n";
    else cout << "Connection accepted: \n";



    //Waits for the client to send us date
    cout << "Waiting for data \n";
    ssize_t bytesRecieved; //Setup a space in memory to hold the size of data we get from the client
    char buffer[5000];//Set up a buffer to hold incoming data. 5000 bytes
    //save the recv size to check recv status
    bytesRecieved = recv(acceptedClient, buffer,5000, );
    // If no data arrives then wait for it to
    if (bytesRecieved == ) cout << "Connection Terminated by Client" ;//Inform the server that the client is gone
    if (bytesRecieved == -1)cout << "Error Receiving Data" ;//Yell at the server user
    cout << bytesRecieved << " bytes received\n\n" ; //Display the number of bytes transfered
    buffer[bytesRecieved] = '\0'; //Add null termination character to end of array to C to know you hit the end of the string of data
    //this is so your string get terminatedfalk093*AF*#9qna

    cout << buffer << endl; //Display the received to the server.
   
    //if(buffer == "hello\0") send(acceptedClient, "Hello Client", 12, 0);
    //could be a nice way to talk back logically

    //Responds to clients request
    cout << "Sendind Message to client: \n\n";
    //Set up a message to send
    char *msg = "[*] Message Recieved Thank you for your comunication\r\n";
    ssize_t bytesSent = ; //
    int len = strlen(msg); //save the lenght of the message as an integer
    bytesSent = send(acceptedClient, msg, len, );

    //Standard Shit, look at the TCP client tutorial for more details
    cout << "Closing Clients and Server\n";
    freeaddrinfo(host_info_list);
    close(acceptedClient);
    close(serv_sock);

return ;
}

To use this code you need to compile and run it.
use command: g++ server.cpp -o server then ./server will run it. It will pop up and tell you that is is waiting for connections

The Next Step from here will be to make a threaded server that will accept several clients and we will make a server loop that keeps the server going after clients disconnect.

The Next section is the TCP client from the last tutorial, I just added the servers IP and Port that we set up in our code here for our server instead of google.. I am putting this here so you can just copy and paste if you can to see how it works. It should run as is if you use gcc to compile it.

client.cpp

#include <iostream>
#include <cstring>
#include <sys/socket.h> //Needed to create the socket. Gives access to system commands
#include <netdb.h> //Needed for socket
#include <errno.h> //This will allow use to translate error number to english
#include <unistd.h> //Since gcc 4.7 you now need this to close socket

using namespace std;

int main()
{
    //Here I just reused the TCP Client code from the last tutorial
    int connStatus; //This is where I will store the status for the server info population
    struct addrinfo host_info; // The struct that getaddrinfo() fills up with data.
    struct addrinfo *host_info_list; // Pointer to the to the linked list of host_info's.

    memset(&host_info, , sizeof host_info); //0 fill the struct. Make sure there is no bad data

    //Set up the Structs for the Server Data.
    cout << "Setting server data into the addrinfo struct.\n\n";

    host_info.ai_family = AF_INET; //Set IP to unspecified. this allows for TCP and UDP transmission
    host_info.ai_socktype = SOCK_STREAM; //Sets the socket type to acccept TCP transmission

    connStatus = getaddrinfo("0.0.0.0", "5555", &host_info, &host_info_list); //Populates the structs with addrinfo of google on port 80

     //Check if we were able to get data from the server
    if(connStatus == ) cout << "Connection information populated successfuly\n\n";
    else if(connStatus != ) cout << "Connection error: " << gai_strerror(connStatus) << endl;

    cout << "Creating the socket now...";

    int sock; //Descriptor for our socket

    sock = socket(host_info_list->ai_family, host_info_list->ai_socktype, host_info_list->ai_protocol); //Opens internet socket with host, type, and protocol

    //Make sure the sock was created
    if(sock == -1) cout << "Socket Error: " << endl; //Need to put error reporting in still
    else cout << "Socket Created\n\n";

    cout << "Connecting to the server...";
    //Pass the connection code the Socket, the Address, and the Address length. return connection status
    connStatus = connect(sock, host_info_list->ai_addr, host_info_list->ai_addrlen);
    //Later I will add code to reatempt connection if it is lost. like 5 attempts
    if(connStatus == -1) cout << "Connection Error: " << endl;
    else cout << "You are now connected.\n\n";

    cout << "Sending Data... \n\n";
    char *msg = "[<<] Hello server I is client\r\n"; //Msg to be sent
    int length; //Initialize msg lenghth
    ssize_t bytesSent; //initialize bytesSent as a ssize_t
    length = strlen(msg); //get length of msg for the buffer
    //Tie to socket, send msg, give it the msg length for the buffer and set flags to 0. these are the same as write() function.
    bytesSent = send(sock, msg, length, );

    //Then we need to receive the data back from server
    //The recv func will wait for a response to system.

    cout << "Waiting to recieve data... \n\n";
    ssize_t bytesReceived;
    char buffer[5000]; //Set up a buffer to recveive data
    bytesReceived = recv(sock, buffer, 5000, );
    if(bytesReceived == ) cout << "Host disconnected.\n";
    if(bytesReceived == -1) cout << "Connection error\n";
    cout << bytesReceived << " bytes received :" << endl ;
    cout << buffer << endl;

    //If you open a door close it
    cout << "\n\nTransaction success. Closing socket...\n";
    freeaddrinfo(host_info_list); //Releases host information from memory
    close(sock);
}

To use g++ client.cpp -o client then ./client will connect it to the server send it a message and recieve the message from the server and then they both close.

Have fun with it. Learn From it. And grow.

As always happy hacking,
-revall