HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2007
0322
AKIUDP送受信?16解決


AKI

リンク

2007/3/22(Thu) 19:13:26|NO.6493

こんにちは
ネット系ソフトを作っています。
前はTCPで作ったのですがデータの送受信が遅くリアルタイム性がないので
UDPでデータ送受信するように考えていますが
UDPクライアントからUDPサーバにデータは送れてるのですが
サーバからクライアントにデータが送れません
どうすればいいですか?
udpsendto "IPアドレス",ポート番号ってありますが
クライアントの方はサーバのIPとポート番号を設定していますが
サーバの方はクライアントのIPとポートを設定するのですか?
一応UDPサーバはクライアントから送られてきたデータを現在繋がっている
クライアントに送るのみです


#include "pcbnet2.as" #include "hspda.as" ; ; UDP通信サンプル(サーバー) ; dim soc,16 dim soc2,16 port = 12346 ;// ポート12346で待機 title "UDP サーバーテスト" udpsock soc,port if stat : dialog "ソケットの初期化に失敗しました。",1 : end ;udpsendto "ipアドレス",port ←たぶんここに入れるんだとおもいます onexit *bye sdim buf,100,15 buf=str(buf) msg=str(msg) *main udpcheck a,soc if a : gosub *recv wait 1 goto *main *recv cls udpget buf,64,soc ;データ受信 sdim getd,128,4 getd=str(getd) csvsel buf csvstr getd,buf dd=int(getd.0);どのクライアントからのデータか確認 pos 0,20*dd mes buf udpput buf,soc ;接続されているクライアント分送らないとダメですよね return *bye udpclose soc end
tcpと同じく繋がっているクライアントリストを製作しないとダメですか?

PS:
udpマルチのスクリは作ったのですが今ひとつD範囲と言うのがわからないので
C/S形式にしました



この記事に返信する


アキス

リンク

2007/3/22(Thu) 19:22:11|NO.6495

UDPマルチのスクリについてですが、
Hsファイルには

マルチキャストアドレスとは,IPアドレスのうちDクラスの範囲(224.0.0.0〜 239.255.255.255)のアドレスです. ただし,特別な用途の通信でない場合は,なるべく239.255.0.0〜239.255.255.255を
この中から見ると、
例として、"239.255.0.0"と"239.255.255.255"があります。
このどちらかを使えば
マルチのスクリは出来るはずです。
そっちの方がいくらから楽と思います。(少なくとも僕の場合は。)



93

リンク

2007/3/22(Thu) 19:33:27|NO.6496

>>AKIさん
UDPの概念にサーバー・クライアントという分け方はありません。
TCPを使った事ある人は混乱すると思いますが;

考え方としてはTCPでセッションを引いて、接続リストを作成してUDPで送受信です。
(もちろん、UDPは信頼性が低いので重要な送受信は抜く)

今、ご飯食べてるので後ほどサンプルスクリプト載せます;



93

リンク

2007/3/22(Thu) 21:39:31|NO.6500

まず、サーバーの方を



#include "common.as" #include "pcbnet2.as" title "Server - TCP&UDP" cls 4 /*変数初期化*/ // サーバー用変数 dim _LISTEN_, 1 // ソケット構造(数値) // 配列に情報をいれていく // 0 = フラグ // 1 = TCPソケットID // 2 = TCPポート(相手の) // 3 = 待機フラグ // 4 = UDPソケットID // 5 = UDPポート(自分の) // 6 = UDPポート(相手の) dim _SOCK_, maxUSER, 6 // ソケット構造(文字列) // 0 = IP sdim _SOCKs_, 256, maxUSER /*まず、セッション(TCP)用を開きます。*/ tcpmake _LISTEN_, PORT if (stat != 0) { end } *MAIN /*新着セッションと接続状況取得*/ gosub *CHECK gosub *RECV gosub *EXQ ;gosub *SEND wait 1 goto *MAIN *CHECK tcpwait _LISTEN_ if (stat != 0) { /*セッション要求があったら*/ tcpaccept DUMMY, _LISTEN_ : i = -1 repeat maxUSER if (_SOCK_(cnt) == 0) { /*iに空いている番号を教える*/ : i = cnt break } loop if (i == -1) { /*もしiが-1のままなら*/ tcpshut DUMMY tcpclose DUMMY } else { /*空いていたら*/ : _SOCK_(i, 0) = 1 : _SOCK_(i, 1) = DUMMY /*IP:PORTを取得*/ sdim pINFO, 256 tcpinfo pINFO, _SOCK_(i, 1) /*IP*/ getstr _SOCKs_(i), pINFO, 0, ':' /*PORT*/ getstr pINFO, pINFO, strsize, ':' : _SOCK_(i, 2) = int(pINFO) : _SOCK_(i, 3) = 0 /*UDPソケットの生成*/ udpsock _SOCK_(i, 4), 0 if (stat != 0) { end } /*自分のポート取得*/ // のちにサーバーに知らせる用 // udpport _SOCK_(i, 5), _SOCK_(i, 4) /*PIDの送信*/ tcpput ""+(_REQ_+_PID_)+"3\n"+(_GET_+_SOC_)+"\n", _SOCK_(i, 1) } } repeat maxUSER : n = cnt /*接続フラグチェック*/ if (_SOCK_(n, 0) == 1) { /*維持なら*/ tcpfail _SOCK_(n, 1) if (stat != 0) { /*切断なら*/ tcpshut _SOCK_(n, 1) tcpclose _SOCK_(n, 1) udpclose _SOCK_(n, 4) /*空いていたら*/ : _SOCK_(n, 0) = 0 } } loop return *RECV repeat maxUSER : n = cnt /*受信チェック*/ if (_SOCK_(n, 0) == 1) { /*変数初期化*/ : _SOCK_(n, 3) = 0 // 0 = 受信 // 1 = 送信 sdim _BUF_, 1024, maxUSER, 2 /*受信状況の取得*/ tcpcount pSIZE, _SOCK_(n, 1) if (pSIZE > 0) { tcpget _BUF_(n, 0), pSIZE+1, _SOCK_(n, 1) /*受信したらフラグを1へ*/ : _SOCK_(n, 3) = 1 } } loop return *EXQ repeat maxUSER : n = cnt /*受信していたら*/ if (_SOCK_(n, 3) == 1) { notesel _BUF_(n, 0) repeat noteinfo(0) : i = cnt /*文字列取得*/ noteget a, i : pCMD = strmid(a, 0, 3) : pTEXT = strmid(a, 3, strlen(a)) switch int(pCMD) // // UDPソケットの設定 case (_SET_+_SOC_) /*udpソケットのポートを受け取る*/ : _SOCK_(n, 6) = int(pTEXT) udpsendto _SOCK_(n, 4), _SOCKs_(i), _SOCK_(n, 6) udpput "OK 受信されました", _SOCK_(n, 4) swbreak // // それ以外 default swbreak swend loop noteunsel } loop return

やっていることは TCP で確実なセッションを引いています。
そして、クライアントにUDPのポート番号を聞いています。
IPはTCPinfoから取得しているので分かっています。

そして、UDPで返信しています。



93

リンク

2007/3/22(Thu) 21:41:49|NO.6501

次にクライアントです。


#include "common.as" #include "pcbnet2.as" title "Client - TCP&UDP" cls 4 /*変数初期化*/ // クライアント用変数 // 0 = ソケットID // 1 = ポート // 2 = 待機フラグ dim _TCP_, 3 dim _UDP_, 3 /*まず、セッション(TCP)用を開きます。*/ tcpopen _TCP_(0), IP, PORT if (stat != 0) { end } udpsock _UDP_(0), 0 if (stat != 0) { end } /*自分のポート取得*/ // のちにサーバーに知らせる用 // udpport _UDP_(1), _UDP_(0) *MAIN gosub *CHECK gosub *RECV gosub *EXQ gosub *SEND wait 1 goto *MAIN *CHECK /*接続状態取得*/ tcpfail _TCP_(0) if (stat != 0) { /*切断なら*/ tcpshut _TCP_(0) tcpclose _TCP_(0) end } return *RECV /*変数初期化*/ : _TCP_(2) = 0 // 0 = 受信 // 1 = 送信 sdim _BUF_, 1024, 2 /*受信状況の取得*/ tcpcount pSIZE, _TCP_(0) if (pSIZE > 0) { tcpget _BUF_(0), pSIZE+1, _TCP_(0) /*受信したらフラグを1へ*/ : _TCP_(2) = 1 } /*追加*/ sdim _PAK_, 1024 udpcheck pSIZE, _UDP_(0) if (pSIZE > 0) { udpcount pSIZE, _UDP_(0) udpget _PAK_, pSIZE+1, _UDP_(0) dialog ""+_PAK_ } return *EXQ /*受信していたら*/ if (_TCP_(2) == 1) { notesel _BUF_(0) repeat noteinfo(0) : i = cnt /*文字列取得*/ noteget a, i : pCMD = strmid(a, 0, 3) : pTEXT = strmid(a, 3, strlen(a)) switch int(pCMD) // // PIDの応答 case (_REQ_+_PID_) : pid = int(pTEXT) swbreak // // UDPソケットの取得 case (_GET_+_SOC_) : _BUF_(1) = ""+(_SET_+_SOC_)+""+_UDP_(1)+"\n"+_BUF_(1) : _TCP_(2) = 2 swbreak // // それ以外 default swbreak swend loop noteunsel } return *SEND if (_TCP_(2) == 2) { tcpput _BUF_(1), _TCP_(0) } return

TCPでサーバーとのセッションを引き、PIDを取得してUDPポートを返します。
あとはUDPを使って取得・送信すればおけです。
少々複雑ですが、少しずつ分解してみてください。



93

リンク

2007/3/22(Thu) 21:51:48|NO.6502

で、最後に共通設定のヘッダーです。


/*共通仕様の宣言*/ #define global IP "127.0.0.1" #define global PORT 3301 #define global maxUSER 8 #define global _REQ_ 200 #define global _SET_ 400 #define global _GET_ 600 #define global _PID_ 001 #define global _SOC_ 002


急ぎだったので、サーバー側に送信処理がなかったりしてます。
あとはクライアントにUDPの送信処理ないです。

恐らく、聞いているのは「クライアントのポートをサーバー教えるには?」だと思います。
これは udpport を使って 「クライアント側で取得してTCPでサーバーに送信する」が解決策です。

マルチキャストも有効ですが、できるだけLAN内だけにした方がいいです。

そこはTCPを参考に出来ると思うのでがんばってみてください。



AKI

リンク

2007/3/23(Fri) 08:34:17|NO.6504

おはようございます
アキスさん
マルチで作った場合にソフトを配布するときにクライアント側での設定がわからないのでパスしました。

93さん
理解するのに少々時間かかります

一応、現在はTCPサーバとUDPサーバがありクライアント接続があるとTCPサーバで
リストに追加してその番号をクライアントに返してからUDPサーバにデータを送っています
info命令で所得した"215.94.144.25:8080"をIPとportに分けるのはどうすれば良いですか?
csvstrは","区切りでの分割なので分け方がわかりません



93

リンク

2007/3/23(Fri) 12:02:42|NO.6508

> AKIさん
少々、複雑すぎました;
IP:PORTの分け方はサーバーのスクリプトに書いてありますが


sdim pINFO, 256 tcpinfo pINFO, SOCK /*IP*/ getstr IP, pINFO, 0, ':' /*PORT*/ getstr PORT, pINFO, strsize, ':'

です。



AKI

リンク

2007/3/23(Fri) 12:48:10|NO.6512

こんにちは93さん
とりあえず
<pre)
     udpsock soc,port
if stat : dialog "ソケットの初期化に失敗しました。",1 : end

udpget buf,64,soc ;データ受信
sdim getd,128,4
getd=str(getd)
csvsel buf
csvstr getd,buf
dd=int(getd.0);どのクライアントからのデータか確認

sdim pINFO, 256
udpinfo pINFO,soc
/*IP*/
getstr ip, pINFO, 0, ':'
/*PORT*/
getstr po, pINFO, strsize, ':'

という風にしています
クライアントからのデータbuf(先にTCPサーバで割り振った番号,aデータ,bデータ)
繋がっているクライアントにすべて送るには
ipとportを配列変数に入れて送るのですか?
udpsendto soc.cnt,ip.cnt,port.cnt
udpput buf,soc.cnt
と言う風にすれば良いですか?



93

リンク

2007/3/23(Fri) 14:48:31|NO.6514

>>AKIさん

送信する方法は udpsendto soc.cnt,ip.cnt,port.cnt : udpput buf,soc.cnt であってます。

データbufはサーバーで加工したバッファでしょうか?
それとも、クライアントからのデータでしょうか?

もし後者なら、サーバー側でパケット構築し直してから送信し直す必要があります。

UDPは順序補償がないので順序補償するのと、何度もudpputするのは効率的ではないので全てのユーザーのデータbufをパッキングします。
パッキングしたものを上記の方法で「一度の送信」で同期させます。



AKI

リンク

2007/3/23(Fri) 19:54:56|NO.6526

こんにちは93さん
後者のクライアントからのデータです。
サーバでサーバー側でパケット構築し直すとはどういうことですか?
少しスクリ変えてみましたけどエラーがでます

#include "pcbnet2.as" #include "hspda.as" ; ; UDP通信サンプル(サーバー) ; sdim ip,255,16 dim po,16 sdim buf,255,16 dim soc,16 dim soc2,16 port = 12346 ;// ポート12346で待機 title "UDP サーバーテスト" udpsock soc,port if stat : dialog "ソケットの初期化に失敗しました。",1 : end *main udpcheck a,soc ;クライアントからデータが来たら*revへ if a:goto *rev wait 1 goto *main *rev udpget buf,64,soc ;データ受信 sdim getd,128,4 getd=str(getd) csvsel buf csvstr getd,buf dd=int(getd.0)    ;どのクライアントからのデータか確認 sdim pINFO, 256 udpinfo pINFO,soc /*IP*/ getstr ip.dd, pINFO, 0, ':' /*PORT*/ getstr po.dd, pINFO, strsize, ':' po=int(po) repeat 15 udpsendto soc.cnt,ip.cnt,po.cnt  ;繋がっているクライアントに送信先指定 udpput buf,soc.cnt        ;クライアントにデータ送信 wait 1 loop goto *main
としましたがudpsendto soc.cnt,ip.cnt,po.cntがパラメータエラーになります
どの変数がおかしいかわからなくなりました
むずかしい



93

リンク

2007/3/23(Fri) 20:14:15|NO.6527

>>AKIさん
ざっとみた感じでは


/*PORT*/ getstr po.dd, pINFO, strsize, ':' po=int(po)

変数poは数値型なのにgetstr取得時は文字列だからエラー?と思ってます。
あと、その下のpoだとpo.0なのでpo.ddではないです。

だから、getstr時は一時的な文字列変数が必要だと思います。



93

リンク

2007/3/23(Fri) 21:41:30|NO.6531

> サーバでサーバー側でパケット構築し直すとはどういうことですか?

パケット1個につき要求が一回のとは限りません。また、悪意あるパケットかもしれません。
なので、受け取ったパケットを検証したり最適化するためです。

サーバーではクライアントの要求を分解して他のクライアントに配る専用のパケットも作る必要があります。
一度の要求に対して結果を一度の送信で終わらせるのが鉄則です。
(何回もudpputやtcpputを呼ぶのは危険かつ無駄です)

ここだけの話tcpが遅いと言っても1ms~5ms程度のなのでHSPじゃあまり変わらない気がします。
それよりも補間技術の作った方が良い気がします。



AKI

リンク

2007/3/24(Sat) 09:10:29|NO.6542

おはようございます
/*PORT*/
getstr po.dd, pINFO, strsize, ':'
po=int(po)
を一応

dim ppp,16 /*PORT*/ getstr po.dd, pINFO, strsize, ':' ppp.dd=int(po.dd) repeat 15 pos 0,20*cnt mes buf.cnt udpsendto soc,""+ip.cnt,ppp.cnt udpput buf.cnt,soc wait 1 loop
にしたら動いたのですが0番ののデータがクライアントに届きません
とりあえず、TCPサーバとUDPサーバとクライアントソフトを載せます
TCPサーバ:http://akihide.no-ip.com/TCPServer.hsp
UDPサーバ:http://akihide.no-ip.com/udpServer.hsp
クライアント:http://akihide.no-ip.com/Client.hsp
です
TCPだけのソフトは掲示板の
http://hsp.tv/play/pforum.php?mode=pastwch&num=3065にあります



93

リンク

2007/3/24(Sat) 12:16:19|NO.6543

>> AKIさん

気になったのはUDPサーバー側ではudpsockが1つしか生成されていません。(しかも、固定ポート
1:1の場合は良いのですが、1:nの場合は間違ってます。

pcbnetでは
TCPは自動的にtcpacceptで複数のソケットが管理されます。(tcpmakeで出来た親sidとtcpacceptで出来る子sid)
UDPではソケットを自分で管理しなくてはいけません。(全てが親sid)

通信プログラムは 作りたいモノ によって変わるので私も助言しづらいですね。

なので、
一度手を休めて専門的なサイトなどを見てどんなシステムがあるのか調べることをおすすめします。



AKI

リンク

2007/3/24(Sat) 16:20:41|NO.6546

udpsockは1:1なんですね
わかりました。
とりあえず自分でコツコツしてみます
ありがとう



AKI

リンク

2007/4/10(Tue) 11:54:34|NO.7221

荒らされたんですね(−−;



ONION software Copyright 1997-2023(c) All rights reserved.