UWSCでWinsockを使う
stuncloudさんがUWSCでもWinsock簡単だよー、と言ってたので、挑戦してみた。
UWSCでIRCクライアント | たっぷす庵
まあまあなモジュールにできた。
現在同期版のため、使用の際には注意が必要。
追記 20131110
nonblocking版を作成したので、このスクリプトは不要かな。
UWSCでnonblocking版Winsockを使う - じゅんじゅんのきまぐれ
スクリプト
Winsock.uws
OPTION EXPLICIT IFB GET_UWSC_NAME = "Winsock.uws" THEN Winsock.Startup() DIM s = Winsock.Create(), res = NULL IFB s <> Winsock.SOCKET_ERROR THEN IFB MSGBOX("server?", BTN_YES OR BTN_NO) = BTN_YES THEN DIM cs = Winsock.SOCKET_ERROR IF Winsock.BindS(s, 12345) = 0 AND Winsock.ListenS(s) = 0 THEN cs = Winsock.AcceptS(s) WHILE cs <> Winsock.SOCKET_ERROR res = Winsock.RecvS(cs) IF res <> Winsock.SOCKET_ERROR THEN res = Winsock.SendS(cs, res) IFB res = Winsock.SOCKET_ERROR THEN Winsock.Close(cs) cs = Winsock.SOCKET_ERROR IF MSGBOX("Listen?", BTN_YES OR BTN_NO) = BTN_YES THEN cs = Winsock.AcceptS(s) ENDIF WEND ELSE IFB Winsock.ConnectS(s, 12345, "127.0.0.1") = Winsock.SOCKET_ERROR THEN MSGBOX("connect fail.") res = EMPTY ENDIF WHILE res <> EMPTY res = INPUT("?") IFB LENGTH(res) THEN res = Winsock.SendS(s, res) IF res = Winsock.SOCKET_ERROR THEN res = EMPTY ELSE PRINT "recv:" + Winsock.RecvS(s) ENDIF WEND ENDIF Winsock.Close(s) ENDIF Winsock.Cleanup() ENDIF MODULE Winsock CONST AF_INET = 2 //* internetwork: UDP, TCP, etc. */ CONST SOCK_STREAM = 1 //* stream socket */ CONST SOCK_DGRAM = 2 //* datagram socket */ CONST SOMAXCONN = 128 CONST INADDR_ANY = 0 CONST IPPROTO_TCP = 6 CONST SOCKADDR_LENGTH = 16 CONST SOCKET_ERROR = $FFFFFFFF DEF_DLL socket(int, int, int): DWORD: ws2_32 DEF_DLL closesocket(DWORD): int: ws2_32 DEF_DLL bind(DWORD, DWORD[], int): int: ws2_32 DEF_DLL gethostbyname(string): DWORD: ws2_32 DEF_DLL listen(DWORD, int): int: ws2_32 DEF_DLL accept(DWORD, DWORD[], var int): DWORD: ws2_32 DEF_DLL connect(DWORD, DWORD[], long): DWORD: ws2_32 DEF_DLL recv(DWORD, var string, long, long): long: ws2_32 DEF_DLL send(DWORD, string, long, long): long: ws2_32 //DEF_DLL htonl(DWORD): DWORD: ws2_32 //DEF_DLL ntohl(DWORD): DWORD: ws2_32 DEF_DLL htons(WORD): WORD: ws2_32 DEF_DLL ntohs(WORD): WORD: ws2_32 //DEF_DLL inet_addr(string): DWORD: ws2_32 DEF_DLL inet_ntoa(DWORD): string: ws2_32 //DEF_DLL RtlMoveMemory(var string, DWORD, DWORD): kernel32 //DEF_DLL RtlFillMemory(DWORD, DWORD, BYTE): kernel32 FUNCTION Startup(verReq=$0202) DEF_DLL WSAStartup(WORD,var WORD[]): int: ws2_32 DIM WSAData[2+(256+128+2)/2+4] RESULT = WSAStartup(verReq, WSAData) FEND FUNCTION Cleanup() DEF_DLL WSACleanup(): int: ws2_32 RESULT = WSACleanup() FEND FUNCTION Create(type=SOCK_STREAM) RESULT = socket(AF_INET, type, 0) FEND FUNCTION Close(s) RESULT = closesocket(s) FEND FUNCTION GetAddrByName(name) DIM ptr = gethostbyname(name), hostent[3], addr[0] DEF_DLL RtlMoveMemory(var DWORD[], DWORD, DWORD): kernel32 IF ptr THEN RtlMoveMemory(hostent, ptr, LENGTH(hostent) * 4) RESULT = 0 IFB INT(hostent[2] / $10000) > 0 THEN RtlMoveMemory(addr, hostent[3], 4) RtlMoveMemory(addr, addr[0], 4) RESULT = addr[0] ENDIF FEND FUNCTION ConvAddr(addr, port) RESULT = SAFEARRAY(0, 3) RESULT[0] = AF_INET + htons(port) * $10000 IF VARTYPE(addr) = VAR_BSTR THEN addr = GetAddrByName(addr) RESULT[1] = addr FEND FUNCTION BindS(s, port, addr=INADDR_ANY) RESULT = bind(s, ConvAddr(addr, port), SOCKADDR_LENGTH) FEND FUNCTION ListenS(s, backlog=SOMAXCONN) RESULT = listen(s, backlog) FEND FUNCTION AcceptS(s) DIM port, addr RESULT = AcceptSEx(s, port, addr) FEND FUNCTION AcceptSEx(s, var port, var addr) DIM tar = ConvAddr(0, 0) DIM len = SOCKADDR_LENGTH RESULT = accept(s, tar, len) IF len >= 4 THEN port = ntohs(INT(tar[0] / $10000)) IF len >= 8 THEN addr = inet_ntoa(tar[1]) FEND FUNCTION ConnectS(s, port, addr) RESULT = connect(s, ConvAddr(addr, port), SOCKADDR_LENGTH) FEND FUNCTION SendS(s, msg) RESULT = send(s, msg, LENGTHB(msg), 0) FEND FUNCTION RecvS(s, size=1024) DIM buf = FORMAT(CHR(0), size) RESULT = recv(s, buf, size, 0) IF RESULT <> SOCKET_ERROR THEN RESULT = COPYB(buf, 1, RESULT) FEND ENDMODULE
使い方
サンプルコードを見てください。
基本的には、2つのUWSCプロセスで通信します。
スクリプトを起動すると、「server?」と聞いてきます。
注意!サーバーは、不完全です。
不完全なので、UWSC本体を起動してから実行してください。
そうしないと、止める手段がプロセスキルしかなくなります。
次にクライアントを実行。(「server?」でいいえ)
クライアント側にINPUTが表示されるので、何か入力してOK。
すると、サーバーからの折り返しを受信してPRINTします。
そのループ。
やめるときは、INPUTにCANCELしてください。
クライアントが終了しても、サーバーが受信待ちで返ってきません。
「STOP」して止めてください。
なお、「STOP」するとListenポートが開きっぱなしになるので、再度実行はできません。
UWSCを終了してください。
備考
同期版は、かなりの行動が同期します。
このため、随所でブロックして、UWSCが応答なしに落ちる罠が多いです。
注意すれば、なんとか使えるレベルとなっています。
本格的にやるなら、やはり非同期版が必須。