Socket Programming in C++

74,107

Solution 1

You can find a working client-server program here: Beej's Guide to Network Programming

Solution 2

There is no socket API in the C++ Standard. The POSIX C API is fairly portable (the GNU libC documentation provides examples of UDP and TCP clients and servers that I usually turn to when I'm scratching together another server), or you could use the Boost.ASIO library for a more C++ experience....

Solution 3

A good C++ networking library is ACE. The only problem is that there aren't any good tutorials online that I have found. This book is pretty good though.

Solution 4

I write a simple client/server example in C/C++ without any extra functionalites. You may built your own use case referring to this example. The origin code is attached here and also open sources in github!.

Client

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

int main(int argc, char **argv) {
    struct sockaddr_in server_addr;     // set server addr and port
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = inet_addr(argv[1]);
    server_addr.sin_port = htons(8000);  // server default port

    int sock_client;
    char send_buf[65536];
    memset(send_buf, '\0', sizeof(send_buf));
    char *send_content = "I am client";
    strcpy(send_buf, send_content);

    if ((sock_client = socket(AF_INET,SOCK_STREAM, 0)) < 0) {
        return 0;
    }

    //connect server, return 0 with success, return -1 with error
    if (connect(sock_client, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
    {
        perror("connect");
        return 0;
    }

    char server_ip[INET_ADDRSTRLEN]="";
    inet_ntop(AF_INET, &server_addr.sin_addr, server_ip, INET_ADDRSTRLEN);
    printf("connected server(%s:%d). \n", server_ip, ntohs(server_addr.sin_port));

    //send a message to server
    send(sock_client, send_buf, strlen(send_buf), 0);
    close(sock_client);

    return 0;
}

Server

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


int main(int argc, char *argv[]) {

    int server_sockfd;      // server socket fd 
    struct sockaddr_in server_addr;     // server info struct
    server_addr.sin_family=AF_INET;     // TCP/IP
    server_addr.sin_addr.s_addr=INADDR_ANY;     // server addr--permit all connection
    server_addr.sin_port=htons(8000);       // server port

    /*创建服务器端套接字--IPv4协议,面向连接通信,TCP协议*/
    /* create socket fd with IPv4 and TCP protocal*/
    if((server_sockfd=socket(PF_INET,SOCK_STREAM,0))<0) {  
                    perror("socket error");
                    return 1;
    }

    /*将套接字绑定到服务器的网络地址上*/
    /* bind socket with server addr */
    if(bind(server_sockfd,(struct sockaddr *)&server_addr,sizeof(struct sockaddr))<0) {
                    perror("bind error");
                    return 1;
    }


    /*监听连接请求--监听队列长度为20*/
    /* listen connection request with a queue length of 20 */
    if(listen(server_sockfd,20)<0) {
                    perror("listen error");
                    return 1;
    }
    printf("listen success.\n");

    char recv_buf[65536];
    memset(recv_buf, '\0', sizeof(recv_buf));

    while (1) {
        struct sockaddr_in client_addr;
        socklen_t length = sizeof(client_addr);
        //进程阻塞在accept上,成功返回非负描述字,出错返回-1
        // block on accept until positive fd or error
        int conn = accept(server_sockfd, (struct sockaddr*)&client_addr,&length);
        if(conn<0) {
            perror("connect");
            return -1;
        }

        printf("new client accepted.\n");

        char client_ip[INET_ADDRSTRLEN] = "";
        inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);

        while(recv(conn, recv_buf, sizeof(recv_buf), 0) > 0 ){
            printf("recv %s from client(%s:%d). \n", recv_buf, client_ip, ntohs(client_addr.sin_port));
            memset(recv_buf, '\0', strlen(recv_buf));
            break;
        }
    }

    printf("closed. \n");
    close(server_sockfd);
    return 0;
}

Solution 5

While standard C++ does not include a standard socket object, there is an experimental technical specification currently available in g++ using compiler flag -std=gnu++2a

https://en.cppreference.com/w/cpp/header/experimental/net

This proposed extension is not standardized yet as of C++20, and there's no guarantee that it is, but it is based on the Boost ASIO library, which you can also get for free on most platforms.

https://www.boost.org/doc/libs/1_76_0/doc/html/boost_asio/tutorial.html

Or you can create your own from scratch.

"C++ Web Server from Scratch | Part 1: Creating a Socket Object" is a nice video tutorial I found creating object oriented UNIX style sockets in C++.

https://www.youtube.com/watch?v=YwHErWJIh6Y

You can do something similar on Windows with winsock but using winsock.h instead of all the UNIX style header files, and you need to add some windows style initialization and cleanup. Boost does it for both Windows and UNIX style so I'd recommend using it.

If you just want something quick for UNIX style networking with no experimental libraries, this simple Socket.h header file can get you started:

// Adapted from C code example 
// at https://www.geeksforgeeks.org/socket-programming-cc/
#include <stdio.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <string>
#include <stdexcept>


class Socket {
        int sock;
public:
        Socket(int socket) : sock(socket) {
                if (sock<0) throw std::runtime_error("Socket creation error");
        }
        Socket() : Socket(socket(AF_INET, SOCK_STREAM, 0)) {}
        std::string rx() {
                char buffer[1024] = {0};
                int n = read( sock , buffer, sizeof(buffer));
                return std::string(buffer,n);
        }
        void tx(std::string s) {
                send(sock , s.c_str() , s.length() , 0);
        }
        int getSocket() {
                return sock;
        }
};

class Connection: public Socket {
public:
        Connection(int socket) : Socket(socket) {}
        Connection(std::string address,unsigned short port): Socket()
        {
                struct sockaddr_in serv_addr;
                serv_addr.sin_family = AF_INET;
                serv_addr.sin_port = htons(port);
                // Convert IPv4 and IPv6 addresses from text to binary form
                if(inet_pton(
                                AF_INET,
                                address.c_str(),
                                &serv_addr.sin_addr
                ) <= 0) throw std::runtime_error("Invalid address: Address not supported");

                if (connect(
                                getSocket(),
                                (struct sockaddr *)&serv_addr,
                                sizeof(serv_addr)
                ) < 0) throw std::runtime_error("\nConnection Failed \n");
        }
};

class PortListener {
        Socket server; // fd is created in default Socket constructor
        struct sockaddr_in address;
        int opt = 1;
public:
        PortListener(unsigned short port) {

                // Forcefully attaching socket to the port 8080
                if (setsockopt(
                                server.getSocket(),
                                SOL_SOCKET,
                                SO_REUSEADDR | SO_REUSEPORT,
                                &opt,
                                sizeof(opt)
                )) throw std::runtime_error("setsockopt");

                address.sin_family = AF_INET;
                address.sin_addr.s_addr = INADDR_ANY;
                address.sin_port = htons( port );

                // Forcefully attaching socket to the port 8080
                if (bind(
                                server.getSocket(),
                                (struct sockaddr *)&address,
                                sizeof(address)
                ) < 0) throw std::runtime_error("bind failed");


                if (listen(server.getSocket(), 3) < 0) {
                        throw std::runtime_error("listen");
                }
        }
        Connection waitForConnection() {
                int new_socket;
                int addrlen = sizeof(struct sockaddr_in);
                new_socket = accept(
                                server.getSocket(),
                                (struct sockaddr *)&address,
                                (socklen_t*)&addrlen
                );
                if (new_socket<0) throw std::runtime_error("accept");
                return Connection(new_socket);
        }
};

Here's sample server code server.cpp

#include "Socket.h"
#include <iostream>

int main(int argc, char const *argv[]) {
        using namespace std;
        try {
                // Normally you'd spawn threads for multiple connections.
                Connection conn = PortListener(8080).waitForConnection();
                cout << conn.rx() << endl;
                conn.tx("Hello from server");
                cout << "Hello message sent" << endl;
        } catch (runtime_error &e) {
                cerr << e.what() << endl;
                return EXIT_FAILURE;
        }
        return 0;
}

Here's sample client code client.cpp

#include "Socket.h"
#include <iostream>

int main(int argc, char const *argv[]) {
        using namespace std;
        try {
                Connection conn("127.0.0.1",8080);
                conn.tx("Hello from client");
                cout << "Hello message sent" << endl;
                string s = conn.rx();
                cout << s << endl;
        } catch (exception &e) {
                cerr << e.what() << endl;
                return EXIT_FAILURE;
        }
    return 0;
}

To compile and run under g++:

$ g++ server.cpp -o server
$ g++ client.cpp -o client
$ server &
[1] 2468
$ client
Hello message sent
Hello from client
Hello message sent
Hello from server
[1]+  Done                    ./server

This should work on any UNIX-like OS. (For Windows, you can install WSL to add a Linux OS and run under Linux)

Share:
74,107
Swapnil Gupta
Author by

Swapnil Gupta

Updated on July 26, 2022

Comments

  • Swapnil Gupta
    Swapnil Gupta almost 2 years

    Can anybody provide me some sample example on Client and server connection using sockets in C++. I have gone through some tutorials now i want to implement it. How to start ?

  • Alexander Kleinhans
    Alexander Kleinhans over 6 years
    I edited the old link. It's here now: beej.us/guide/bgnet/html/single/bgnet.html
  • Trevor Lee Oakley
    Trevor Lee Oakley almost 5 years
    This is basically a book recommendation. Is that part of the standards at Stack Overflow?
  • zooropa
    zooropa almost 5 years
    I gave a recommendation on how to learn the library since there aren't many online tutorials. I do answer the question. But, I can remove it and make it more generic by saying that there aren't many online tutorials but there are books available to help learning the library. Would that be better?
  • Trevor Lee Oakley
    Trevor Lee Oakley almost 5 years
    I am not being critical of you. I only made a comment because I see a lot of mixed standards at this site. I see numerous posts struck down for making recommendations and then I see countless recommendations at the site. I have no issues with recommendations but it seems a lot of people do at this site.
  • arr_sea
    arr_sea over 4 years
  • Ted Shaneyfelt
    Ted Shaneyfelt almost 3 years
    Maybe give a snippet of code for a client and server exchanging simple hello messages would be appropriate.