Open1

neo-cでソケット通信

ab25cqab25cq

neo-cは簡単にwebサーバーが作れます。以下が例です。

#include <neo-c-net.h>

#define RESPONSE "HTTP/1.1 200 OK\r\n" \
                 "Content-Type: text/plain\r\n" \
                 "Content-Length: 11\r\n" \
                 "\r\n" \
                 "Hello World"

int main() {
    httpd_socket() {
        char buf[1024] = {0};
        read(it, buf, 1024);
        printf("Received request:\n%s\n", buf);
    
        it.write(string(RESPONSE)) and die("write");
    }

    return 0;
}

HTTPSの場合

#include <neo-c-net.h>

int main(int argc, char **argv) {
    httpsd_socket() {
        const char reply[] = "HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 11\r\n\r\nHello World";

        SSL_write(it, reply, strlen(reply));
    }
    return 0;
}

です。HTTPは8080番ポートのlocalhost。HTTPSはlocalhost:443です。
HTTPSの場合localhostの鍵を作らないとダメです。ChatGPTに聞けば親切に教えてくれます。

ライブラリのソースは

#include "neo-c-net.h"

void socket_fd::bind(socket_fd self, struct sockaddr* address, size_t size)
{
    bind(self, address, size) or {
        close(self);
        perror("listen");
        exit(EXIT_FAILURE);
    }
}

void socket_fd::listen(socket_fd self, size_t len=3)
{
    listen(self, len) or {
        close(self);
        perror("listen");
        exit(EXIT_FAILURE);
    }
}

socket_fd socket_fd::accept(socket_fd self, struct sockaddr* address, socklen_t* addrlen)
{
    socket_fd new_socket = accept(self, (struct sockaddr *)&address, (socklen_t*)&addrlen);
    
    return new_socket;
}

int socket_fd::write(socket_fd self, string str)
{
    return write(self, str, str.length());
}

void httpd_socket(int port=8080, int socket_family=AF_INET, int socket_type=SOCK_STREAM, int protocol=0, void* parent, void (*block)(void* parent, socket_fd it))
{
    socket_fd self = socket(socket_family, socket_type, protocol) and die("socket failed");

    struct sockaddr_in address;
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(port);
    
    int addrlen = sizeof(address);
    
    self.bind((struct sockaddr*)&address, sizeof(address));

    self.listen();

    socket_fd new_socket = self.accept((struct sockaddr *)&address, (socklen_t*)&addrlen);
   
    block(parent, new_socket);
    
    close(new_socket);
    close(self);
}

void ERR_print_errors_fp(FILE* f)
{
/*
unsigned long err = ERR_get_error();  // エラーコードを取得
char err_str[256];  // エラーメッセージを格納するためのバッファ
ERR_error_string_n(err, err_str, sizeof(err_str));  // エラーメッセージを文字列に変換
fprintf(f, "OpenSSL Error: %s\n", err_str);  // エラーメッセージを標準エラーに出力
*/
}


int httpsd_socket(int port=443, void* parent, void (*block)(void* parent, SSL* it))
{
    int sock;
    SSL_CTX *ctx;

    SSL_load_error_strings();   
    OpenSSL_add_ssl_algorithms();
    
    const SSL_METHOD *method;

    method = SSLv23_server_method();

    ctx = SSL_CTX_new(method);
    if (!ctx) {
        perror("Unable to create SSL context");
        ERR_print_errors_fp(stdout);
        exit(EXIT_FAILURE);
    }

    if (SSL_CTX_use_certificate_file(ctx, "cert.pem", SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stdout);
        exit(EXIT_FAILURE);
    }

    if (SSL_CTX_use_PrivateKey_file(ctx, "key.pem", SSL_FILETYPE_PEM) <= 0 ) {
        ERR_print_errors_fp(stdout);
        exit(EXIT_FAILURE);
    }

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock < 0) {
        perror("Unable to create socket");
        exit(EXIT_FAILURE);
    }

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = INADDR_ANY;

    if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("Unable to bind");
        exit(EXIT_FAILURE);
    }

    if (listen(sock, 1) < 0) {
        perror("Unable to listen");
        exit(EXIT_FAILURE);
    }

    while (1) {
        struct sockaddr_in addr;
        uint len = sizeof(addr);
        int client = accept(sock, (struct sockaddr*)&addr, &len);

        if (client < 0) {
            perror("Unable to accept");
            exit(EXIT_FAILURE);
        }

        SSL *ssl = SSL_new(ctx);
        SSL_set_fd(ssl, client);

        if (SSL_accept(ssl) <= 0) {
            ERR_print_errors_fp(stdout);
        } else {
            block(parent, ssl);
        }

        SSL_shutdown(ssl);
        SSL_free(ssl);
        close(client);
    }

    close(sock);
    SSL_CTX_free(ctx);
    EVP_cleanup();
    return 0;
}

です。CGIもすぐ作れると思います。試しにwebのviクローンを作っています。頓挫しそうですが。以下がソース。

#include <neo-c-net.h>

int main(int argc, char **argv) {
    httpsd_socket() {
        string code = """
        <!DOCTYPE html>
        <html lang="ja">
        <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>文字列配列の表示</title>
        </style>
        </head>
        <body>
                    
        <div id="output"></div>
                    
        <script>
        // 表示する文字列の配列
        let gTexts = ["ABC", "DEF", "GHI"];
        let gX = 0;
        let gY = 0;
        
        const kMode = {
            kInertMode:0,
            kEditMode:1
        };
        
        let gMode = 1;
                            
        function view() {
            // 出力先のdiv要素を取得
            let outputDiv = document.getElementById('output');
            
            document.getElementById('output').textContent = "";
                                        
            // 配列の各要素を1行ずつ表示
            let x = 0;
            let y = 0;
            gTexts.forEach(function(str) {
                if(y == gY) {
                    const line = document.createElement('p');
                    
                    if(gX > 0) {
                        const text = document.createTextNode(str.slice(0,gX));
                        line.appendChild(text);
                    }
                    
                    const flipped = document.createElement('span');
                    flipped.textContent = str[gX];
                    flipped.style.display = 'inline-block';
                    flipped.style.transform = 'rotate(180deg)';
                    line.appendChild(flipped);
                    
                    const text2 = document.createTextNode(str.slice(gX+1));
                    line.appendChild(text2);
                    
                    outputDiv.appendChild(line);
                }
                else {
                    const p = document.createElement('p');
                    p.textContent = str;
                    outputDiv.appendChild(p);
                }
                y++;
            });
        }
        
        let key = null;
        let code = null;
        
        function insertModeKeyDown(key, code) {
            let line = gTexts[gY];
            gTexts[gY] = gTexts[gY].slice(0,gX) + key + gTexts[gY].slice(gX, -1);
            gX++;
        }
        
        function editModeKeyDown(key, code) {
            switch(key) {
                case 'h':
                    gX--
                    break;
                    
                case 'l':
                    gX++;
                    break;
                    
                case 'j':
                    gY++;
                    break;
                    
                case 'k':
                    gY--;
                    break;
            }
        }
    
        document.addEventListener('keydown', function(event) {
            key = event.key;
            code = event.code;
            
            switch(gMode) {
                case kMode.kInsert:
                    insertModeKeyDown(key, code);
                    break;
                    
                case kMode.kEditMode:
                    editModeKeyDown(key, code);
                    break;
            }
        });
                    
        // サンプルのために5秒後にキーコードをチェック
        setInterval(view, 10);

        </script>
                                                                        
        </body>
        </html>
        """;
        
        char*% reply = xsprintf("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: %lu\r\n\r\n%s", strlen(code), code);

        SSL_write(it, reply, strlen(reply));
    }
    return 0;
}