Open1
neo-cでソケット通信
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;
}