ALGOBIT

2011/05/06

TCPサーバ ruby と python

Filed under: 離散的な気まぐれ — タグ: , , , — Kohyama @ 11:01

自分用まとめです.
各言語の TCPサーバ雛形としてのエコーサーバ.
クライアントから来たメッセージが変数で見える最小コードシリーズ.

ruby 版.
ポートを変えてる以外は 本家のリファレンス そのままですが.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
require 'socket'
 
gs = TCPServer.open(1111)
addr = gs.addr
addr.shift
 
while true
    Thread.start(gs.accept) do |cs|
        while cs.gets
            cs.write($_)
        end
        cs.close
    end
end

python 版.
抽象化レイヤーは他にもあるようですが, 複数クライアントを相手にする, クライアントから送られて来たデータが変数で見える, 最小コードという趣旨だと SocketServer.ThreadingTCPServer が一番簡単なようです.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import SocketServer
 
class MyHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        while True:
            data = self.request.recv(1024)
            if len(data) == 0:
                self.request.close()
                return
            self.request.send(data)
 
if __name__ == "__main__":
    HOST, PORT = "localhost", 1111
    server = SocketServer.ThreadingTCPServer((HOST, PORT), MyHandler)
    server.serve_forever()

UNIX系なら, いつも通りそれぞれ「#!/usr/bin/env ruby」, 「#!/usr/bin/env python」を一行目に挿入して chmod +x で直接実行.

2011/04/18

Node.js なら TCP エコーサーバが5行!

Filed under: 離散的な気まぐれ — タグ: , , , — Kohyama @ 13:41

サーバサイドスクリプトをいろいろ試しているところです.

Gauche でエコーサーバ
に続き, Node.js 版 TCPエコーサーバものせておきます.
Node.js は JavaScript の処理系です.

1
2
3
4
5
var net = require('net');
var server = net.createServer(function (stream) {
    stream.on('data', function (data) { stream.write(data); });
    stream.on('end', function () { stream.end(); }); });
server.listen(1111, 'localhost');

5行!
なんということでしょう.

エコーサーバとしてだけならば,本家にもっと短い例が載ってますが, データを処理するサーバの雛形なので, 送られてきたデータがコード内で変数として見えている最小のコードとしました.

node.js はインストールされているとします.
上記のソースをファイルに保存したら, 第一引数に保存したファイルのパスを指定して, node (Windows なら node.exe) を実行します.

UNIX 系ならば, #!/usr/bin/env node という行を先頭に追加し, chmod +x してあげれば直接実行できます.

2011/04/07

scheme で TCP エコーサーバ

Filed under: 離散的な気まぐれ — タグ: , , , , — Kohyama @ 13:01

scheme で TCP のサーバを書く際の雛形としてエコーサーバとしてコードをまとめてみた.
ほとんど処理系依存, UNIX系OS + Gauche じゃないと動きません.
daemon のところは Gauche クックブック / デーモンプロセスを作成する をほぼそのまま使わせていただきました.
Gauche ユーザリファレンスC と Scheme の関数の対応 あたりを激しく参照しました.
chmod +x して実行すると, 制御端末を切り離し, 1111 番ポートの TCP 接続を待ちます.
telnet localhost 1111 などで接続すると, 改行の入力を待って, 改行までに入力されたテキストに改行を付けてそのまま返します.
止めるときは ps などで親の(一番若い) pid を調べて kill します.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#!/usr/bin/env gosh
(use gauche.net)
(set-signal-handler! SIGCHLD (lambda _
                               (guard (e ((<system-error> e)))
                                      (while (< 0 (sys-waitpid -1))))))
(set-signal-handler! SIGTERM (lambda _ (sys-kill 0 SIGTERM)))
 
(define (daemon)
  (when (positive? (sys-fork))
        (sys-exit 0))
  (sys-setsid)
  (sys-chdir "/")
  (sys-umask 0)
  (call-with-input-file "/dev/null"
                        (cut port-fd-dup! (standard-input-port) <> ))
  (call-with-output-file "/dev/null"
                         (lambda (out)
                           (port-fd-dup! (standard-output-port) out)
                           (port-fd-dup! (standard-error-port) out))))
 
(define (prog cs)
  (let ((iport (socket-input-port cs))
        (oport (socket-output-port cs)))
    (let loop ((line (read-line iport)))
      (cond
        ((eof-object? line) (socket-close cs))
        (else
          (display (string-append line "\r\n") oport)
          (loop (read-line iport)))))))
 
(define (main args)
  (daemon)
  (let1 s (make-server-socket 'inet 1111 :reuse-addr? #t)
        (let loop ((cs (socket-accept s)))
          (let ((pid (sys-fork)))
            (cond
              ((= pid 0)   ; child
               (set-signal-handler! SIGTERM #t) ; reset
               (prog cs)
               (sys-exit 0))
              (else        ; parent
                (socket-close cs)
                (loop (socket-accept s))))))))

以下はほぼ等価な C のプログラムです.
というか, 過去に C で書いた TCP サーバから最低限のコードをエコーサーバとして下のように整理し, これを Gauche に置き換えたのが上のプログラムです.
UNIX 系の OS でないと動きません.
syslog は省いてます.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include <sys/types.h>    /* read */
#include <sys/socket.h>   /* accept, bind, getsockname, listen, socket */
#include <sys/uio.h>  /* read */
#include <sys/wait.h> /* waitpid */
#include <arpa/inet.h>    /* htons */
#include <errno.h>        /* errno */
#include <fcntl.h>        /* open */
#include <signal.h>       /* kill, sigaction */
#include <stdio.h>        /* BUFSIZ */
#include <stdlib.h>       /* daemon, exit */
#include <unistd.h>       /* _exit, close, dup, fork, read, write */
 
static void
reapchild(int signo)
{
    while (waitpid(-1, NULL, WNOHANG) > 0)
        ;
}
 
static void
parent_exit(int signo)
{
    kill(0, SIGTERM);
    exit(0);
}
 
static int
prog(int s)
{
    int n;
    char buf[BUFSIZ];
 
    while (0 < (n = read(s, buf, BUFSIZ)))
        write(s, buf, n);
}
 
int
main(int argc, char *argv[])
{
    int addrlen, cs, pid, s;
    struct sockaddr_in addr;
    int port = 1111;
    struct sigaction sa;
 
    daemon(1, 1);
    chdir("/");
    close(0);
    close(1);
    close(2);
    open("/dev/null", O_RDWR);
    dup(0);
    dup(0);
 
    sa.sa_handler = reapchild;
    sigaction(SIGCHLD, &sa, NULL);
    sa.sa_handler = parent_exit;
    sigaction(SIGTERM, &sa, NULL);
 
    if ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
        exit(1);
 
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons((unsigned short)port);
    addrlen = sizeof(addr);
    if (bind(s, (struct sockaddr *)&addr, addrlen) < 0)
        exit(1);
 
    if (getsockname(s, (struct sockaddr *)&addr, &addrlen))
        exit(1);
 
    listen(s, 5);
    for (; ; ) {
        if ((cs = accept(s, (struct sockaddr *)&addr, &addrlen)) < 0) {
            if (errno == EINTR)
                continue;
            exit(1);
        }
        switch (fork()) {
        case -1:
            exit(1);
        case 0:
            sa.sa_handler = SIG_DFL;
            sigaction(SIGTERM, &sa, NULL);
            close(s);
            prog(cs);
            _exit(0);
        }
        close(cs);
    }
}

Kahuaプロジェクト
オライリージャパン
発売日:2008-03-14

デビッド・A. クリ
アスキー
発売日:1991-07

実際に役に立つプログラムは直接なり間接なり OS と対話しなければならない.
とくに UNIX オペレーティングシステムを記述するために生まれた C 言語は, より上位のプログラミング環境の下位で OS と直接対話する基盤である.
システムコールプログラミングへようこそ.

Copyright © 2010 Yoshinori Kohyama All Rights Reserved.