【C言語/C++】 TCP/IPで送受信を行うプログラム

目次

ソケット通信とは

ソケットは通信の出入口のようなものです.TCP/IPなどに代表されるHTTP通信などもソケット通信の一つです.すなわち,ソケットを使用することで異なるマシン間(もしくは同一マシン上)の異なるプロセス間で通信を可能にします.

ソケットプログラミング

簡単なサンプル問題として,次のようなプログラムを作成します.

  1. ソケットを作成
  2. 接続を確立
  3. クライアントが文字列を送信し,サーバが受け取る
  4. サーバが文字列を送信し,クライアントが受け取る
  5. 接続を閉じる

サーバプログラム

server.c


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>

int main(int argc, char const *argv[])
{
    const int port_number = 12345;
    // create socket
    int sockfd = 0;
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }
    // setup socket
    struct sockaddr_in server;
    int addrlen            = sizeof(server);
    server.sin_family      = AF_INET;
    server.sin_addr.s_addr = inet_addr("0.0.0.0"); // or INADDR_ANY
    server.sin_port        = htons(port_number);
    int opt                = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)))
    {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
    if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0)
    {
        perror("Bind error");
        exit(EXIT_FAILURE);
    }
    // start to listen requests
    if (listen(sockfd, SOMAXCONN) < 0)
    {
        perror("Listen error");
        exit(EXIT_FAILURE);
    }
    // accept request from client
    struct sockaddr_in client;
    int client_len = sizeof(client);
    int sock       = 0;
    if ((sock = accept(sockfd, (struct sockaddr *)&client, (socklen_t *)&client_len)) < 0)
    {
        perror("Error: accept");
        exit(EXIT_FAILURE);
    }
    // read data from client
    char buff[1024] = {0};
    recv(sock, buff, sizeof(buff), 0);
    printf("recived message is \"%s\"\n", buff);
    // send data to client
    char *hello = "Hello from server";
    send(sock, hello, strlen(hello), 0);
    printf("Message sent to client\n");
    // close connecting socket
    close(sock);
    // close listening socket
    shutdown(sockfd, SHUT_RDWR);
    close(sockfd);
    return 0;
}

特定のIPアドレスのみを許可したい場合は,server.sin_addr.s_addrでListenするIPアドレスを指定します.今回は,すべてのIPアドレス許可しているため,inet_addr("0.0.0.0")(もしくはINADDR_ANY)を与えています.

server.sin_portでポート番号を指定します.今回は,12345を指定しています.

クライアントプログラム

client.c


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <arpa/inet.h>
#include <sys/socket.h>

int main(int argc, char const* argv[])
{
    const int port_number = 12345;
    int sock              = 0;
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("\n Socket creation error \n");
        exit(EXIT_FAILURE);
    }
    // setup socket
    struct sockaddr_in server;
    server.sin_family      = AF_INET;
    server.sin_addr.s_addr = inet_addr("127.0.0.1");
    server.sin_port        = htons(port_number);
    // connect to server
    if ((connect(sock, (struct sockaddr*)&server, sizeof(server))) < 0)
    {
        printf("\nConnection Failed \n");
        exit(EXIT_FAILURE);
    }
    // send data to server
    char* hello = "Hello from client";
    send(sock, hello, strlen(hello), 0);
    printf("Message sent to server\n");
    // read data from server
    char buff[1024] = {0};
    recv(sock, buff, sizeof(buff), 0);
    printf("recived message is \"%s\"\n", buff);
    // close connecting socket
    close(sock);
    return 0;
}

server.sin_addr.s_addrでサーバのIPアドレスを指定します.この場合,同一マシンのため127.0.0.1を与えています.

実行

サーバとクライアントプログラムをそれぞれビルドします.


gcc -o server server.c
gcc -o client client.c

サーバプログラムを実行し,待ち受け状態にします.


./server

サーバが立ち上がったことでポート12345が待ち受け状態になっているはずなので,下記コマンドで確認します.


netstat -tlnw
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State      
tcp        0      0 0.0.0.0:12345           0.0.0.0:*               LISTEN     
raw6       0      0 :::58  

別マシンの場合,サーバとの疎通確認は下記の方法でも行うことができます.

クライアントプログラムを実行します.


./client 
Message sent to server
recived message is "Hello from server"

サーバプログラムでは下記のような出力が得られます.


recived message is "Hello from client"
Message sent to client

プログラム例

入力した数値が素数判定するプログラム

ここでは,サンプルプログラムを少し変更して,クライアントから与えられた数値が素数かどうかの判定を行い,その結果を返すサーバを作ります.コードは,C++で記述します.

server.cpp


#include <unistd.h>
#include <cmath>
#include <string>

#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>

bool ToValue(unsigned long &val, const std::string &str)
{
    try
    {
        val = std::stoul(str);
        return true;
    }
    catch (...)
    {
        return false;
    }
}
bool IsPrime(const unsigned long &val)
{
    if (val < 2)
        return false;
    if (val == 2)
        return true;
    if (val % 2 == 0)
        return false;
    auto sq = static_cast<unsigned long>(std::sqrt(val));
    for (unsigned long i = 3; i <= sq; i += 2)
    {
        if (val % i == 0)
            return false;
    }
    return true;
}
int main(int argc, char const *argv[])
{
    const int port_number = 12345;
    // create socket
    int sockfd = 0;
    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }
    // setup socket
    struct sockaddr_in server;
    int addrlen            = sizeof(server);
    server.sin_family      = AF_INET;
    server.sin_addr.s_addr = inet_addr("0.0.0.0");
    server.sin_port        = htons(port_number);
    int opt                = 1;
    if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)))
    {
        perror("setsockopt");
        exit(EXIT_FAILURE);
    }
    if (bind(sockfd, (struct sockaddr *)&server, sizeof(server)) < 0)
    {
        perror("Bind error");
        exit(EXIT_FAILURE);
    }
    // start to listen requests
    if (listen(sockfd, SOMAXCONN) < 0)
    {
        perror("Listen error");
        exit(EXIT_FAILURE);
    }
    // accept request from client
    struct sockaddr_in client;
    int client_len = sizeof(client);
    int sock       = 0;
    if ((sock = accept(sockfd, (struct sockaddr *)&client, (socklen_t *)&client_len)) < 0)
    {
        perror("Error: accept");
        exit(EXIT_FAILURE);
    }
    // read data from client
    char buff[1024] = {0};
    recv(sock, buff, sizeof(buff), 0);
    const auto str    = std::string(buff);
    unsigned long val = 0;
    std::string msg   = "";
    if (ToValue(val, str))
    {
        if (IsPrime(val))
        {
            msg = "Prime Number";
        }
        else
        {
            msg = "Not Prime Number";
        }
    }
    else
    {
        msg = "Please send value!";
    }
    auto cmsg = msg.c_str();
    send(sock, cmsg, 1024, 0);
    close(sock);
    // close listening socket
    shutdown(sockfd, SHUT_RDWR);
    close(sockfd);
    return 0;
}

ToValue関数は下記記事で詳細を説明しています.

client.cpp


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <iostream>
#include <string>

#include <arpa/inet.h>
#include <sys/socket.h>

int main(int argc, char const* argv[])
{
    const int port_number = 12345;
    int sock              = 0, client_fd;
    if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    {
        printf("\n Socket creation error \n");
        exit(EXIT_FAILURE);
    }
    // setup socket
    struct sockaddr_in server;
    server.sin_family      = AF_INET;
    server.sin_addr.s_addr = inet_addr("127.0.0.1");
    server.sin_port        = htons(port_number);
    // connect to server
    if ((client_fd = connect(sock, (struct sockaddr*)&server, sizeof(server))) < 0)
    {
        printf("\nConnection Failed \n");
        exit(EXIT_FAILURE);
    }
    std::string str;
    std::cin >> str;
    // send data to server
    auto cstr = str.c_str();
    send(sock, cstr, 1024, 0);
    // read data from server
    char buff[1024] = {0};
    recv(sock, buff, sizeof(buff), 0);
    printf("recived message is \"%s\"\n", buff);
    // close connecting socket
    close(client_fd);
    return 0;
}

まとめ

この記事では,ソケットプログラミングのサンプルを紹介しました.また,少し応用して素数判定するプログラムを作りました.

このプログラムでは,暗号化を行っていないため,運用時にはSSL/TLSなどの暗号化をすべきでしょう.

暗号化したソケット通信に関しては下記の記事にまとめましたので,興味があればご覧ください.

参考にしたサイト

よかったらシェアしてね!
目次