c以外は手軽に書けることは間違いないんだけど、ちょっとやってみますかということで。 サーバの処理としてはtcp port 9999で待ち受けて、クライアントからアクセスがあったら"hello, world"を返すだけのもの。 実装方法は完全に同じじゃなくて、例えばcだとaccept()のあとはfork(2)をしているし、rubyはthreadを使ったりという違いはありますが、その辺りは置いておいてどんなもんかというところを見てみたいと。
まずはc言語版。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> #include <arpa/inet.h> int main(int argc, char **argv) { int sock = socket(AF_INET, SOCK_STREAM, 0); if (sock < 0) { perror("socket"); exit(-1); } struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(9999), .sin_addr.s_addr = htonl(INADDR_ANY) }; if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("bind"); exit(-1); } if (listen(sock, 10) < 0) { perror("listen"); exit(-1); } while (1) { socklen_t len = sizeof(addr); int conn = accept(sock, (struct sockaddr *) &addr, &len); if (conn < 0) { perror("accept"); exit(-1); } pid_t pid = fork(); if (!pid) { close(sock); char *msg = "hello, world\n"; if (send(conn, msg, strlen(msg), 0) < 0) { perror("send"); _exit(-1); } close(conn); _exit(0); } else if (pid < 0) { perror("fork"); exit(-1); } close(conn); } return 0; }
次にgolang版。
package main import ( "net" "os" ) func main() { listener, err := net.Listen("tcp", "0.0.0.0:9999") if err != nil { panic("failed to listen") } for { conn, err := listener.Accept() if err != nil { panic("Accept() failed") os.Exit(-1) } go handleHello(conn) } } func handleHello(conn net.Conn) { defer conn.Close() msg := "hello, world\n" conn.Write([]byte (msg)) }
次がruby版。
#!/usr/bin/env ruby require 'socket' if __FILE__ == $0 server = TCPServer.open("0.0.0.0", 9999) while true Thread.start(server.accept) do |conn| conn.write("hello, world\n") conn.close end end end
最後にpython版。
#!/usr/bin/env python import socketserver import codecs class HelloServer(socketserver.BaseRequestHandler): def handle(self): self.request.send(codecs.encode("hello, world\n")) self.request.close() if __name__ == "__main__": server = socketserver.TCPServer(("0.0.0.0", 9999), HelloServer) server.serve_forever()
cは言わずもがなで、socket(2)でsocketを作ってbind(2)でアドレスの割当して、listen(2)で接続を待ってaccept(2)で接続要求を受け付けてってことをする必要がありますね。 あと、ネットワークバイトオーダーも書き手が気にしないといけないというところとか。
では、golangはというと、net.Listen()がcのsocket(2)、bind(2)、listen(2)も行ってくれるような感じ。で、あとはAccept()で接続要求を受け付ける。 rubyはTCPServerクラスを使ってまして、これのopen()がgolangのListen()に近い感じですかね。あとはこのクラスのaccept()で接続要求を受け付けて〜という流れ。 最後のpythonはというと、TCPServerのコンストラクタでListen()に近いことをして、serve_forever()でaccept()ということかな。
cは色々めんどいのでユーザーランドのコードを書くには使いたくないですが、golang、ruby、pythonはどれを選んでも良さ気。golangはdeferがあるからsocketのクローズ漏れが抑えられそうか。異常終了とかの時とか。 (´-`).oO(個人的にはgolangはcっぽさがあって好きというのはあるんだけど。あとDial()みたいなPlan9っぽさとか。
ハロー "Hello, World" OSと標準ライブラリのシゴトとしくみ
- 作者: 坂井弘亮
- 出版社/メーカー: 秀和システム
- 発売日: 2015/09/12
- メディア: 単行本
- この商品を含むブログ (1件) を見る