社内にIRCサーバーが出来まして
利用条件は一つ、参加者が自分でクライアントを作ること!
今のところ6人くらいですがみんな面白いもの作ってる
僕は当然UWSCで作る
createforemでUIを作って、通信はwinsock使いました
しかし、createforemとthreadが仲悪いのでそのへん面倒だったり
まだ改良中ということもあって記事にはコード載せないけど、代わりにPINGPONGサンプルを載せときます
IRCってサーバーが定期的にPINGコマンドを飛ばしてくるので、すかさずPONGを返さないとログアウトさせられちゃうんですね
dim serverIP = "xxx.xxx.xxx.xxx" dim port = 6667 dim WSADATA[198] dim sin_zero[7] SETCLEAR(sin_zero, 0) dim s = SOCKET_ERROR logprint(TRUE, 100, 100, 800, 600) try print "初期化" if WSAStartup($202, WSADATA) then msgbox("WSAStartup失敗") exit endif print "ソケット作成" s = socket(AF_INET, SOCK_STREAM, 0) if s = SOCKET_ERROR then msgbox("ソケット作れませんでした") exit endif print "サーバーに接続" if connect(s, AF_INET, htons(port), inet_addr(serverIP), sin_zero, SOCKADDR_LENGTH) = SOCKET_ERROR then msgbox("接続に失敗しました") exit endif print "ニックネーム送信" msg = "NICK stuncloud" msg = msg + chr(10) if send(s, msg, lengthb(msg), 0) = SOCKET_ERROR then msgbox("送信失敗") exit endif print "ユーザー名送信" msg = "USER Joey hostname servername :Joey Takahashi" msg = msg + chr(10) if send(s, msg, lengthb(msg), 0) = SOCKET_ERROR then msgbox("送信失敗") exit endif print "受信待機" while TRUE recvmsg = format(chr(0), 1024) if recv(s, recvmsg, lengthb(recvmsg), 0) = SOCKET_ERROR then msgbox("受信失敗") exit endif print recvmsg if pos("PING", recvmsg) = 1 then print "PINGが来たのでPONGを返す" msg = replace(recvmsg, "PING", "PONG") msg = msg + chr(10) if send(s, msg, lengthb(msg), 0) = SOCKET_ERROR then msgbox("送信失敗") exit else msgbox("PONGを送信しました") endif break endif wend finally if s <> SOCKET_ERROR then closesocket(s) WSACleanup() endtry const AF_INET = 2 const SOCK_STREAM = 1 const SOMAXCONN = 128 const INADDR_ANY = $0 const SOCKET_ERROR = $FFFFFFFF const IPPROTO_TCP = 6 const SOCKADDR_LENGTH = 16 def_dll socket(Long, Long, Long):dword:ws2_32.dll def_dll closesocket(dword):dword:ws2_32.dll def_dll connect(dword, {word, word, dword, byte[]}, long):dword:ws2_32.dll def_dll recv(dword, var string, Long, Long):Long:ws2_32.dll def_dll send(Long, string, Long, Long): Long:ws2_32.dll def_dll htonl(dword):dword:ws2_32.dll def_dll ntohl(dword):dword:ws2_32.dll def_dll htons(word):word:ws2_32.dll def_dll ntohs(word):word:ws2_32.dll def_dll inet_addr(String):dword:ws2_32.dll def_dll inet_ntoa(dword):String:ws2_32.dll def_dll WSAStartup(word, var word[]):Long:ws2_32.dll def_dll WSACleanup():Long:ws2_32.dll
createformとthreadで困った話
recvがスクリプトを止めちゃうので、threadで実行したいんですよ
で、メインスレッドでcreateformするとrecvスレッドからフォームにアクセス出来ないんですよ
しょうがないのでフォームもrecvスレッドで作ると一見うまくいくんですけどこんどはoleeventが出来ないんですよ
oleevent呼ぶのははメインスレッドじゃないとダメなようです
なので結局recvだけ別スレッドにして、受信したメッセージを共用変数に入れることでどうにかしました
変数になんか入ったらメイン側で処理、変数を空にしたらrecv再開みたいな
ただ、なんか危なっかしいのでもちょっとどうにかしたいなーと思ったてたところでしゅんさんがタイムリーな記事を書いてださいました
これ参考にちょっとキューを実装してみようと思います
あとね、winsockそんなに難しくないのでUWSCでもわりと気楽に使えます
これ使えば複数のPCでやりとりするようなものも書けますし、覚えておくと便利よ
WSAStartupの第二引数は、199個のWORD配列へのポインターで良いと思いますよー。
スクリプトだと一見足りてなさそうですが、VirtualAllocの最低確保サイズがページ(4k)単位なので問題ないですが。
sin_zeroは多分不要ですが、そこにメモリーが必要なはずなので、今のままではいけない気がします、、、。
未確認ですみませんが。
とはいえ、Winsockサンプル、ありがとうございます!
>199個のWORD配列へのポインター
なおしました
szDescriptionとszSystemStatusがポインタだと思ってて2+2+4+4+2+2+4で20バイトにしてました
sin_zeroもなんかそんな感じでやってました
とりあえずこっちもbyte配列にしました
>sin_zeroは多分不要
sockaddrを{word, word, dword}と展開してみたらなんだか普通に動きますね
そもそもいらなかったのか…
sockaddr_inとして使う場合は後ろが余るからスペーサとして入ってるような認識ではあったんですけど、なんだかよくわかりません、sin_zero
素早いですね。
せっかくなのでWinsockモジュールを作ろうと苦闘中ですが、いらない子に思えるsin_zero、acceptの際には何か関係しているのかもしれません。
なしだと動かない様子、、、。(確定ではありませんが)
結局、DWORD4個の配列でAF_INETとポート番号を同居させる方向でやったら、上手くいきました。
しかし、同期版だと、異常系で応答なしに陥りやすいので、公開は考え中、、、。
非同期まで手を出すか、、、。