Squeak でソケット通信

sumim さんのSqueak Smalltalk 向けの分散処理関連で遊ぶ - Smalltalkのtは小文字ですに刺激を受けて、以前pythonでsocket入門: 思い立ったが吉日生活 さんと同じ事を Squeak でやってみる、と言うのを 試した時のメモを引っ張りだしてみました。

ソケット通信開始

Python が IDLE なので、Squeak は Workspace で お手軽ファイト!です。 (初めて Workspace 変数のありがたみが解りましたよ)

"Server-side (Smalltalk)"

sock _ Socket newTCP.   "TCPソケットの作成"
sock listenOn: 7777.     "ポート7777 で接続待ち"
sock waitForConnectionFor:
      Socket standardDeadline.  "クライアントのアクセス待ち"

#waitForConnectionFor: で アクセス待ちになります。 引数に取るのはタイムアウト時間で、通常は Socket >> #standerdDeadline を使えばOKです。

ちなみにPython版はコレ。ほとんど1対1 の対応になってます(当たり前だけど)。 accept のあたりが違いますが、そこは後述します。

#Server-side (Python)

import socket

sock = socket.socket( socket.AF_INET,
                      socket.SOCK_STREAM ) # TCPソケットの作成
sock.bind(('',7777))            # ポート7777で、
sock.listen(1)                  # 接続待ち
conn, addr = sock.accept()      # クライアントのアクセス待ち

さて、今度はクライアントサイド。

"Client-side(Smalltalk)"

sock _ Socket newTCP.           "TCPソケットの作成"
sock connectToHostNamed: 
     'localhost' port: 7777.    "localhost:7777 に接続"

これも Python版とほぼ一対一になります。

#Client-side (Python)

import socket

sock = socket.socket( socket.AF_INET,
                      socket.SOCK_STREAM ) # TCPソケットの作成
sock.connect(('localhost',7777))     # localhost:7777 に接続

Client→Server通信

"Client-side(Smalltalk)"

sock sendData: 'Hello! I am Squeak Socket client'. 
#Client-side (Python)

sock.send( 'Hello! I am Python Socket client' )

戻り値は送信バイト数になります。 今度はこれを、サーバー側で取得してみましょう。

"Server-side(Smalltalk)"

Transcript cr; show: sock receiveData 

Server-side (Python)

print conn.recv(1024)

Transcript に以下のように出力されます。

Hello! I am Squeak Socket client

Server→Client通信

今度はサーバーから通信してみます。

"Server-side(Smalltalk)"

sock sendData: 'Welcome to Squeak socket Server!'

例によって、戻り値として送信バイト数を返します。 コレをクライアント側から取ると

"Crient-side(Smalltalk)"

Transcript cr; show: sock receiveData 

Socket通信の終了

サーバーもクライアントも、

sock close

するだけです。

Squeak 版の欠点(?)

2007.08.24 訂正。
以下は嘘です。単に使い方が悪いだけでした...orz
見ての通り、 Squeak版では サーバ側でクライアントコネクション待ちしたときに、 Python 版とは異なり connection オブジェクトを返していません。 (Python 版だと connection オブジェクトを介して send() や recv() を行っている)

つまり、このやり方だと複数クライアントと会話できない、一度に一人しかアクセスできません。

実は Squeak の Socket にもちゃんと #accept メソッドがあります。 新しいコネクションの Socket オブジェクトを返してくれるハズなのです。

接続待ちでは、#waitForAcceptFor: なんていう いかにもお誂え向けのメソッドがいて、 これは結局中で #waitForConnectionFor: をしたあと、#accept で新しいソケット(子ソケット)を返してくれる ・・・という風な動きをします。

しかし、コレを使って

sock _ Socket newTCP.
sock listenOn: 7878.
con _ sock waitForAcceptFor: Socket standardDeadline.


のようなコードを作っても、 コネクション確立した後に呼び出される #accept から辿りたどった Socket >> #primiAcceptFrom:receiveBufferSize:sendBufSize:semaIndex: 内の のプリミティブコードが 上手く動かず 例外が発生します。

全然追っかけてないし、私のソケットの理解は甘いので ただ「動かない」って言ってみただけなのだけれど、 Squeakでネットワーク(サーバサイド) さんも accept を使わないで「欠点は一度に一人じか接続できない」て解説してるのをみると、 なんかあるのかな?と思わなくもないです。