A simple C++ socket example

This is a simple C++ socket example with a client and echo server. This tutorial is based on  simple server and client chat (linux) written by Hassan M. Yousuf.

I do not pretend bring a long explanation about sockets, you can find a lot of information in Google. I  share a basic example to understand how it works.

Socket workflow

source: http://www.tenouk.com/Module39_files/image008.png

Server side

/*!
 * Simple socket program server.cpp
 * Version - 1.0.0
 * Based on: Simple chat program (server side).cpp - http://github.com/hassanyf
 *
 * Copyleft (c) 2017 Rodrigo Tufino <rtufino@ups.edu.ec, r.tufino@alumnos.upm.es>
 */

#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

using namespace std;

int main() {
 /* -------------- INITIALIZING VARIABLES -------------- */
 int server, client; // socket file descriptors
 int portNum = 2705; // port number
 int bufSize = 1024; // buffer size
 char buffer[bufSize]; // buffer to transmit
 bool isExit = false; // var fo continue infinitly

 /* Structure describing an Internet socket address. */
 struct sockaddr_in server_addr;
 socklen_t size;

 cout << "\n- Starting server..." << endl;

 /* ---------- ESTABLISHING SOCKET CONNECTION ----------*/

 server = socket(AF_INET, SOCK_STREAM, 0);

 /*
 * The socket() function creates a new socket.
 * It takes 3 arguments:
 * 1) AF_INET: address domain of the socket.
 * 2) SOCK_STREAM: Type of socket. a stream socket in
 * which characters are read in a continuous stream (TCP)
 * 3) Third is a protocol argument: should always be 0.
 * If the socket call fails, it returns -1.
 */

 if (server < 0) {
 cout << "Error establishing socket ..." << endl;
 exit(-1);
 }

 cout << "- Socket server has been created..." << endl;

 /*
 * The variable serv_addr is a structure of sockaddr_in.
 * sin_family contains a code for the address family.
 * It should always be set to AF_INET.
 * INADDR_ANY contains the IP address of the host. For
 * server code, this will always be the IP address of
 * the machine on which the server is running.
 * htons() converts the port number from host byte order
 * to a port number in network byte order.
 */

 server_addr.sin_family = AF_INET;
 server_addr.sin_addr.s_addr = htons(INADDR_ANY);
 server_addr.sin_port = htons(portNum);

 /*
 * This function is used to set the socket level for socket.
 * It is used to avoid blind error when reuse the socket.
 * For more info, see the url.
 * http://stackoverflow.com/questions/5592747/bind-error-while-recreating-socket
 */

 int yes = 1;
 if (setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) == -1) {
 perror("setsockopt");
 exit(1);
 }
 /* ---------------- BINDING THE SOCKET --------------- */

 /*
 * The bind() system call binds a socket to an address,
 * in this case the address of the current host and port number
 * on which the server will run. It takes three arguments,
 * the socket file descriptor. The second argument is a pointer
 * to a structure of type sockaddr, this must be cast to
 * the correct type.
 */

 if ((bind(server, (struct sockaddr*) &server_addr, sizeof(server_addr)))
 < 0) {
 cout
 << "- Error binding connection, the socket has already been established..."
 << endl;
 exit(-1);
 }

 /* ------------------ LISTENING CALL ----------------- */

 size = sizeof(server_addr);
 cout << "- Looking for clients..." << endl;

 /*
 * The listen system call allows the process to listen
 * on the socket for connections.
 * The program will be stay idle here if there are no
 * incomming connections.
 * The first argument is the socket file descriptor,
 * and the second is the size for the number of clients
 * i.e the number of connections that the server can
 * handle while the process is handling a particular
 * connection. The maximum size permitted by most
 * systems is 5.
 */

 listen(server, 1);

 /* ------------------- ACCEPT CALL ------------------ */

 client = accept(server, (struct sockaddr *) &server_addr, &size);

 /*
 * The accept() system call causes the process to block
 * until a client connects to the server. Thus, it wakes
 * up the process when a connection from a client has been
 * successfully established. It returns a new file descriptor,
 * and all communication on this connection should be done
 * using the new file descriptor. The second argument is a
 * reference pointer to the address of the client on the other
 * end of the connection, and the third argument is the size
 * of this structure.
 */

 if (client < 0)
 cout << "- Error on accepting..." << endl;

 string echo;
 while (client > 0) {
 // Welcome message to client
 strcpy(buffer, "\n-> Welcome to echo server...\n");
 send(client, buffer, bufSize, 0);
 cout << "- Connected with the client, waiting for data..." << endl;
 // loop to recive messages from client
 do {
 cout << "\nClient: ";
 echo = "";
 /*
 * A send operation from client is done for each word
 * has written on it's terminal line. We need a special
 * character to stop transmission and this loop works
 * until this char ('*') arrives.
 */
 do {
 // wait the request from client
 recv(client, buffer, bufSize, 0);
 cout << buffer << " ";
 // verify if client does not close the connection
 if (*buffer == '#') {
 // exit loop and say goodbye (It's a polite server :D)
 isExit = true;
 *buffer = '*';
 echo = "Goodbye!";
 } else if ((*buffer != '#') && (*buffer != '*')) {
 // concatenate the echo string to response to the client
 echo += buffer;
 echo += " ";
 }
 } while (*buffer != '*');
 // copy the echo string to the buffer
 sprintf(buffer, "%s", echo.c_str());
 // send the message to the client
 send(client, buffer, bufSize, 0);
 } while (!isExit);

 /* ---------------- CLOSE CALL ------------- */
 cout << "\n\n=> Connection terminated with IP "
 << inet_ntoa(server_addr.sin_addr);
 close(client);
 cout << "\nGoodbye..." << endl;
 exit(1);

 }

 /* ---------------- CLOSE CALL ------------- */
 close(server);
 return 0;

}

Client side

/*!
 * Simple socket program client.cpp
 * Version - 1.0.0
 * Based on: Simple chat program (client side).cpp - http://github.com/hassanyf
 *
 * Copyleft (c) 2017 Rodrigo Tufino <rtufino@ups.edu.ec, r.tufino@alumnos.upm.es>
 */
#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>

using namespace std;

int main() {
 /* -------------- INITIALIZING VARIABLES -------------- */
 int client; // socket file descriptors
 int portNum = 2705; // port number (same that server)
 int bufsize = 1024; // buffer size
 char buffer[bufsize]; // buffer to transmit
 char ip[] = "127.0.0.1"; // Server IP
 bool isExit = false; // var fo continue infinitly

 /* Structure describing an Internet socket address. */
 struct sockaddr_in server_addr;

 cout << "\n- Starting client..." << endl;

 /* ---------- ESTABLISHING SOCKET CONNECTION ----------*/

 client = socket(AF_INET, SOCK_STREAM, 0);

 /*
 * The socket() function creates a new socket.
 * It takes 3 arguments:
 * 1) AF_INET: address domain of the socket.
 * 2) SOCK_STREAM: Type of socket. a stream socket in
 * which characters are read in a continuous stream (TCP)
 * 3) Third is a protocol argument: should always be 0.
 * If the socket call fails, it returns -1.
 */

 if (client < 0) {
 cout << "\n-Error establishing socket..." << endl;
 exit(-1);
 }

 cout << "\n- Socket client has been created..." << endl;

 /*
 * The variable serv_addr is a structure of sockaddr_in.
 * sin_family contains a code for the address family.
 * It should always be set to AF_INET.
 * INADDR_ANY contains the IP address of the host. For
 * server code, this will always be the IP address of
 * the machine on which the server is running.
 * htons() converts the port number from host byte order
 * to a port number in network byte order.
 */

 server_addr.sin_family = AF_INET;
 server_addr.sin_port = htons(portNum);

 /*
 * This function converts an Internet address (either IPv4 or IPv6)
 * from presentation (textual) to network (binary) format.
 * If the comunication is on the same machine, you can comment this line.
 */
 inet_pton(AF_INET, ip, &server_addr.sin_addr);

 /* ---------- CONNECTING THE SOCKET ---------- */

 if (connect(client, (struct sockaddr *) &server_addr, sizeof(server_addr))
 < 0)
 cout << "- Connection to the server port number: " << portNum << endl;

 /*
 * The connect function is called by the client to
 * establish a connection to the server. It takes
 * three arguments, the socket file descriptor, the
 * address of the host to which it wants to connect
 * (including the port number), and the size of this
 * address.
 * This function returns 0 on success and -1
 * if it fails.
 * Note that the client needs to know the port number of
 * the server but not its own port number.
 */
 cout << "- Awaiting confirmation from the server..." << endl; //line 40

 // recive the welcome message from server
 recv(client, buffer, bufsize, 0);
 cout << buffer << endl;

 cout << "- Connection confirmed, you are good to go!" << endl;
 cout << "- Enter * to end the message" << endl;
 cout << "- Enter # to end the connection\n" << endl;
 // loop to send messages to server
 do {
 cout << "Message: ";
 /*
 * The function 'cin' get an word at time and send it
 * to the server. The send operation is call until
 * the user write '*'.
 */
 do {
 // read from terminal
 cin >> buffer;
 // send to the server
 send(client, buffer, bufsize, 0);
 if (*buffer == '#') {
 // exit from the loop
 *buffer = '*';
 isExit = true;
 }
 } while (*buffer != '*');
 // wait the response from the server
 cout << "Server says: ";
 recv(client, buffer, bufsize, 0);
 // print the server message
 cout << buffer << endl;
 } while (!isExit);

 /* ---------------- CLOSE CALL ------------- */

 cout << "\nConnection terminated.\n";

 /*
 * Once the server presses # to end the connection,
 * the loop will break and it will close the server
 * socket connection and the client connection.
 */
 close(client);
 return 0;
}

 

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *