ページ 11

どうしてもコードが作れません(WinSock)

Posted: 2012年1月26日(木) 09:22
by 史上最悪のデスペナ
WinSockの講座をいろいろ探したのですが、私の検索能力では力が及ばず

サーバー→クライアントから送信された文字列を受信して表示するだけ
クライアント→サーバーへ文字列を送信するだけ

というサンプル例しか見つかりませんでした。これを応用すればいいのは分かるのですが、いろいろ弄ってみても思った動作をしてくれません
Echoサーバーとかいうのもあるようですが、説明がほとんど無く、プログラムを見ても良く分からなかったので
取りあえずはWinSockの基本関数のみを用いてP2P型で相互通信ができるようになりたいです。
(データはプログラム内で勝手に決めておく)
最終的な目標はC/S型で構造体送受信が出来るようになりたいです。(クライアント人数は増減せず固定とする。&DxLibとの共存)
UDPは調子が悪いようなのでTCPオンリーで取り合えず行きます

真に申し訳ありませんが、自分に追い込みを掛けるため、と申しましょうか。
プログラムの稼動実験をMMOの友達に頼んだ上、試験日を2/18(土)18:00~
と宣言してしまったので、何としてもそれまでに最終目標は達成したいです。
基本骨格はこのサイトを使用しています

クライアント→メッセージを送信後、サーバーからメッセージを受信する。

コード:

#include <stdio.h>
#include <winsock2.h>

int main()
{
	WSADATA wsaData;
	struct sockaddr_in Recv, Send;
	SOCKET RecvSock, SendSock;
	char buf[32];

	//winsock2の初期化
	WSAStartup(MAKEWORD(2,0), &wsaData);

	// ソケットの作成
	RecvSock = socket(AF_INET, SOCK_STREAM, 0);
	SendSock = socket(AF_INET, SOCK_STREAM, 0);

	// 接続先指定用構造体の準備
	Recv.sin_family = AF_INET;
	Recv.sin_port = htons(12345);
	Recv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");

	// 接続先指定用構造体の準備
	Send.sin_family = AF_INET;
	Send.sin_port = htons(12346);
	Send.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
		bind(SendSock, (struct sockaddr *)&Send, sizeof(Send));

	// サーバに接続
	connect( RecvSock, (struct sockaddr *)&Recv, sizeof(Recv) );

	send( SendSock, "HELLO", 5, 0);

	// サーバからデータを受信
	memset(buf, 0, sizeof(buf));
	int n = recv( RecvSock, buf, sizeof(buf), 0 );

	printf("%d, %s\n", n, buf);

	// winsock2の終了処理
	WSACleanup();

	return 0;
}
サーバー→クライアントからメッセージ受信後、クライアントにメッセージ送信

コード:

#include <stdio.h>
#include <winsock2.h>

int main()
{
	//winsock2の初期化
	WSADATA wsaData;
		WSAStartup( MAKEWORD(2,0), &wsaData );

	//ソケットの作成
	SOCKET RecvSock = socket(AF_INET, SOCK_STREAM, 0);
	SOCKET SendSock = socket(AF_INET, SOCK_STREAM, 0);

	struct sockaddr_in Recv, Send;

	int len;

	// 接続先指定用構造体の準備
	Send.sin_family = AF_INET;
	Send.sin_port = htons(12345);
	Send.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
		bind( SendSock, (struct sockaddr *)&Send, sizeof(Send));


	//ソケットの設定
	Recv.sin_family = AF_INET;//IPv4。IPv6ならAF_INET6
	Recv.sin_port = htons(12346);//ポート番号
	Recv.sin_addr.S_un.S_addr = INADDR_ANY;//どのアドレスから接続してもおk
	bind(RecvSock, (struct sockaddr *)&Recv, sizeof(Recv) );

	 //TCPクライアントからの接続要求を待てる状態にする。数字は5
	 listen( RecvSock, 5 );

	 //TCPクライアントからの接続要求を受け付ける
	len = sizeof( Send );
	SendSock = accept(RecvSock, (struct sockaddr *)&Send, &len);
	char buf[32];
		// サーバからデータを受信
		memset(buf, 0, sizeof(buf));
		int n = recv( RecvSock, buf, sizeof(buf), 0 );
	printf("%d, %s\n", n, buf);

	 // 5文字送信
	 send(SendSock, "HELLO", 5, 0);

	 // TCPセッションの終了
	 closesocket(SendSock);
	 closesocket(RecvSock);

	 // winsock2の終了処理
	 WSACleanup();

	 return 0;
}
このようにするとサーバーが送信したデータはクライアントが受信できるのですが、クライアントが送信したデータがサーバー側に表示されません
どうしたらいいか教えてください

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月26日(木) 09:48
by softya(ソフト屋)
TCPでサーバ・クライアント型とする場合は、HTTPサーバーの例が参考になると思うのですが。

「Geekなぺーじ:HTTPサーバの作成(TCPサーバサンプル)」
http://www.geekpage.jp/programming/wins ... server.php

そういえば、recvがないように見えますよ。

あとサーバと成るPC(史上最悪のデスペナさんのPC)のインターネット回線のルータはポートを開放しなくてはいけません。ここら辺は大丈夫ですか?
それといちいちIPアドレスを教えるのも大変なので、無料のDDNSサービスも使いましょう。
「Dynamic DO!.jp - ダイナミックDNS -」
http://ddo.jp/

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月26日(木) 11:28
by YuO
とりあえず,TCP/IPでの通信では,一通信で必要なSOCKETは一つです。
一つのSOCKETで,送受信可能になります。
なので,クライアント側で必要なSOCKETは一つ,サーバー側で必要なSOCKETはlisten用に一つと,通信ごとにacceptで払い出されるSOCKETが通信の数だけ,となります。

でもって,行儀良く通信を終了させるために,shutdown(sock, SD_SEND)→0が返るまでrecv→closesocketとする必要があります。


TCP/IPでのC/Sなら,なおさらwinsockについていろいろな質問があります。で書いたWinSock 2.0 プログラミング―Window Socket APIによるネットワークプログラミングのすべてはお薦め。
時間が無いからこそ,確実にまとまった本を読む,というのはよい選択肢だと思いますよ。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月26日(木) 17:59
by 史上最悪のデスペナ
softya(ソフト屋) さんが書きました:TCPでサーバ・クライアント型とする場合は、HTTPサーバーの例が参考になると思うのですが。
HTTPサーバを軽く流し読みしたときはブラウザで接続すると云々かんぬんと出てきたので、あ、これは関係ないなと飛ばした記憶が・・・・・・・
取り合えず、これを読んでもう一度組んでみます
softya(ソフト屋) さんが書きました:そういえば、recvがないように見えますよ。
サーバー側の話ですよね?40行目にありますよ
softya(ソフト屋) さんが書きました:あとサーバと成るPC(史上最悪のデスペナさんのPC)のインターネット回線のルータはポートを開放しなくてはいけません。ここら辺は大丈夫ですか?
それといちいちIPアドレスを教えるのも大変なので、無料のDDNSサービスも使いましょう。
ポート開放はツールを使えばいいかなと思ってます。
IPアドレスは、プログラム内に記述しておけばいいかなと思ってました。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月26日(木) 18:16
by softya(ソフト屋)
史上最悪のデスペナ さんが書きました:サーバー側の話ですよね?40行目にありますよ
失礼しました。ありました。
インデントは法則性をお願いします。
史上最悪のデスペナ さんが書きました:ポート開放はツールを使えばいいかなと思ってます。
IPアドレスは、プログラム内に記述しておけばいいかなと思ってました。
IPアドレスは固定アドレスなのでしょうか?
それとも、2/18の試験はモデムやルーターの電源を切ったりしないからIPアドレスが埋め込みで良いという意味でしょうか?

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月26日(木) 19:26
by 史上最悪のデスペナ
モデムやルーターの電源を切ったりしないので、大丈夫だと判断しました

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月27日(金) 13:53
by softya(ソフト屋)
MMOの前にネット越しの通信テストをやって置くべきだと思いますね。遅延時間とか実際にテストしてみないと分からない事だらけです。
特に家庭用回線はアップロード側が極端に弱いので数人接続するだけでパンクするかも知れません。データ転送量と周期にもよりますが。

Dixq (管理人)さんもロビー機能だけでテストされた事があります。
「α版公開。ご協力お願いします! • C言語交流フォーラム ~ mixC++ ~」
http://dixq.net/forum/blog.php?u=53&b=2166

通信に関する負荷テストや遅延データの収集をしておかないと、そもそもゲームの通信データの周期を決めることや同期のシステムを決定できません。
なのでテストが先だと思うんですけどね。最初は接続できるかどうかだけでも問題なので1対1テストだけでもやって置くほうが良いと思います。出来れば多人数接続も。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月27日(金) 16:25
by 史上最悪のデスペナ
softya(ソフト屋) さんが書きました:MMOの前にネット越しの通信テストをやって置くべきだと思いますね。遅延時間とか実際にテストしてみないと分からない事だらけです。
まだMMOとして全然出来てませんので、ただのチャットソフトレベルのテストをしたいだけです。
一応予定としては
リアルフレと1:1のP2P通信(コンソール)
→リアルフレ二人がMyPCをサーバーとしてC/S通信(コンソール→withDxLib)
→2/18・・・・・・5人程度の少人数でC/S通信
としたいです。
来週月曜日から期末テストなので1週間ほどこちらにお邪魔できないと思いますがご容赦ください

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月27日(金) 21:25
by softya(ソフト屋)
史上最悪のデスペナ さんが書きました:
softya(ソフト屋) さんが書きました:MMOの前にネット越しの通信テストをやって置くべきだと思いますね。遅延時間とか実際にテストしてみないと分からない事だらけです。
まだMMOとして全然出来てませんので、ただのチャットソフトレベルのテストをしたいだけです。
一応予定としては
リアルフレと1:1のP2P通信(コンソール)
→リアルフレ二人がMyPCをサーバーとしてC/S通信(コンソール→withDxLib)
→2/18・・・・・・5人程度の少人数でC/S通信
としたいです。
来週月曜日から期末テストなので1週間ほどこちらにお邪魔できないと思いますがご容赦ください
たとすると、まずチャットシステムを組むだけでも手間がかかると思います。
2/18の前に1:1のC/Sのテストも必要でしょうね。
P2Pは両方がC/Sなるという意味ですから狙っている今の方向と違うと思います。それにP2Pだと両側でポートを開ける必要が出てきます。
IPアドレスを通知し合う仕組みとかP2Pは違う意味で難度が高くなります。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月28日(土) 08:41
by 史上最悪のデスペナ
softya(ソフト屋) さんが書きました:まずチャットシステムを組むだけでも手間がかかると思います。
2/18の前に1:1のC/Sのテストも必要でしょうね。
P2Pは両方がC/Sなるという意味ですから狙っている今の方向と違うと思います。それにP2Pだと両側でポートを開ける必要が出てきます。
IPアドレスを通知し合う仕組みとかP2Pは違う意味で難度が高くなります。
チャットシステムはDxLibのサンプルを流用すればいいかなと思ってました。(ゲームはDxLibを使用するため)
C/Sでも両方にポート開放をしなければならないと思っていました。でも、言われてみればMMOやってるときにわざわざ受信用にポート開放しませんね
(父がルータ設定で受信用のポート開放できないようにしてるみたいなので。(正確に理解してるわけではないですが))
では、P2Pは止めた方がいいですよね。

いろいろ「ネットワーク通信 仕組み」とか「ネットワーク通信 ルータ」とかで調べているのですがなかなかいいサイトが見つかりません
YuO さんが書きました:TCP/IPでのC/Sなら,なおさらwinsockについていろいろな質問があります。で書いたWinSock 2.0 プログラミング―Window Socket APIによるネットワークプログラミングのすべてはお薦め。
時間が無いからこそ,確実にまとまった本を読む,というのはよい選択肢だと思いますよ。
真に身勝手で申し訳ありませんが本を買いたくないので(図書館で一応予約しました)、よろしければいい講座をご存知でしたら教えていただけませんか?

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月28日(土) 09:26
by softya(ソフト屋)
史上最悪のデスペナ さんが書きました:チャットシステムはDxLibのサンプルを流用すればいいかなと思ってました。(ゲームはDxLibを使用するため)
C/Sでも両方にポート開放をしなければならないと思っていました。でも、言われてみればMMOやってるときにわざわざ受信用にポート開放しませんね
(父がルータ設定で受信用のポート開放できないようにしてるみたいなので。(正確に理解してるわけではないですが))
では、P2Pは止めた方がいいですよね。
受信用のポートを開放できないのですか?それではサーバーに成れません。友人のPCをサーバーにするのでしょうか?
せっかく友人を集めて無駄骨に終わらせたくなければ、UPnPCJ などでポート開放のテストとかしておいたほうが良いと思います。
史上最悪のデスペナ さんが書きました:真に身勝手で申し訳ありませんが本を買いたくないので(図書館で一応予約しました)、よろしければいい講座をご存知でしたら教えていただけませんか?
正確に言うと予算の都合で買えないって話では無かったでしょうか?
良いサイトはないので自分で考えるしか無いってのが現状でしょうか。良い書籍に勝る情報はなかなか無いと言って良いと思います。
大体は英語なので、一応ご紹介しますけど。

とりあえず、書籍のサンプルコードがダウンロードできます。
「WinSock2 プログラミング 改訂第2版」
http://winsock2.org/

本家マイクロソフト。英語版のみ。
「Windows Sockets 2 (Windows)」
http://msdn.microsoft.com/en-us/library/ms740673

「WinSock Development Information」 英語の情報サイト。
http://www.sockets.com/

「Winsock Programmer's FAQ」 一応日本語FAQ
http://www.kt.rim.or.jp/~ksk/wskfaq-ja/index.html

「Studying HTTP」 関係する情報として
http://www.studyinghttp.net/

[追記] 基礎情報
「TCP/IP入門」
http://net-newbie.com/tcpip/
「TCP/IP入門 - @IT」
http://www.atmarkit.co.jp/channel/tcpip/tcpip.html



それとこんなものを見つけました。
「Windows Server 2003、Windows XP、および Windows Vista で Winsock2 の破損を確認して回復する方法」
http://support.microsoft.com/kb/811259/ja
これを確認してみてください。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月28日(土) 18:31
by 史上最悪のデスペナ
softya(ソフト屋) さんが書きました:受信用のポートを開放できないのですか?それではサーバーに成れません。友人のPCをサーバーにするのでしょうか?
せっかく友人を集めて無駄骨に終わらせたくなければ、UPnPCJ などでポート開放のテストとかしておいたほうが良いと思います。
相変わらす言葉足らずで申し訳ありません。
父が設定したなら頼んでそのポート番号だけ開放できるようにしてもらえば、と思っていたのです。
YuO さんが書きました:とりあえず,TCP/IPでの通信では,一通信で必要なSOCKETは一つです。
一つのSOCKETで,送受信可能になります。
なので,クライアント側で必要なSOCKETは一つ,サーバー側で必要なSOCKETはlisten用に一つと,通信ごとにacceptで払い出されるSOCKETが通信の数だけ,となります。
一応、Serverが受信して送信することができるようになりました。(←テスト勉強はどうした)

後はポート開放が何ともならないと何も出来ないのですが、ふと疑問に思ったのでここでついでに質問させていただきます

コード:

----------ルータ(-----モデム)----------Internet----------(モデム-----)ルータ----------
|    |                                                                          |    |
|    |                                                                          |    |
|    |                                                                          |    |
A    B                                                                          C    D
この場合、A(Client)とD(Server)で通信する場合、AがDに接続するためにアクションを起こすわけですが
Aが指定するIPアドレスはグローバルIPアドレスで、それは言い換えればモデムのアドレスですよね?で、ルータのポートを通ってLAN内に入るわけですが、
そこからC,Dの識別とD内の接続先アプリケーションの識別はどうやるのでしょう?
人によっては、ルータからさらに無線コンバータで無線接続している人もいるでしょう。無線コンバータがさらに複数のPCのアクセスポイントとなっていると・・・・・・・・・
教えてください

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月28日(土) 18:40
by softya(ソフト屋)
ポート開放時に、転送先のLANのローカルIPアドレスを固定する必要があります。
なので、WANからLANに入るときルータでポート番号毎に宛先ローカルIPアドレスに振り分けられるので何処に行くのか心配する必要はありません。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月28日(土) 18:59
by 史上最悪のデスペナ
なるほど。ポート開放時に固定をしているから気にする必要が無い、だから巷のネット講座にはそれに関する記述が無かったんですね

ネットワークの仕組みとか調べても
LANとは・WANとは・IPアドレス・OSI7層構造etc
が出てくるだけで困ってたんです。

ありがとうございます。
まだ、躓くことが出てくると思いますのでそのときはよろしくお願いします

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月28日(土) 19:20
by softya(ソフト屋)
史上最悪のデスペナ さんが書きました:なるほど。ポート開放時に固定をしているから気にする必要が無い、だから巷のネット講座にはそれに関する記述が無かったんですね

ネットワークの仕組みとか調べても
LANとは・WANとは・IPアドレス・OSI7層構造etc
が出てくるだけで困ってたんです。

ありがとうございます。
まだ、躓くことが出てくると思いますのでそのときはよろしくお願いします
ちなみに、その技術は静的IPマスカレードを調べてみてください。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月29日(日) 12:12
by 史上最悪のデスペナ
softya(ソフト屋) さんが書きました:その技術は静的IPマスカレードを調べてみてください。
そんな用語があるのですね。本ではなくネットで検索することの弊害の一つですよね。正しい言葉じゃないと見つけれないのは。

次の段階に来ました。(ポート開放はまだ)
アップデートサーバーを作ろうと思いましたが、ファイルサイズ(size_t)が送信出来ません
sendやsendtoでは送信データがchar*型で(char *)filesizeとやってみても送信自体が出来ていないようです

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月29日(日) 12:35
by softya(ソフト屋)
史上最悪のデスペナ さんが書きました:
softya(ソフト屋) さんが書きました:その技術は静的IPマスカレードを調べてみてください。
そんな用語があるのですね。本ではなくネットで検索することの弊害の一つですよね。正しい言葉じゃないと見つけれないのは。

次の段階に来ました。(ポート開放はまだ)
アップデートサーバーを作ろうと思いましたが、ファイルサイズ(size_t)が送信出来ません
sendやsendtoでは送信データがchar*型で(char *)filesizeとやってみても送信自体が出来ていないようです
ポインタなので(char *)(&filesize)だと思いますが。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月30日(月) 15:06
by 史上最悪のデスペナ
ポート開放を行い、1:1のC/S型通信に成功しました。
softya(ソフト屋) さんが書きました:ポインタなので(char *)(&filesize)だと思いますが。
そうでした。

次に、exeを送信しようとしたのですが

コード:

    FILE *fp = fopen("Client.exe",rb"); 
        fpos_t fsize = 0;
        fpos_t fsizeb =  fseek(fp,0,SEEK_END);
        fgetpos(fp,&fsize);
        fseek(fp,fsizeb,SEEK_SET); 
 
        BYTE *binBuff = new BYTE[filesize]
        fread( binBuff, filesize,  1, fp)
    fclose(fp);
 
という風に読み込みsendで先にファイルサイズを送ってから次にbinBuffを送ったのですが、

コード:

	recv( RecvSock, (char *)&fsize, sizeof(size_t), 0 );
	BYTE *binBuff = new BYTE[ (size_t)fsize ];
	recv( RecvSock, (char *)binBuff, fsize, 0 );
		FILE *fp = fopen( "SAO-Client.exe","wb" );
			fwrite( (BYTE *)binBuff, (size_t)fsize, 1, fp );
		fclose(fp);
以上のようにして受け取ったexeを起動してみると

コード:

このファイルのバージョンは、現在の実行中のWindowsのバージョンとは互換性がありません。コンピュータのシステム情報を確認して、x86(32ビット)またはx64(64ビット)のどちらのバージョンのプログラムが必要であるかを確認してからソフトウェアの発行者に問い合わせてください
と出ました。
所謂、アップデート(大型)を行いたいのですがどのようにしたらいいのでしょうか?

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月30日(月) 15:16
by softya(ソフト屋)
あれ?試験中では?

大きなパケットを送るのは複数のパケットに自動分解されますので一回のrecvで受け止め切れないんだと思います。recv出来るだけrecvし続ける必要があります。
受け取ったバイナリを送信前のものと比べてみてください。途中までしか無いと思います。こういう所でバイナリエディタの出番です。

[補足]送信されているパケットもwiresharkで確認して下さい。こういうところを手を抜くとあとでひどい目に会います。
LANの192.168.0.1のルータ宛でも構わないのでwiresharkで表示出来るように出来るはずです。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月30日(月) 15:49
by shiro4ao
しばらくROMっておりました。
TCPはストリームなのでデータの区切りがないからそういう状況になったのでしょうか・・・・?

たとえば以下のようなコードのサーバから以下のように送っても

コード:

send(Client,"a",1,0);
send(Client,"b",1,0);
クライアントが以下のコードになっていても受け取りかたはいろいろあります。

コード:

recv(Server,buf,sizeof(buf),0);     //①
recv(Server,buf,sizeof(buf),0);     //②
いろいろパターンがあります
「①でa②でbが届く」
「①でabがどとく」
などです。

今回は、「①でabがどとく」場合になって、
exeファイルにサイズのデータがまぎれこんでしまったため
Windowsが理解出来ないファイルになってそのようなエラーがでたのかと予想します。


仮に「TCPはストリームなのでデータの区切りがないからそういう状況になった」のが原因と考えた場合の解決策です。
1.時間をおいてsend()する。(急ごしらえの解決策)
2.ファイルだけ送る(実装簡単、拡張性なし)
3.ファイルサイズと本体の区別をしてを送るようなプロトコルを喋らせる(たぶん長期的に大丈夫な解決策)

まず、1ですが、上記の例だと確実に「①でa②でbが届く」パターンになるように十分時間を開けるのです

コード:

send(Client,"a",1,0);
Sleep(1000*5);			//5秒待つ
send(Client,"b",1,0);
そうすれば、多くの場合は「①でa②でbが届く」パターンになります
しかし、そうは言っても、必ず成功する安全な方法ではないので、長期的にはお勧めできません。


2はサイズなど送らずにファイルを送ります。
「え?受信側はどうやってファイルの受信の終了をしればいいの?」という疑問が出ますが、受信側で以下のようにします

コード:

int size;
size=sizeof(buf);
while(ret<size)ret=recv(Server,buf,size,0);
こうすれば、
バッファが満タンになって処理を返した→まだデータが送られてきますね
バッファが少し余って処理を返した  →データがもうきませんね、抜けます
となります。
実装はある程度簡単だが、他のデータが送れないので将来性は高くない

次に3ですが、たとえば、5バイトのファイルで中身に"abcdef"と書かれたファイルを送る場合
送信する側は「SendSize=5Body=abcdef」のようにおくるのです
どのようなルールでサイズと中身の違いをつたえるかはプログラマの自由です。
受信側は、もらったデータをサイズ部分とファイル本体部分に切り分けて処理します
実装はややこしくなりますが機能追加など考えた場合は長期的に有利です。



「データの区切りがない」のが原因という仮説の元長々と書いてしまいましたが、ご参考になれば幸いです。


追伸:1対多のチャットのサンプルのような出来損ないを日記に書きました(1/28「複数のクライアントからのデータを送る」)


/*追記*/
バッファが足りなかったのですね、なんと焦って投稿してしまいすみません。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月30日(月) 22:47
by 史上最悪のデスペナ
softya(ソフト屋) さんが書きました:あれ?試験中では?

大きなパケットを送るのは複数のパケットに自動分解されますので一回のrecvで受け止め切れないんだと思います。recv出来るだけrecvし続ける必要があります。
受け取ったバイナリを送信前のものと比べてみてください。途中までしか無いと思います。こういう所でバイナリエディタの出番です。

[補足]送信されているパケットもwiresharkで確認して下さい。こういうところを手を抜くとあとでひどい目に会います。
LANの192.168.0.1のルータ宛でも構わないのでwiresharkで表示出来るように出来るはずです。
息抜きもたまには必要ですよね!遊んでいるわけでは無いんです。これはあくまで(以下略)
確かに試験勉強中なので試行錯誤系はやれませんので後回しさせていただきます。
shiro4ao さんが書きました:追伸:1対多のチャットのサンプルのような出来損ないを日記に書きました(1/28「複数のクライアントからのデータを送る」)

拝見させていただきました。ちょうど、今からお聞きしようと思っていた無いようだったので吃驚です。あなたは預言者なのでしょうかw?
こちらも息抜き時に少しづつ解読させていただきます。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月31日(火) 20:51
by 史上最悪のデスペナ
史上最悪のデスペナ さんが書きました:確かに試験勉強中なので試行錯誤系はやれませんので後回しさせていただきます。
(中略)
こちらも息抜き時に少しづつ解読させていただきます。
WireSharkの方はまだ後回し。
Shiro4aoさんのコードですが、よく分かりませんでした。
コメントにあったコードは、やろうとしていることは分かる気がするのですが。

なので、よろしければ作り方を少しずつ教えていただけると嬉しいです。
と、その前に
この前成功したのは、物理的距離が30kmぐらい(かなり適当)離れた人との通信で

コード:

クライアントが接続→クライアントがHELLOと送信→サーバーが受信→サーバー側で受信文字列表示
→サーバーがHELLO-Cと送信→クライアントが受信→クライアント側で受信文字列表示
という形でした。
今度は、取り合えず同一PCで
上記の形を延々とループさせたいと思い、クライアント側のコードを

コード:


	// サーバに接続
	connect( Sock, (struct sockaddr *)&Addr, sizeof(Addr) );

	send( Sock, "HELLO", 5, 0);

	// サーバからデータを受信
	memset(buf, 0, sizeof(buf));
	int n = recv( Sock, buf, sizeof(buf), 0 );

	printf("%d, %s\n", n, buf);
から、

コード:

while(1)
{
	// サーバに接続
	connect( Sock, (struct sockaddr *)&Addr, sizeof(Addr) );

	send( Sock, "HELLO", 5, 0);

	// サーバからデータを受信
	memset(buf, 0, sizeof(buf));
	int n = recv( Sock, buf, sizeof(buf), 0 );

	printf("%d, %s\n", n, buf);
}
と変化させたのですが、最初の一回のみ送受信できましたが残りは出来ませんでした。WireSharkで見たところ、数秒おきに送信できているようです。
計測したわけではなく体感ですが送信感覚はばらばらのようです。
受信側は

コード:

	while(1)
	{
		 //TCPクライアントからの接続要求を受け付ける
		len = sizeof( Send );
		SendSock = accept(RecvSock, (struct sockaddr *)&Send, &len);
		char buf[32];
		// サーバからデータを受信
		memset(buf, 0, sizeof(buf));
		recv( SendSock, buf, sizeof(buf), 0 );
			printf("%s\n", buf);
		 // 5文字送信
		 send(SendSock, "HELLO-C", 7, 0);
		 // TCPセッションの終了
		 closesocket(SendSock);
	}
としています

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月31日(火) 21:35
by YuO
TCP/IPでのsocketを使った通信方法を理解してくださいとしかいいようが無いのですが……。
サーバー側でclosesocketしている理由は何故ですか。

とりあえず,MSDN: Getting Started with Winsockのコードを,適宜MSDN読みながら見ていくとよいかと。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月31日(火) 21:41
by 史上最悪のデスペナ
YuO さんが書きました:サーバー側でclosesocketしている理由は何故ですか。
分かりません。このサイトをコピペしていじってるだけなので。
YuO さんが書きました:とりあえず,MSDN: Getting Started with Winsockのコードを,適宜MSDN読みながら見ていくとよいかと。
行ってきます

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月31日(火) 21:52
by taketoshi
Shiro4aoさんの日記にコード残したのは私カッ!
私も一年ほど前に似たようなことで悩んだので少しでもアドバイスになればと思います。
TCP通信をするにあたって、データは小刻みに送られてきます。wiresharkでパケットが数回に分けて送られているのが確認できると思います。
recv関数の戻り値が受信したbyte数だと思ったのですが以下のような実験をしたことがあります。

受信専用関数にメッセージボックス関数を噛ませて同一データを送り。
サーバとクライアントの距離により、何回受信通知(FD_READ)が来たか調べる実験。

千葉→千葉 1回
千葉→東京 3回
千葉→大阪 5回
(追記:実験内容は。ん~、記憶が遠くて少し違うかもしれない、こんな感じなんだというのを掴んでいただければ)

同一データを送受信しただけでもこのようなデータのやりとりをしています。(あくまで個人的な実験の結果です)
導きだしたのは、何度受信通知が来ようが、データを受信しきらねばならないということです。

同一PCでクライアントとサーバーを起動させている環境だと単純にsend関数とrecv関数をひとつずつ記述するだけで送受信は成功します。
しかし、距離が離れた時点でそれは破綻します。send関数は一度ですべて送りきりますが、recvは何回も受信しなければデータの尻尾が切れてしまうのです。

http://dixq.net/forum/viewtopic.php?t=7917

一年前に私が同一の質問をした内容です。参考になると思います。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年1月31日(火) 22:00
by softya(ソフト屋)
どうしても本を買えないなら重要な所は英語で何とかして下さいとしか言えないですね。
「Winsock Programmer’s FAQ: Winsock Programmer's FAQ」
http://tangentsoft.net/wskfaq/
日本語訳もありますが古いです。
「Winsock Programmer's FAQ」
http://www.kt.rim.or.jp/~ksk/wskfaq-ja/

一応、こちらも参考に成るかと。
「Winsock 入門」
http://tip.sakura.ne.jp/htm/netframe.htm

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月01日(水) 02:41
by YuO
史上最悪のデスペナ さんが書きました:
YuO さんが書きました:サーバー側でclosesocketしている理由は何故ですか。
分かりません。このサイトをコピペしていじってるだけなので。
ここまで言い切られるといっそ清々しいですが……。

サンプルはサンプルです。
エッセンスは抽出されていますが,それだけで理解するためのものではありませんし,そのまま実用に耐えるものとは限りません。
ちゃんとMSDN等で何をするAPIかを調べることは必須ですし,エラー処理はちゃんとしましょう。


コードを見ている限り,ソケットのレイヤでコードを間違えているためにやりたいことができていないように思えます。
ソケットAPIの呼び出しの戻り値を確認し,失敗していたら必ずWSAGetLastErrorを確認する,というエラー処理の基本をするだけでも,
例えばクライアント側の2回目のconnectWSAEISCONNが起きていたのが認識できたはずですし,
sendまたはrecvWSAECONNABORTEDが起きていることも認識できたはずです。

ちなみに,簡単にサーバーの通信手順を書くと,
  • socketで待ち受け用ソケットを作成する
  • bindでソケットの待ち受け先を指定する
  • listenで待ち受けを開始する
  • 待ち受けた通信を行う
    • acceptで着信を待ち,通信用のソケットを取得する (acceptは通信が開始されるまで内部で待ちます)
    • send/recv等で通信を行う
    • 通信が終わったらshutdownして通信の終了を通知する
    • closesocketでソケットを破棄する
  • 待ち受けを終了する時は,待ち受け用ソケットをclosesocketする
となります。
ついでに,クライアント側の通信手順は,
  • socketで通信用ソケットを作成する
  • connectでサーバーに接続し通信を開始する
  • send/recv等で通信を行う
  • 通信が終わったらshutdownして通信の終了を通知する
  • closesocketでソケットを破棄する
となります。
# 非同期通信とか,マルチスレッドとか,そういう話はひとまず置いておきます。


こちらで作成したテストプログラムは以下になります (VS2010, C++, Console)。
これをソリューション中に二つのプロジェクトとして登録してマルチスタートアップで同時実行しました。
# 名残で_getchなどがありますし,テストプログラムなのでsend/recv自体をループにしていません(送信/受信できた分だけ表示)。
  • server
    ► スポイラーを表示
  • client
    ► スポイラーを表示
クライアント側でconnectの位置をwhile中に移動したり,サーバー側でlisten_sockのclosesocketを取りやめてaccpetをwhile中に放り込んで末尾にclosesocketを入れてみたりすると,
エラーが発生してエラーコードが表示されると思います。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月01日(水) 08:34
by shiro4ao
史上最悪のデスペナ さんが書きました:確かに試験勉強中なので試行錯誤系はやれませんので後回しさせていただきます。

Shiro4aoさんのコードですが、よく分かりませんでした。
コメントにあったコードは、やろうとしていることは分かる気がするのですが。

なので、よろしければ作り方を少しずつ教えていただけると嬉しいです。
出来れば週末に大まかな流れを日記に記載しようとおもいます。
とはいっても、あのコードはスレッドたちとソケットの面倒を見るための操作が
ほとんどなのでWinsockの参考になるかといえば、微妙です。
もっと無駄のないサンプルがあると思います。

YuOさんが上でご紹介なさっていた
MSDN: Getting Started with Winsockが丁寧でわかりやすいかとおもいます。


チャットをする場合、送信ごとに接続&切断していたらオーバーヘッドが大きすぎて
困ると思います。(まして、ゲームなら応答性も高いほうがいいですし)
毎回切断するのは「HTTPを通してチャットしなければならない」といった状況以外では
あまりいい方法ではない気がします。
解決策は
「毎回切断せず、つながったら、送信したければsend()受信したければrecv()とする」
です。

が、多くの場合チャットは人間がするものなので送信と受信が交互に行われる保証がないため、
同期通信より非同期な通信の方が現実的です。

まずはYuOさんの説明で、Winsockの使い方を知るのが近道のような気がします。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月01日(水) 22:58
by 史上最悪のデスペナ
taketoshi さんが書きました:Shiro4aoさんの日記にコード残したのは私カッ!
(中略)
受信専用関数にメッセージボックス関数を噛ませて同一データを送り。
サーバとクライアントの距離により、何回受信通知(FD_READ)が来たか調べる実験。

千葉→千葉 1回
千葉→東京 3回
千葉→大阪 5回
貴方ダッ!距離が遠いと分割されやすい・・・・?仮に地球の裏側でも一つのパケットだけで送れるサイズはどれくらいなんだろう・・・・?(独り言)
YuO さんが書きました:エラー処理はちゃんとしましょう。
そうなんですよね。前に他のトピックでそうアドバイスをいただいたのに出来てないんですよね・・・・・・・・エラーコードエラーコード・・・・・・・・・
YuO さんが書きました:ちなみに,簡単にサーバーの通信手順を書くと,(中略)
ついでに,クライアント側の通信手順は,(中略)

クライアント側でconnectの位置をwhile中に移動したり,サーバー側でlisten_sockのclosesocketを取りやめてaccpetをwhile中に放り込んで末尾にclosesocketを入れてみたりすると,
エラーが発生してエラーコードが表示されると思います。
shiro4ao さんが書きました:解決策は
「毎回切断せず、つながったら、送信したければsend()受信したければrecv()とする」
です。
出来ました。毎回切断して、送信するたびに繋げばいいと思ってました。だからだったんですね。

その次の段階として、

コード:

   →→→→→→ ←←←←←←
  ↑ Hello-B  ↓ Hello-A  ↑
  ↑          ↓          ↑
ClientA   Server   ClientB
  ↑      ↓      ↑
  ↑          ↓          ↑
   ←←←←←← →→→→→→
      Hello-A      Hello-A
      Hello-B      Hello-B
無限ループで以上のような動作をさせたいと思います。
ClientAとBのconnectするタイミングは不明なのでマルチスレッドでacceptをループさせ続けることにしました。
後はrecvをどうにかすればいいのだと思いますが、単純にマルチスレッドにするとクライアント数が可変のときに対応が出来ないんじゃないかと思いました。
ここで非同期通信とか言う物が必要になってくるのでしょうか?

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月02日(木) 10:08
by shiro4ao
非同期な通信の方法としては

1.select(),WSAASyncSelect(),WSAAEventSelect()などをつかう。
 上の3つのどれを使っても「繋がっているクライアントの誰が喋ったのか」がわかります
 なのでsend()recv()などにブロッキングされずに通信ができます。
 サーバーをGUIで作るなら近道だと思います。クライアントが大量にぶら下がらず、
 なおかつGUIインフラが存在するであろうP2Pソフトならある程度楽かもしれません。
 (推測ですが先日、日記にご返信いただいたときの
 taketoshiさんのコードはそのようになっていた気がします。)
 ただし、64ソケット制限にひっかかりますので、
 MMOレベルに大規模化する場合は実装に工夫が必要です。
 それに加えメッセージが飛び交うのでたぶん、プロセッサを余計に食ったり
 スループットが「スレッドをつかう」に比べて悪い可能性あり。(実験はしていません)
 (それ以前にクライアント用Windowsで64人も接続しちゃダメな気がする、ライセンス的な意味で)

2.スレッドを使う。クライアントごとに処理を分けられるので
 上記に比べ見通しがよくなる気がします。
 が、スレッド間の同期をとろうとしたらそれなりに難易度があがります。

3.ポーリングする。同期通信の変形ですが、送信データをすぐに贈るのではなく貯めこんでおいて、
 「データ溜まってますか?」という問に答える形で「こんなデータがありますよ」と送る。
 チャットとしてはなんとかならないわけではないかもしれないが、
 リアルタイムに送受信してるわけではないのでアクション性の高いゲームにはむかないと思います。
 なによりスケーラビリティが低いので、おすすめできない。

サーバーなのでスループット、スケーラビリティを考えればスレッドを使うのがいいような気がしますが、
他の方はどうお考えでしょうか?

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月02日(木) 11:23
by キナル
この通信教育はいつまでつづくのでしょうか?
質問の範囲はどこまでなのでしょうか?
回答していても、終わりがないようなきがします。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月02日(木) 11:31
by softya(ソフト屋)
一般のWindows7/Vistaは20接続までです。
サーバー用Windowsはバカ高い(安いやつで8万円ぐらい)ので、個人でやるにはLinux/FreeBSDぐらいしか選択肢はありません。

なので本格的に構築するなら最初からLinux/FreeBSDで開発したほうが良いと思います。
VirtualBoxやVMwarePlayerで導入すればWindows下で起動できて別ローカルIPを割り当てることも出来るので一台のPCでサーバ・ークライアント間の通信もテストできます。wireSharkでパケットも取れるはずです。ルータ越えのテストは出来ませんが。
shiro4ao さんが書きました:サーバーなのでスループット、スケーラビリティを考えればスレッドを使うのがいいような気がしますが、
他の方はどうお考えでしょうか?
私もスレッドの導入は必要だと思います。
キナル さんが書きました:この通信教育はいつまでつづくのでしょうか?
質問の範囲はどこまでなのでしょうか?
回答していても、終わりがないようなきがします。
キナルさんは、どうされるべきだと思いますか?

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月02日(木) 11:50
by softya(ソフト屋)
史上最悪のデスペナ さんが書きました:無限ループで以上のような動作をさせたいと思います。
ClientAとBのconnectするタイミングは不明なのでマルチスレッドでacceptをループさせ続けることにしました。
後はrecvをどうにかすればいいのだと思いますが、単純にマルチスレッドにするとクライアント数が可変のときに対応が出来ないんじゃないかと思いました。
ここで非同期通信とか言う物が必要になってくるのでしょうか?
「スレッド winsock サーバ」で検索すれば色々と転がっています。
まず、そちらを見た上で質問されたほうが良いかと思います。見た上で分からないことであれば色々と聞いて下さい。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月02日(木) 20:06
by 史上最悪のデスペナ
キナル さんが書きました:この通信教育はいつまでつづくのでしょうか?
質問の範囲はどこまでなのでしょうか?
回答していても、終わりがないようなきがします。
最初に書いた以下のように、C/S型で構造体受信が出来るようになるまでお願いしたいと思っています。
史上最悪のデスペナ さんが書きました: 最終的な目標はC/S型で構造体送受信が出来るようになりたいです。(クライアント人数は増減せず固定とする。&DxLibとの共存)
softya(ソフト屋) さんが書きました:
shiro4ao さんが書きました:サーバーなのでスループット、スケーラビリティを考えればスレッドを使うのがいいような気がしますが、
他の方はどうお考えでしょうか?
私もスレッドの導入は必要だと思います。
softya(ソフト屋) さんが書きました:「スレッド winsock サーバ」で検索すれば色々と転がっています。
一応、ここで質問させていただく前にネットで調べてからにしているのですが、まだまだ足りないようですね。

「マルチスレッドにする」という方針の下、紆余曲折を経て可変長配列に辿り着きました。
これを用いて

コード:

acceptスレッド
acceptで新しいソケットが作成されたら可変長配列に加える
その後新しい通信用スレッドを配列に加える

通信用スレッド
通信が終わったらソケットを破棄、ソケットハンドルをerase()後、スレッドを破棄してスレッドハンドルをerase
という感じでやっていこうと思いますが、もっといい方法は無いでしょうか?

スレッド数動的変更や、スレッド動的作成について調べたのですが、それについて書かれたサイトを見つけることが出来ませんでした。どのようにしたらいいか教えて下さい。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月02日(木) 21:38
by softya(ソフト屋)
史上最悪のデスペナ さんが書きました: 「マルチスレッドにする」という方針の下、紆余曲折を経て可変長配列に辿り着きました。
これを用いて

コード:

acceptスレッド
acceptで新しいソケットが作成されたら可変長配列に加える
その後新しい通信用スレッドを配列に加える

通信用スレッド
通信が終わったらソケットを破棄、ソケットハンドルをerase()後、スレッドを破棄してスレッドハンドルをerase
という感じでやっていこうと思いますが、いい方法は無いでしょうか?
可変長配列をスレッドで共有するのは不慣れな人がやると危険極まりないのでやめたほうが良いと思います。
それに共有する意味そのものがないと思いますので、acceptで新しいソケットが作成されたら、そのままスレッドに渡してスレッドで破棄すれば良いのでは無いでしょうか。
それと通信用スレッドハンドルも管理しなくて良い気がします。なにをメインスレッドから管理しますか?
史上最悪のデスペナ さんが書きました: スレッド数動的変更について調べたのですが、それについて書かれたサイトを見つけることが出来ませんでした。どのようにしたらいいか教えて下さい。
acceptで新しいソケットが作成される毎に新しいスレッドを起動するだけで良いです。

【補足】
本格的なサーバならスレッドの異常終了とか色々と考慮しないといけないですし、スレッド起動しっぱなしで同期処理したほうが早いので可変長配列で情報共有とか色々テクニックはあると思いますが、それはスレッドや通信などの知識に長けた人が考えるべきで付け焼刃で設計しても不安定なものしか出来ません。なので安定・簡単をコンセプトに作成することを目指しましょう。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月02日(木) 21:51
by 史上最悪のデスペナ
softya(ソフト屋) さんが書きました:
史上最悪のデスペナ さんが書きました:
という感じでやっていこうと思いますが、いい方法は無いでしょうか?
可変長配列をスレッドで共有するのは不慣れな人がやると危険極まりないのでやめたほうが良いと思います。
それに共有する意味そのものがないと思いますので、acceptで新しいソケットが作成されたら、そのままスレッドに渡してスレッドで破棄すれば良いのでは無いでしょうか。
それと通信用スレッドハンドルも管理しなくて良い気がします。なにをメインスレッドから管理しますか?
史上最悪のデスペナ さんが書きました: スレッド数動的変更について調べたのですが、それについて書かれたサイトを見つけることが出来ませんでした。どのようにしたらいいか教えて下さい。
acceptで新しいソケットが作成される毎に新しいスレッドを起動するだけで良いです。
編集が間に合わなかった様ですね。「もっと」いい方法、のつもりでした。
そうか、スレツドではmain関数内の変数を使用するのではなく、各スレッド内で別々に変数が使用出来るので同じ変数名になっても大丈夫ですね。
スレッドハンドルを可変長配列にしようと思ったのは、名前をCommu[O]、Commu[1]・・・としたかったからです。
softya(ソフト屋) さんが書きました: 【補足】
本格的なサーバならスレッドの異常終了とか色々と考慮しないといけないですし、スレッド起動しっぱなしで同期処理したほうが早いので可変長配列で情報共有とか色々テクニックはあると思いますが、それはスレッドや通信などの知識に長けた人が考えるべきで付け焼刃で設計しても不安定なものしか出来ません。なので安定・簡単をコンセプトに作成することを目指しましょう。
スレッド常時起動・・・ですか。今の私には、想像もつきません。一度通信関係を作り終えたら調べてみようと思います。(←調べるだけで、多分またsoftya(ソフト屋)さんにお世話になるに決まっているのでやめておいた方が無難かも)

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月02日(木) 22:01
by softya(ソフト屋)
史上最悪のデスペナ さんが書きました:スレッドハンドルを可変長配列にしようと思ったのは、名前をCommu[O]、Commu[1]・・・としたかったからです。
スレッドハンドルを何に使うかと固定長ではなく可変長配列でないと行けない理由を説明してみてください。
史上最悪のデスペナ さんが書きました: スレッド常時起動・・・ですか。今の私には、想像もつきません。一度通信関係を作り終えたら調べてみようと思います。(←調べるだけで、多分またsoftya(ソフト屋)さんにお世話になるに決まっているのでやめておいた方が無難かも)
サーバーで行う処理の設計次第です。世の中にある有名サーバーも常時起動型と随時起動型があるみたいですし。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月02日(木) 22:15
by 史上最悪のデスペナ
えっと・・・
スレッドハンドル→スレッドを区別するため
いや・・・待てよ・・・
スレッドハンドル→スレッドで使う関数を区別するため・・・?
とすると、プログラムは同じなんだからスレッドハンドルは1つでいいのか・・・な?

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月02日(木) 22:23
by softya(ソフト屋)
史上最悪のデスペナ さんが書きました:えっと・・・
スレッドハンドル→スレッドを区別するため
いや・・・待てよ・・・
スレッドハンドル→スレッドで使う関数を区別するため・・・?
とすると、プログラムは同じなんだからスレッドハンドルは1つでいいのか・・・な?
とりあえず、多少遅くても良いからノンブロッキングにしましょうか。
スレッド講座からやっていると終わらなさそうです。
Linuxで作る時はスレッドにして下さいね。

[訂正]
TCP/IPだとノンブロッキングだとややこしいですね。
うーん。どうしよう。

[修正案]
スレッドで同期をなるべくしないでスレッドに出来るだけ投げっぱなしで処理をできるように検討してみてはどうでしょうか?
ただ、チャットだと電文だけはスレッド間の共有管理にしないといけませんが。同期イベントなどが使えるでしょう。
いつもクライアント系でサーバー系は経験が無いので大雑把に私も考えているので的外れがあるかも知れません。

ちなみにスレッドハンドルは起動したスレッドの数だけ生成されます。自分でスレッドハンドルを閉じる必要のある使い方とスレッド自身が自分で消滅させるやり方があります。これの使い分けはスレッドの設計次第です。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月02日(木) 22:43
by taketoshi
私が示した例はTCP通信、WinSDKネイティブクライアントでWSAAsyncSelect関数を使って非同期通信をして
1スレッドでS/Cシステムを構築し、構造体を送受信しようとしているサンプルです。

コレを書き進めて1対10ほどのS/Cシステムを構築して現在仕事で使ってます。
クライアントの要求があった場合、その都度300kbくらいのデータを配信しています。


とりあえず、ワンスレッドで1vs1にして構造体の送受信が出来るプログラムを構築してはいかがですか?
そこが出来ればechoバックにしてワンスレッドで1vs多のチャットソフトが作れます。
CUIだとテストプログラム的な1vs1のデータのやり取りまでしか作ったことがないので1vs多はちょっと不明瞭ですが。

スレッドの話は後から勉強すればいいんじゃないでしょうか。

>softyaさん
>一般のWindows7/Vistaは20接続までです。
エッ、まじっすか。クライアントの接続制限は20まで?
少し脱線しますが、自前のシステムは30まで接続受け入れをしていて、テストでは30接続まで受け入れられたのですが
(1から30台までの可変設定にはしてあります)

ちょっとまずいかな。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月02日(木) 22:45
by 史上最悪のデスペナ
softya(ソフト屋) さんが書きました:とりあえず、多少遅くても良いからノンブロッキングにしましょうか。
スレッド講座からやっていると終わらなさそうです。
Linuxで作る時はスレッドにして下さいね。

[訂正]
TCP/IPだとノンブロッキングだとややこしいですね。
うーん。どうしよう。
もう一度スレッドについて調べてきます。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月02日(木) 22:48
by 史上最悪のデスペナ
taketoshi さんが書きました:とりあえず、ワンスレッドで1vs1にして構造体の送受信が出来るプログラムを構築してはいかがですか?
そこが出来ればechoバックにしてワンスレッドで1vs多のチャットソフトが作れます。
CUIだとテストプログラム的な1vs1のデータのやり取りまでしか作ったことがないので1vs多はちょっと不明瞭ですが。

スレッドの話は後から勉強すればいいんじゃないでしょうか。
その手もありますね。とするとechoバックから調べることになりますが。
最終的に
スレッドを頑張る
ワンスレッドで1vs多→マルチスレッド
どちらが為になるのでしょう?

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月02日(木) 22:49
by softya(ソフト屋)
じゃあ、taketoshiさんのものがあるので、それを参考にしてノンブロッキングで行きましょうか。
あと「クライアントの接続制限は20まで?」は聞き及んだだけで勘違いしているかも知れません。少なくとも実験したことはないです。
スレッドはとにかく大変なので、初心者がドはまると1ヶ月ぐらい軽く潰します。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月02日(木) 22:51
by softya(ソフト屋)
史上最悪のデスペナ さんが書きました:その手もありますね。とするとechoバックから調べることになりますが。
最終的に
スレッドを頑張る
ワンスレッドで1vs多→マルチスレッド
どちらが為になるのでしょう?
最適な手法を選択するためには両方を知るべきである。だと私は思います。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月02日(木) 22:53
by 史上最悪のデスペナ
softya(ソフト屋) さんが書きました:じゃあ、taketoshiさんのものがあるので、それを参考にしてノンブロッキングで行きましょうか。
あと「クライアントの接続制限は20まで?」は聞き及んだだけで勘違いしているかも知れません。少なくとも実験したことはないです。
私も20までしか(規約でとか書いてあったような・・・・)接続できないと聞いたことがあります。
softya(ソフト屋) さんが書きました:スレッドはとにかく大変なので、初心者がドはまると1ヶ月ぐらい軽く潰します。
そこまで難しいとは想定外・・・・
もう一度taketoshiさんのプログラムを見に行ってきます

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月02日(木) 23:07
by taketoshi
過去の私のnikkiに1vs多のechoバックサーバーのプログラムを乗っけていました。
このときのプログラムはただの文字列を送信するプログラムで構造体を送受信しているわけではないですが
参考までに、よろしかったらご覧ください。

ただし私も猫でも判るネットワーク本+自前で調べた知識なのでまったくプロではないです。むしろアマです。
やっちゃいけないことやってるかもしれないです。

http://dixq.net/forum/blog.php?u=288&b=1451


>「クライアントの接続制限は20まで?」
そうですか、私も実験したのはサーバと同一PC内でクライアントを起動しまっくって接続したので、
これもテストとは言い難いかもしれませんね。

*ワンスレッドのサーバクライアントシステムを使っていて思いますが
*スレッドへの移行も必要かなぁとは思います。
*(一応スレッドも実装してあるけど、winsockとは無関係のタイマー機能で使ってる)

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月03日(金) 11:21
by 史上最悪のデスペナ
taketoshi さんが書きました:blog.php?u=288&b=1451
こちらは何となくは分かったのですが知らない関数もあり、ちょっと追いかけるのが大変だったので
shiro4aoさんの日記に対する返信内のコードについて質問させていただきます。
taketoshi さんが書きました:

コード:

////////////////////////////////////////////////////////////////////////
//ソケットとクライアント情報を管理する構造体
//
////////////////////////////////////////////////////////////////////////
typedef struct{
	int nId;			//構造体の要素数+1
	SOCKADDR_IN from;	//クライアント情報
	SOCKET s;			//ソケット
}DATA;

////////////////////////////////////////////////////////////////////////
//受信関数
//戻り値:int
////////////////////////////////////////////////////////////////////////
int server::read(WPARAM wParam){
	char szBuffer[256];
		for(i = 0;i < MAX_MEMBER;++i){
			if((SOCKET)wParam == MemData[i].s){
				wsprintf(szOutBuff,"%s",inet_ntoa(MemData[i].from.sin_addr));
			}
		}
		for(i = 0;i < MAX_MEMBER;++i){
			if((SOCKET)wParam == MemData[i].s){	//受信ソケットの照合作業ですこの作業をやらないと何故か接続オーバーしたクライアントからゴミを送りつけられる。必ずソケットは選択受信すること。
				memset(szBuffer,0,sizeof(szBuffer));
				nRet =recv(MemData[i].s,szBuffer,sizeof(szBuffer),0);
				szBuffer[nRet] = '\0';
				wsprintf(szInfoBuff,"%s:%s\r\n",szOutBuff,szBuffer);
				SendMessage(hEdit[1],EM_REPLACESEL,1,(LPARAM)szInfoBuff);
				for(i = 0;i < MAX_MEMBER;++i){//サーバーにきたクライアントの文章を他のクライアントに転送
					if((SOCKET)wParam != MemData[i].s)
						send(MemData[i].s,szInfoBuff,(int)strlen(szInfoBuff),0);
				}
			}
		}
		return 0;
}
同じループを二つに分けている理由です。以下のように(wsprintfは分かりませんが、printfと似たような関数なのでしょう。szOutBuffが何か分かりませんが、受信データのIPアドレスを表示しようとしてるのかな?それか、データ型を指定してコピー?)

コード:

for(i = 0;i < MAX_MEMBER;++i){
	if((SOCKET)wParam == MemData[i].s){
		wsprintf(szOutBuff,"%s",inet_ntoa(MemData[i].from.sin_addr));
	}
}
for(i = 0;i < MAX_MEMBER;++i){
        if((SOCKET)wParam == MemData[i].s){

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月03日(金) 11:48
by softya(ソフト屋)
wsprintfはワイド文字のsprintfでC言語標準ではなくWindowsAPI拡張です。細かいな違いが色々あるようです。
「wsprintf 関数」
http://msdn.microsoft.com/ja-jp/library/cc364872.aspx

C95の正式なのは、swprintfとなります。
「sprintf、_sprintf_l、swprintf、_swprintf_l、__swprintf_l (CRT)」
http://msdn.microsoft.com/ja-jp/library ... s.80).aspx
「C言語関数辞典 - swprintf」
http://www.c-tipsref.com/reference/wchar/swprintf.html

2つのループに分けている理由は特に無いような気も。
あえて言えば文脈的にわかりやすくしただけかな?

[補足]sprintf自体を知らない可能性を忘れてました。
要は数値を書式文字列化などをしたいときに使います。
C++のsstreamなども同様ですね。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月03日(金) 12:11
by softya(ソフト屋)
[マルチスレッドについて] ← 今すぐやらないくて良いですが知識として。

マルチコア/マルチスレッド=並列処理は、これだけで本一冊になるぐらいは奥深いです。
「Amazon.co.jp: 並行コンピューティング技法 ―実践マルチコア/マルチスレッドプログラミング: Clay Breshears, 千住 治郎: 本」

「Amazon.co.jp: マルチコアCPUのための並列プログラミング―並列処理&マルチスレッド入門: 安田 絹子, 飯塚 博道, 青柳 信吾, 小林 林広, 阿部 貴之, フィックスターズ: 本」

まぁ、OpenMpやMPIにも触れているので厚い本になるのですが。

それとWindowsとLinuxではマルチスレッドの方式が違うので概念は同じですが使う関数がまったく違います。

マルチスレッド入門など。
「マルチスレッドでの開発」
http://www.syuhitu.org/other/thread/thread.html
「マルチスレッドプログラミング (POSIX、Win32)」 POSIXはLinuxの方です。
http://www.ops.dti.ne.jp/~allergy/thread/thread.html
「並列処理を行うための基礎知識(Visual C++) - @IT」
http://www.atmarkit.co.jp/fdotnet/bookp ... 01_01.html
「第2回 実行メカニズムの理解に欠かせない「スレッド」の概念 - (新)APIから知るWindowsの仕組み:ITpro」
http://itpro.nikkeibp.co.jp/article/COL ... ST=develop
「第3回 マルチタスクに不可欠な同期の仕組みを学ぶ - (新)APIから知るWindowsの仕組み:ITpro」
http://itpro.nikkeibp.co.jp/article/COL ... 03/273403/

同期については、Windowsの場合クリティカル・セクションやMUTEXやセマフォやイベントなどを使います。
これの細かい話はまた別の機会に。
同期は別のスレッド同士が同じ変数にアクセスする場合に絶対に必要な処理で、失敗すると追跡困難なバグが生まれます。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月03日(金) 16:18
by 史上最悪のデスペナ
softya(ソフト屋) さんが書きました:それとWindowsとLinuxではマルチスレッドの方式が違うので概念は同じですが使う関数がまったく違います。
それは・・・・・・・・
もしかしたらLinuxを使うことは無いかもしれません。もしくはアフィリエイト使ってその日暮でVPS借りるか・・・・・・余裕が出たらまた勉強したいですけどね。
softya(ソフト屋) さんが書きました:同期については、Windowsの場合クリティカル・セクションやMUTEXやセマフォやイベントなどを使います。
これの細かい話はまた別の機会に。
そうですね。取りあえずはシングルスレッドで少人数でテスト出来るようにならなければ。
そう言えば、シングルスレッドで通信を行った場合、何人までが遅延を感じずにいられるんでしょう?

質問がもう2つありました。
taketoshi さんが書きました: そのときは構造体で接続ソケットを管理して、
WinMain関数(間違え、プロシージャでした)のWPARAMに通知をしてきたソケット情報が入っているので
それと照合して、WPARAM以外のソケットに一斉送信するような仕組みにしました。
とのことですが、Win32APIを使わないでwinsock2.hのみで取得する方法は無いでしょうか?
また、recvで受信されるまで処理が止まるとのことですが、あのプログラムではどうやって回避しているのでしょう?

追記:
地理的概算距離30kmで1:1の実験を行ったと述べましたが、その数日後に地理的概算距離200~250km(200kmよりは確実に長いけど、250kmよりは確実に短い、その間のどこか)にて実験したところ、
クライアントがサーバーに接続→サーバーからHELLOを受信&描画
に5秒ほど掛かったようです。
時間掛かりすぎ・・・・・?

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月03日(金) 20:03
by shiro4ao
(うわぁ、知らない間に話が進んでいる…レスが多いので2ページになったのですね。
知らなかった。浦島太郎的な気分・・・・orz)


クライアントのWindowsはライセンスで同時接続数が制限されていますが、
技術的にはいくらでも接続できるようです。(当然リソースの限界はありますが)

現実にはLinuxなど制限のないOSを選ぶというのが正解のような気がしますが、
いきなりLinuxで開発しなくても、はじめはWindowsでもいいと思います。
NT系なら安定していますし、なによりできるところから始める、
のがとっつきやすいと思います。

どうしてもプラットフォームがWinでなければならない場合は
学生さんであれば、DreamSparkをつかって
サーバー版のWindowsを入手する方法もありです。
史上最悪のデスペナ さんが書きました:
taketoshi さんが書きました: そのときは構造体で接続ソケットを管理して、
WinMain関数(間違え、プロシージャでした)のWPARAMに通知をしてきたソケット情報が入っているので
それと照合して、WPARAM以外のソケットに一斉送信するような仕組みにしました。
とのことですが、Win32APIを使わないでwinsock2.hのみで取得する方法は無いでしょうか?
また、recvで受信されるまで処理が止まるとのことですが、あのプログラムではどうやって回避しているのでしょう?
WSAAsyncSelect関数で、受信データが来たらウィンドウプロシージャで処理できるようにしているんだと思います。
通信データがやってくるとウィンドウプロシージャにFD_READメッセージがやってきます。その時にrecv()を呼ぶようにしておけば
受信データが確実にある状態なのですぐにrecv()が処理を返します
つまりrecv()がブロッキングしません。(←説明になっているのかな・・・?これ・・・?)
でも、ウィンドウメッセージは少なからず飛び交っているので、recv()のバッファを1バイトとかにすると
すごい勢いでCPUを食い潰して笑えます。

遅延の問題はうーんなんとも自分には・・・?
ただ、250km前後で5秒ほどかかるというのはどうなんでしょう?
ちょっと長すぎる気がしますが。
(検索エンジンなんて、世界のどこにあるやも知れぬサーバから検索結果をあっという間に返してくれるし)

マルチスレッドorマルチタスクは処理系によって大きく変わります。
関数の名前がどうこうというレベルではなく、考え方から大きく違っています。
なので、Windows用とLinux用では全く違うソースを作らなければなりません。
(条件コンパイルしたり、うまく抽象化するようなラッパを作れればいいのですが、
スレッドの同期とか考えると技術的に可能か自分にはわかりません・・・・)

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月03日(金) 20:19
by softya(ソフト屋)
250km前後で5秒ほどかかるというのはどうなんでしょう?
たぶん、アメリカ相手でも1秒もかかりません。なのでUDPと同じパケット送信エラーが発生しているんだと思います。
TCP/IPの場合は自動的にリトライされるので気づかない間に何度も再送された結果が5秒なのではないでしょうか?
これはwireSharkでパケットを監視していれば分かります。
shiro4ao さんが書きました:マルチスレッドorマルチタスクは処理系によって大きく変わります。
関数の名前がどうこうというレベルではなく、考え方から大きく違っています。
なので、Windows用とLinux用では全く違うソースを作らなければなりません。
(条件コンパイルしたり、うまく抽象化するようなラッパを作れればいいのですが、
スレッドの同期とか考えると技術的に可能か自分にはわかりません・・・・)
両方共作ったことがありますが根本の根本は同じです。
ただ、名前やら管理の方法は違いますし、同期系の関数も同じものが無いってことはあります。
LinuxでPOSIX だとMUTEXやセマフォはあります。条件変数(POSIX)と言うのが同じのが無いですが、Windowsのイベントが近いかな?

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月03日(金) 20:46
by shiro4ao
softya(ソフト屋) さんが書きました:
shiro4ao さんが書きました:マルチスレッドorマルチタスクは処理系によって大きく変わります。
関数の名前がどうこうというレベルではなく、考え方から大きく違っています。
なので、Windows用とLinux用では全く違うソースを作らなければなりません。
(条件コンパイルしたり、うまく抽象化するようなラッパを作れればいいのですが、
スレッドの同期とか考えると技術的に可能か自分にはわかりません・・・・)
両方共作ったことがありますが根本の根本は同じです。
ただ、名前やら管理の方法は違いますし、同期系の関数も同じものが無いってことはあります。
LinuxでPOSIX だとMUTEXやセマフォはあります。条件変数(POSIX)と言うのが同じのが無いですが、Windowsのイベントが近いかな?
おお・・・根本は同じなのですね・・・・
forkの動きとかで「違うなあ、どうしよう???」と感じていいたのですが、
結構みてくれに惑わされていたのかもしれません。むずかしい・・・

書き忘れていたのですが、最終目標の構造体の送信はどのようにするのでしょうか?
うろ覚えなのですが、構造体を送る場合、ソースでは同じ構造体でも、
処理系やコンパイラの違いでパディングに違いができるので同じものにならない
という話を聞いたことがあるのですが、そうすると、一手間掛かりそうです。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月03日(金) 20:53
by softya(ソフト屋)
shiro4ao さんが書きました:おお・・・根本は同じなのですね・・・・
forkの動きとかで「違うなあ、どうしよう???」と感じていいたのですが、
結構みてくれに惑わされていたのかもしれません。むずかしい・・・
プロセスは確かに違いますね。
まぁ、考え方の問題なので関数に包んじゃえばOKです。
POSIX方は、まだ近い気も・・・。
shiro4ao さんが書きました:書き忘れていたのですが、最終目標の構造体の送信はどのようにするのでしょうか?
うろ覚えなのですが、構造体を送る場合、ソースでは同じ構造体でも、
処理系やコンパイラの違いでパディングに違いができるので同じものにならない
という話を聞いたことがあるのですが、そうすると、一手間掛かりそうです。
パディングはVC++もgccも変える方法があるので、それで解決できると思います。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月03日(金) 21:09
by beatle
Boost.Threadを使えば,WindowsでもLinuxでもソース互換なマルチスレッドプログラムを作れますよ.

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月03日(金) 21:45
by taketoshi
う、私が回答する必要もない程解説頂いてる。ありがとうございます。
ほとんどご指摘いただいている通りのコードです。

ループを分けている理由は特にありません、文脈を解りやすくしただけです。
一度目のfor文で発言先のIPアドレスを抜き取り、「IPアドレス(Outbuff):発言文(Infobuff)」の形に文章を成形しているだけです。

また、WSAAsyncSelect関数で非同期モードにしてメッセージを受け取っています
ただ、この関数はGUIで使うための関数みたいです。CUIで非同期化はやったことないので不明ですが、ググったところselect関数などを使うようです。

GUIとCUIのどちらで開発されているか書いてないので解らないですが
WSAAsyncSelect関数はWINAPIではなくWinsockの関数になりますよ。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月04日(土) 18:39
by 史上最悪のデスペナ
返信が遅くなってしまい申し訳ありません。
shiro4ao さんが書きました:WSAAsyncSelect関数で、受信データが来たらウィンドウプロシージャで処理できるようにしているんだと思います。
通信データがやってくるとウィンドウプロシージャにFD_READメッセージがやってきます。その時にrecv()を呼ぶようにしておけば
受信データが確実にある状態なのですぐにrecv()が処理を返します
つまりrecv()がブロッキングしません。
taketoshi さんが書きました:また、WSAAsyncSelect関数で非同期モードにしてメッセージを受け取っています
ただ、この関数はGUIで使うための関数みたいです。CUIで非同期化はやったことないので不明ですが、ググったところselect関数などを使うようです。

GUIとCUIのどちらで開発されているか書いてないので解らないですが
WSAAsyncSelect関数はWINAPIではなくWinsockの関数になりますよ。
GUIとCUIのどちらというのを書いてなかったのはその用語自体も知らなかったのと、コンソールかウィンドウかで違いがあるとは思わなかったためです。
サーバー側はCUI、クライアント側はGUIのつもりです。
取り合えず、WSAAsyncSelectでやってみて駄目だったらselectでやってみます。
softya(ソフト屋) さんが書きました:これはwireSharkでパケットを監視していれば分かります。
WireSharkの存在になかなか思い当たりません。教えていただいて、「あ、そういえば」と思い出すんですよね・・・・
softya(ソフト屋) さんが書きました:
shiro4ao さんが書きました:書き忘れていたのですが、最終目標の構造体の送信はどのようにするのでしょうか?
うろ覚えなのですが、構造体を送る場合、ソースでは同じ構造体でも、
処理系やコンパイラの違いでパディングに違いができるので同じものにならない
という話を聞いたことがあるのですが、そうすると、一手間掛かりそうです。
パディングはVC++もgccも変える方法があるので、それで解決できると思います。
取り合えず18日の試験は構造体まで間に合わない予感・・・・・
かつてのトピック「どんなデータ型でも~」や「ポインタがよく~」で教えていただいた「構造体→バイナリデータ」変換でバイナリデータを送るので、大丈夫ですよね?

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月04日(土) 18:57
by softya(ソフト屋)
史上最悪のデスペナ さんが書きました:取り合えず18日の試験は構造体まで間に合わない予感・・・・・
かつてのトピック「どんなデータ型でも~」や「ポインタがよく~」で教えていただいた「構造体→バイナリデータ」変換でバイナリデータを送るので、大丈夫ですよね?
かなりムダが多いですが文字列化16進数データで送ればとりあえずは大丈夫です。
結局のところ構造体を復元するには、それなりの知識は必要なので大丈夫かどうかは作ってもらわないと分かりませんよ。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月05日(日) 12:45
by 史上最悪のデスペナ

コード:

while(1)
{
	//接続受信
	{
		memcpy( &ConnectFds, &SubConnectFds, sizeof(fd_set) );
		select( 0, &ConnectFds, NULL, NULL, NULL );
		if ( FD_ISSET(RecvSock, &ConnectFds) )
		{
			SendSockBuf = accept(RecvSock, (struct sockaddr *)&Send, &len);
				if( SendSockBuf != INVALID_SOCKET)
				{
					MAXClient++;
					SendSock.push_back( SendSockBuf );
					FD_SET( SendSock[MAXClient], &SubSendFds);
				}
		}
	}

	//送受信
	{
		for( int i=0 ; i<=MAXClient ; i++ )
		{
			memcpy( &SendFds, &SubSendFds, sizeof(fd_set) );
			select( 0, &SendFds, NULL, NULL, NULL );
			if( FD_ISSET(SendSock[i], &SendFds) )
			{
				char buf[32];
				// サーバからデータを受信
				memset(buf, 0, sizeof(buf));
				recv( SendSock[i], buf, sizeof(buf), 0 );
					printf("%s\n", buf);
				for( int j=0 ; j<=MAXClient ; j++ )
				{
					if( i != j )
						send( SendSock[j], buf, sizeof(buf), 0 );
				}
			}
		}
	}
}
selectを使い、このようにしてみたのですが、受信が全く出来ませんでした。
printf("socket : %d\n", WSAGetLastError());
でところどころにエラーコード表示を入れてみたのですが、値はすべて0でした。
ブレイクポイントを作成してちまちまと追って行ったところ、どうもwhileループ2回目で、「接続受信」におけるselectで動作が止まってそれ以上先に進んでいないようです。
どうしたらいいでしょうか?

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月05日(日) 13:00
by softya(ソフト屋)
Selectでタイムアウト時間をしてしていないので待っているのでは?すぐ抜けたいならタイムアウト時間を指定しないと。
それと1回抜けたのなら、なにか受信しているはずが?

ソースを見て思ったのですがサンプルを見て改造しているだけでselectとか個々の関数の機能を調べていないのでは?
書籍を買えない以上は例え英語サイトでもちゃんと知らべて機能を理解して使いましょう。あとエラー処理をちゃんと書きましょう。

「select function」
http://msdn.microsoft.com/en-us/library ... s.85).aspx
google翻訳とかyahooWEB翻訳とか便利のものもあります。
http://translate.google.co.jp/
http://honyaku.yahoo.co.jp/url/

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月05日(日) 13:09
by 史上最悪のデスペナ
softya(ソフト屋) さんが書きました:Selectでタイムアウト時間をしてしていないので待っているのでは?すぐ抜けたいならタイムアウト時間を指定しないと。
softya(ソフト屋) さんが書きました:ソースを見て思ったのですがサンプルを見て改造しているだけでselectとか個々の関数の機能を調べていないのでは?
一応、調べてはいるのですが・・・・・・
今回も、タイムアウトを指定していない、と教えていただくと「あ、第5引数のあれか」とパッと出てくるのですが、確かにサンプルを見て「関数を調べて」改造しているだけなのでそれを使う場面だということに思い至りません。しっかりと関数の機能を理解していないだけなのでしょうが・・・・・・・
softya(ソフト屋) さんが書きました:それと1回抜けたのなら、なにか受信しているはずが?
あ、そうです。接続が確立したらずっと「HELLO」と送り続けるクライアントを用いると、一つのみ受信します。
(上の回答のは、セーブしておいたのをロードして書き換えたり付け足したりしたものなので、「全く受信しない」というのは原因が分かって回避できたんでした)

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月05日(日) 13:39
by softya(ソフト屋)
史上最悪のデスペナ さんが書きました:
softya(ソフト屋) さんが書きました:Selectでタイムアウト時間をしてしていないので待っているのでは?すぐ抜けたいならタイムアウト時間を指定しないと。
softya(ソフト屋) さんが書きました:ソースを見て思ったのですがサンプルを見て改造しているだけでselectとか個々の関数の機能を調べていないのでは?
一応、調べてはいるのですが・・・・・・
今回も、タイムアウトを指定していない、と教えていただくと「あ、第5引数のあれか」とパッと出てくるのですが、確かにサンプルを見て「関数を調べて」改造しているだけなのでそれを使う場面だということに思い至りません。しっかりと関数の機能を理解していないだけなのでしょうが・・・・・・・
softya(ソフト屋) さんが書きました:それと1回抜けたのなら、なにか受信しているはずが?
あ、そうです。接続が確立したらずっと「HELLO」と送り続けるクライアントを用いると、一つのみ受信します。
(上の回答のは、セーブしておいたのをロードして書き換えたり付け足したりしたものなので、「全く受信しない」というのは原因が分かって回避できたんでした)
戻り値のエラー処理をちゃんと書き加える場合もう一度関数をちゃんと調べると思うんですよ。
何でもかんでもWSAGetLastError()頼みというのも・・・。

それと今のコードだとaccept()したら接続のままなのでselectで待っても新しい接続にはならないと思いますが?

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月05日(日) 16:44
by 史上最悪のデスペナ
softya(ソフト屋) さんが書きました:それと今のコードだとaccept()したら接続のままなのでselectで待っても新しい接続にはならないと思いますが?
clearsocketを入れることにより、正常に(?)動作しました。
softya(ソフト屋) さんが書きました:戻り値のエラー処理をちゃんと書き加える場合もう一度関数をちゃんと調べると思うんですよ。
何でもかんでもWSAGetLastError()頼みというのも・・・。
WSAGetLastError()はコピーして貼り付けるだけでいいので楽だからそっちを使っちゃうんですよね。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月07日(火) 22:08
by 史上最悪のデスペナ
薦めていただいた本を図書館で借りて読んだのですが、結局良く分かりませんでした。

しかし、試行錯誤の末なんとか複数クライアントとの通信に成功しました。
相変わらず通信が確立されるのに5秒近くかかってますが。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月16日(木) 18:51
by 史上最悪のデスペナ
1週間ほどご無沙汰しておりましたが、皆様いかがお過ごしでしょうか。

中略

vectorで作った動的配列をeraseしたいのですが、上手く出来ません。
単純な

コード:

vector<int> a;
    a.push_back(5);
    a.push_back(24);
    a.push_back(17);

vector<int>::iterator it = a.begin();

it = a.erase(a+1);
のようなものであればちゃんと思い通りに動くのですが、
SOCKETだと動かず、「範囲外のとこを削除しようとしています」のような英文が現れます。

コード:

#include <stdio.h>
#include <winsock2.h>
#include <vector>
using namespace std;

int main()
{
	int Error;
	struct timeval tv;

	//winsock2の初期化
	WSADATA wsaData;
		Error = WSAStartup( MAKEWORD(2,0), &wsaData );
			switch( Error )
			{
				case WSASYSNOTREADY:
					printf( "ネットワークサブシステムがネットワークへの接続準備ができていません" );
					Sleep( 1000 );
					return -1;
					break;

				case WSAVERNOTSUPPORTED:
					printf( "要求した WinSock のバージョンはこのシステムで提供されていません" );
					Sleep( 1000 );
					return -1;
					break;

				case WSAEINPROGRESS:
					printf( "ブロッキング操作の実行中、またはサービスプロバイダがコールバック関数を処理しています" );
					Sleep( 1000 );
					return -1;
					break;

				case WSAEPROCLIM:
					printf( "WinSock が同時に処理できる最大プロセスに達成しました" );
					Sleep( 1000 );
					return -1;
					break;

				case WSAEFAULT:
					printf( "lpWSAData は有効なポインタではありません" );
					Sleep( 1000 );
					return -1;
					break;
			}


	//ソケットの作成
	SOCKET RecvSock = socket(AF_INET, SOCK_STREAM, 0);
		if( RecvSock == INVALID_SOCKET )
		{
			printf( "ソケット作成失敗" );
			Sleep( 1000 );
			return -1;
		}

	SOCKET SendSockBuf;
	vector<SOCKET> SendSock;

	fd_set ConnectFds, SubConnectFds;
	fd_set SendFds, SubSendFds;

	int MAXClient = -1;

	struct sockaddr_in Recv, Send;

	int len;

	//ソケットの設定
	Recv.sin_family = AF_INET;//IPv4。IPv6ならAF_INET6
	Recv.sin_port = htons(53135);//ポート番号
	Recv.sin_addr.S_un.S_addr = INADDR_ANY;//どのアドレスから接続してもおk
		Error = bind(RecvSock, (struct sockaddr *)&Recv, sizeof(Recv) );
			if( Error == SOCKET_ERROR )
			{
				printf( "バインド失敗" );
				Sleep( 1000 );
				return -1;
			}

	FD_ZERO( &SubConnectFds );
		FD_SET( RecvSock, &SubConnectFds );

	FD_ZERO( &SubSendFds );

	//TCPクライアントからの接続要求を受け付ける
	len = sizeof( Send );
	//TCPクライアントからの接続要求を待てる状態にする
	listen( RecvSock, 5 );
		if( Error == SOCKET_ERROR )
		{
			printf( "リッスン失敗" );
			Sleep( 1000 );
			return -1;
		}

	while(1)
	{
		//0秒でタイムアウト
		tv.tv_sec = 0;
		tv.tv_usec = 0;

		memcpy( &ConnectFds, &SubConnectFds, sizeof(fd_set) );
		select( 0, &ConnectFds, NULL, NULL, &tv );
		if ( FD_ISSET(RecvSock, &ConnectFds) )
		{
			SendSockBuf = accept(RecvSock, (struct sockaddr *)&Send, &len);
				if( SendSockBuf != INVALID_SOCKET)
				{
					MAXClient++;

					SendSock.push_back( SendSockBuf );
					FD_SET( SendSock[MAXClient], &SubSendFds);
					closesocket(RecvSock);

					//ソケットの作成
					SOCKET RecvSock = socket(AF_INET, SOCK_STREAM, 0);
						if( RecvSock == INVALID_SOCKET )
						{
							printf( "ソケット作成失敗" );
							Sleep( 1000 );
							return -1;
						}
					Error = bind(RecvSock, (struct sockaddr *)&Recv, sizeof(Recv) );
						if( Error == SOCKET_ERROR )
						{
							printf( "バインド失敗" );
							Sleep( 1000 );
							return -1;
						}
					listen( RecvSock, 5 );
						if( Error == SOCKET_ERROR )
						{
							printf( "リッスン失敗" );
							Sleep( 1000 );
							return -1;
						}
				}
		}

		for( int i=0 ; i<MAXClient+1 ; i++ )
		{
			memcpy( &SendFds, &SubSendFds, sizeof(fd_set) );
			select( 0, &SendFds, NULL, NULL, &tv );
			if( FD_ISSET(SendSock[i], &SendFds) )
			{
				char buf[101];
				// サーバからデータを受信
				memset(buf, 0, sizeof(buf));
				recv( SendSock[i], buf, sizeof(buf), 0 );
					printf("%s\n", buf);
				for( int j=0 ; j<MAXClient+1 ; j++ )
				{
					int Error = send( SendSock[j], buf, sizeof(buf), 0 );
						if( Error == SOCKET_ERROR )
						{
							closesocket(SendSock[j]);
							SendSock.erase( SendSock.begin() );
						}
				}
			}
		}
	}

	closesocket(RecvSock);

	//winsock2の終了処理
	WSACleanup();

	return 0;
}
最後から数行目にeraseがあります。
やりたいことは、ソケットが何らかの理由でつながらなくなった場合、
それを削除して配列を切り詰めたい、です

追記:何故かTabが全部消えてますね・・・・・修正中ですスポイルは諦め

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月16日(木) 18:58
by softya(ソフト屋)
無茶苦茶見づらかったのでcodeタグで表示します。

コード:

#include <stdio.h>
#include <winsock2.h>
#include <vector>
using namespace std;

int main()
{
	int Error;
	struct timeval tv;

	//winsock2の初期化
	WSADATA wsaData;
		Error = WSAStartup( MAKEWORD(2,0), &wsaData );
			switch( Error )
			{
				case WSASYSNOTREADY:
					printf( "ネットワークサブシステムがネットワークへの接続準備ができていません" );
					Sleep( 1000 );
					return -1;
					break;

				case WSAVERNOTSUPPORTED:
					printf( "要求した WinSock のバージョンはこのシステムで提供されていません" );
					Sleep( 1000 );
					return -1;
					break;

				case WSAEINPROGRESS:
					printf( "ブロッキング操作の実行中、またはサービスプロバイダがコールバック関数を処理しています" );
					Sleep( 1000 );
					return -1;
					break;

				case WSAEPROCLIM:
					printf( "WinSock が同時に処理できる最大プロセスに達成しました" );
					Sleep( 1000 );
					return -1;
					break;

				case WSAEFAULT:
					printf( "lpWSAData は有効なポインタではありません" );
					Sleep( 1000 );
					return -1;
					break;
			}


	//ソケットの作成
	SOCKET RecvSock = socket(AF_INET, SOCK_STREAM, 0);
		if( RecvSock == INVALID_SOCKET )
		{
			printf( "ソケット作成失敗" );
			Sleep( 1000 );
			return -1;
		}

	SOCKET SendSockBuf;
	vector<SOCKET> SendSock;

	fd_set ConnectFds, SubConnectFds;
	fd_set SendFds, SubSendFds;

	int MAXClient = -1;

	struct sockaddr_in Recv, Send;

	int len;

	//ソケットの設定
	Recv.sin_family = AF_INET;//IPv4。IPv6ならAF_INET6
	Recv.sin_port = htons(53135);//ポート番号
	Recv.sin_addr.S_un.S_addr = INADDR_ANY;//どのアドレスから接続してもおk
		Error = bind(RecvSock, (struct sockaddr *)&Recv, sizeof(Recv) );
			if( Error == SOCKET_ERROR )
			{
				printf( "バインド失敗" );
				Sleep( 1000 );
				return -1;
			}

	FD_ZERO( &SubConnectFds );
		FD_SET( RecvSock, &SubConnectFds );

	FD_ZERO( &SubSendFds );

	//TCPクライアントからの接続要求を受け付ける
	len = sizeof( Send );
	//TCPクライアントからの接続要求を待てる状態にする
	listen( RecvSock, 5 );
		if( Error == SOCKET_ERROR )
		{
			printf( "リッスン失敗" );
			Sleep( 1000 );
			return -1;
		}

	while(1)
	{
		//0秒でタイムアウト
		tv.tv_sec = 0;
		tv.tv_usec = 0;

		memcpy( &ConnectFds, &SubConnectFds, sizeof(fd_set) );
		select( 0, &ConnectFds, NULL, NULL, &tv );
		if ( FD_ISSET(RecvSock, &ConnectFds) )
		{
			SendSockBuf = accept(RecvSock, (struct sockaddr *)&Send, &len);
				if( SendSockBuf != INVALID_SOCKET)
				{
					MAXClient++;

					SendSock.push_back( SendSockBuf );
					FD_SET( SendSock[MAXClient], &SubSendFds);
					closesocket(RecvSock);

					//ソケットの作成
					SOCKET RecvSock = socket(AF_INET, SOCK_STREAM, 0);
						if( RecvSock == INVALID_SOCKET )
						{
							printf( "ソケット作成失敗" );
							Sleep( 1000 );
							return -1;
						}
					Error = bind(RecvSock, (struct sockaddr *)&Recv, sizeof(Recv) );
						if( Error == SOCKET_ERROR )
						{
							printf( "バインド失敗" );
							Sleep( 1000 );
							return -1;
						}
					listen( RecvSock, 5 );
						if( Error == SOCKET_ERROR )
						{
							printf( "リッスン失敗" );
							Sleep( 1000 );
							return -1;
						}
				}
		}

		for( int i=0 ; i<MAXClient+1 ; i++ )
		{
			memcpy( &SendFds, &SubSendFds, sizeof(fd_set) );
			select( 0, &SendFds, NULL, NULL, &tv );
			if( FD_ISSET(SendSock[i], &SendFds) )
			{
				char buf[101];
				// サーバからデータを受信
				memset(buf, 0, sizeof(buf));
				recv( SendSock[i], buf, sizeof(buf), 0 );
					printf("%s\n", buf);
				for( int j=0 ; j<MAXClient+1 ; j++ )
				{
					int Error = send( SendSock[j], buf, sizeof(buf), 0 );
						if( Error == SOCKET_ERROR )
						{
							closesocket(SendSock[j]);
							SendSock.erase( SendSock.begin() );
						}
				}
			}
		}
	}

	closesocket(RecvSock);

	//winsock2の終了処理
	WSACleanup();

	return 0;
}

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月16日(木) 19:02
by Fimbul
[編集]
私の勘違いでした。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月16日(木) 19:04
by softya(ソフト屋)
eraseして行ったら、MAXClientの数は減るはずだけど?
あと先頭を消すことで前に詰まる添字番号のズレとか対象しているように見えないですよ。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月16日(木) 19:55
by 史上最悪のデスペナ
softya(ソフト屋) さんが書きました:eraseして行ったら、MAXClientの数は減るはずだけど?
あ、そうですね。MAXClient--;を加えたら上手くいきました。eraseの部分が間違ってるのかとそこばっかり見てました。
全体を流して見たときも全然気付きませんでしたしorz
softya(ソフト屋) さんが書きました:あと先頭を消すことで前に詰まる添字番号のズレとか対象しているように見えないですよ。
えっと、どういうことでしょう?

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月16日(木) 20:46
by softya(ソフト屋)
史上最悪のデスペナ さんが書きました:
softya(ソフト屋) さんが書きました:あと先頭を消すことで前に詰まる添字番号のズレとか対象しているように見えないですよ。
えっと、どういうことでしょう?
対象=対処です。失礼しました。
で、for( int j=0 ; j<MAXClient+1 ; j++ )で回してますが、j番目がSOCKET_ERRORになると無条件に先頭をけしているので2つ問題があります。
1.SOCKET_ERRORとなったj番目を消していない。SendSock.erase( SendSock.begin() );では先頭が消える。
2.次はj+1番目を処理するが先頭が消えているので実際に次に処理するのは消す前のj+2番目の要素。
となります。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月16日(木) 21:02
by 史上最悪のデスペナ
あ、ごめんなさい。編集ミスです。最初のやつすら消せなかったときの名残が残ったままでした。
実際には
SendSock.erase( SendSock.begin()+j );
とやっています。
softya(ソフト屋) さんが書きました:2.次はj+1番目を処理するが先頭が消えているので実際に次に処理するのは消す前のj+2番目の要素。
となります。
なるほど。これは思い至りませんでした。直してきます。
+jしてる場合は必要ありませんでしたね。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月16日(木) 21:57
by 史上最悪のデスペナ
taketoshi さんが書きました: http://dixq.net/forum/viewtopic.php?t=7917

一年前に私が同一の質問をした内容です。参考になると思います。
やっと構造体まで来ました。上のリンクではエンディアンについて考慮してないように思ったのですがどうなんでしょう?
考慮しなくても(大抵は)大丈夫なのでしょうか?(大抵・・・・WIndowsXP SP3以降を使用する場合)
(同一PCでは構造体の送信が成功しました。)

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月16日(木) 22:14
by softya(ソフト屋)
史上最悪のデスペナ さんが書きました:あ、ごめんなさい。編集ミスです。最初のやつすら消せなかったときの名残が残ったままでした。
実際には
SendSock.erase( SendSock.begin()+j );
とやっています。
softya(ソフト屋) さんが書きました:2.次はj+1番目を処理するが先頭が消えているので実際に次に処理するのは消す前のj+2番目の要素。
となります。
なるほど。これは思い至りませんでした。直してきます。
+jしてる場合は必要ありませんでしたね。
本当に大丈夫ですか?
1つ飛ばしているように見えるんですが。

サンプルコード

コード:

#include <iostream>
#include <vector>
using namespace std;

int main()
{
	vector<int> intvec;
	
	//	初期化
	int max = 10;
	for( int i=0 ; i<max ; i++ ) {
		intvec.push_back( i );
	}
	
	//	処理のシュミレート
	for( int i=0 ; i<max ; i++ ) {
		cout << intvec[i] << endl;
		if( i==5 ) {
			max--;
			intvec.erase( intvec.begin()+i );
		}
	}
}
実行結果。6がありません。
0
1
2
3
4
5
7
8
9
史上最悪のデスペナ さんが書きました:やっと構造体まで来ました。上のリンクではエンディアンについて考慮してないように思ったのですがどうなんでしょう?
考慮しなくても(大抵は)大丈夫なのでしょうか?(大抵・・・・WIndowsXP SP3以降を使用する場合)
(同一PCでは構造体の送信が成功しました。)
古いMACとか特殊なサーバーとかPS3サーバーとかを使わない限りは気にしなくて良いです。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月17日(金) 18:42
by 史上最悪のデスペナ
softya(ソフト屋) さんが書きました:実行結果。6がありません。
0
1
2
3
4
5
7
8
9
えっと、それで大丈夫です。表示したのは配列の中身なので。

コード:

    //  処理のシュミレート
    for( int i=0 ; i<max ; i++ ) {
        if( i==5 ) {
            max--;
            intvec.erase( intvec.begin()+i );
        }
       printf("%d,%d\n", i, intvec[i]);
    }
cout << intvec << endl;
を、添え字の値も表示するために、やり方知らないので削除してprintfで最後に表示するようにしたところ
0,0
1,1
2,2
3,3
4,4
5,6
6,7
7,8
8,9
とちゃんと表示されました

softya(ソフト屋) さんが書きました:古いMACとか特殊なサーバーとかPS3サーバーとかを使わない限りは気にしなくて良いです。

ありがとうございます。
では、一応解決をしたということで、将来需要があったときのためにクライアントコードとサーバーコードを下に貼り付けようと思います

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月17日(金) 18:50
by softya(ソフト屋)
すっごくダメですよ。もう一度よく考えてみて下さい。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月17日(金) 19:05
by softya(ソフト屋)
もう一度サンプルコードです。

コード:

#include <stdio.h>
#include <vector>
using namespace std;

int main()
{
	vector<int> intvec;
	
	//	初期化
	int max = 10;
	for( int i=0 ; i<max ; i++ ) {
		intvec.push_back( i );
	}
	
    //  処理のシュミレート
    for( int i=0 ; i<max ; i++ ) {
        printf("i=%d,intvec[%d] => send process \n", i, intvec[i]);
        if( i==5 ) {
            max--;
            intvec.erase( intvec.begin()+i );
        }
    }
}
結果です。全部で10個の要素があったのに9個しか処理されません。つまり1個処理をスキップしています。
途中で削ろうとも10個の要素があったら10回ループしないのは間違っているのです。

コード:

i=0,intvec[0] => send process
i=1,intvec[1] => send process
i=2,intvec[2] => send process
i=3,intvec[3] => send process
i=4,intvec[4] => send process
i=5,intvec[5] => send process
i=6,intvec[7] => send process
i=7,intvec[8] => send process
i=8,intvec[9] => send process
で、そもそもループ途中で要素を削るのは色々とバグの原因となるのでお勧めしません。
プロであれば出来れば避ける処理と言ったほうが良いでしょう。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月17日(金) 19:13
by 史上最悪のデスペナ
ん~・・・・・何故いけないのか考えても分かりませんが、本来こうなればいいのでしょうか?

コード:

i=0,intvec[0] => send process
i=1,intvec[1] => send process
i=2,intvec[2] => send process
i=3,intvec[3] => send process
i=4,intvec[4] => send process
i=5,intvec[5] => send process
i=5,intvec[6] => send process←これがスキップされてる
i=6,intvec[7] => send process
i=7,intvec[8] => send process
i=8,intvec[9] => send process

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月17日(金) 19:17
by softya(ソフト屋)
史上最悪のデスペナ さんが書きました:ん~・・・・・何故いけないのか考えても分かりませんが、本来こうなればいいのでしょうか?
それは、sendされない相手があっても良いのでしょうか?

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月17日(金) 19:34
by softya(ソフト屋)
全要素が処理されるコードです。
まずいのでif( i==5 ) {をif( intvec==5 ) {に変更。

コード:

#include <stdio.h>
#include <vector>
using namespace std;

int main()
{
	vector<int> intvec;
	
	//	初期化
	int max = 10;
	for( int i=0 ; i<max ; i++ ) {
		intvec.push_back( i );
	}
#if 0
    //  処理のシュミレート
    for( int i=0 ; i<max ; i++ ) {
        printf("i=%d,intvec[%d] => send process \n", i, intvec[i]);
        if( intvec[i]==5 ) {
            max--;
            intvec.erase( intvec.begin()+i );
        }
    }
#else
    //  処理のシュミレート
    int i=0;
    while( i<max ) {
        printf("i=%d,intvec[%d] => send process \n", i, intvec[i]);
        if( intvec[i]==5 ) {
            max--;
            intvec.erase( intvec.begin()+i );
        } else {
			i++;
		}
    }
#endif
}
結果

コード:

i=0,intvec[0] => send process
i=1,intvec[1] => send process
i=2,intvec[2] => send process
i=3,intvec[3] => send process
i=4,intvec[4] => send process
i=5,intvec[5] => send process
i=5,intvec[6] => send process
i=6,intvec[7] => send process
i=7,intvec[8] => send process
i=8,intvec[9] => send process

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月17日(金) 20:47
by 史上最悪のデスペナ
softya(ソフト屋) さんが書きました:それは、sendされない相手があっても良いのでしょうか?
Send()でエラー(何らかの理由で切断された)だったら強制的にソケット削除なので、
今回の例で言うところの
i=5,intvec[5] => send process
は、送信する必要ないのではないかと思ったのです。

ループ文でずっと回すところはWhileを使い、「~まで」という区切りがある場合forを今まで使っていたので
今回のようにwhileを使うという発想に到りませんでした。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月17日(金) 21:15
by softya(ソフト屋)
分かってもらえてないかも。
intvec[5] => send processはエラーに成ったので削除して良いと思いますが、その煽りを食らってintvec[6] => send processが行われなくっているというのが問題です。

つまり
intvec[5] => send process => エラー削除
intvec[6] => send process => vectorから一つ削除されたので内容が前に詰まって実行されない
どういう事かというとi=5で添字5のソケットがエラーに成った場合、添字6に入っていたソケットは削除により前に移動して添字5になります。
この状態でforループでiがひとつ進むため次はi=6ですよね。i=6で元々添字7がsend実行されてi=5のソケット(元々は添字6)はsendされないままとなるのです。
分かりますか?

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月17日(金) 21:38
by 史上最悪のデスペナ
すみません・・・・・・
今、やっと理解しました。
whileを使わない場合、既に挙げたコードを使用するならば

コード:

for( int j=0 ; j<MAXClient+1 ; j++ )
{
    int Error = send( SendSock[j], buf, sizeof(buf), 0 );
        if( Error == SOCKET_ERROR )
        {
            MAXClient--;
            closesocket(SendSock[j]);
            SendSock.erase( SendSock.begin() );
            send( SendSock[j], buf, sizeof(buf), 0 ); //←これを追加
        }
}
としないといけないんですね。

ふと思ったので、Winsockのプログラムに関することなのでここで質問させていただきます
ここをなどで
データ部 データ IPなどの上位層パケットが入る。46~1500Byte(IEEE802.2では42~1497Byte)
と書かれていますが、IPv4の場合IPアドレスは4Byteなので
46~1500Byte→42~1496Byte
42~1497Byte→38~1493Byte
内のデータであれば分割されることなく、一回のrecvで受け取れると考えていいのでしょうか?

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月17日(金) 21:55
by softya(ソフト屋)
史上最悪のデスペナ さんが書きました:すみません・・・・・・
今、やっと理解しました。
whileを使わない場合、既に挙げたコードを使用するならば

コード:

for( int j=0 ; j<MAXClient+1 ; j++ )
{
    int Error = send( SendSock[j], buf, sizeof(buf), 0 );
        if( Error == SOCKET_ERROR )
        {
            MAXClient--;
            closesocket(SendSock[j]);
            SendSock.erase( SendSock.begin() );
            send( SendSock[j], buf, sizeof(buf), 0 ); //←これを追加
        }
}
としないといけないんですね。
そうすると2つ目のsendのエラー処理がされないのでマズイです。
どうも、ちゃんと処理の流れを見れていない様なので、vectorで無理をせず固定配列にしてフラグ管理したほうが良いと思います。

コード:

#include <stdio.h>
using namespace std;

typedef int SOCKET_t; //シミュレーションなので、
typedef struct {
	bool bActive;	//有効
	bool bError;    //エラーになる。
	SOCKET_t socket;
} send_t;

static const int MAX = 10;
int main()
{
	send_t SendSock[MAX];
	vector<send_t> SendSock;

	//  初期化
	for( int i = 0 ; i < MAX ; i++ ) {
		SendSock[i].bActive = true;
		SendSock[i].bError = false;
		SendSock[i].socket = i;
	}
	//  エラーシュミレートする。
	SendSock[5].bError = true;
	SendSock[6].bError = true;

	//  処理のシュミレート
	for( int i = 0 ; i < MAX ; i++ ) {
		if( SendSock[i].bActive ) {
			printf( " i=%d,SendSock[%d] => send process \n", i, SendSock[i].socket );
			if( SendSock[i].bError ) {
				//	フラグで削除する。
				printf( "  delelte SendSock[%d]\n",SendSock[i].socket );
				SendSock[i].bActive = false; //← この添字番号を再利用可能にする
			}
		}
	}
}
史上最悪のデスペナ さんが書きました: ふと思ったので、Winsockのプログラムに関することなのでここで質問させていただきます
ここをなどで
データ部 データ IPなどの上位層パケットが入る。46~1500Byte(IEEE802.2では42~1497Byte)
と書かれていますが、IPv4の場合IPアドレスは4Byteなので
46~1500Byte→42~1496Byte
42~1497Byte→38~1493Byte
内のデータであれば分割されることなく、一回のrecvで受け取れると考えていいのでしょうか?
パケットサイズはPCから出ていくときやルータやインターネットを経由する間に情報が付加されたり再分割されたりする場合もあります。
「@IT:連載 基礎から学ぶWindowsネットワーク 第10回 IPパケットの構造とIPフラグメンテーション 2.IPフラグメンテーション」
http://www.atmarkit.co.jp/fwin2k/networ ... 10_03.html
なので当てにしてはいけないと言われています。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月18日(土) 13:58
by 史上最悪のデスペナ
softya(ソフト屋) さんが書きました:そうすると2つ目のsendのエラー処理がされないのでマズイです。
どうも、ちゃんと処理の流れを見れていない様なので、vectorで無理をせず固定配列にしてフラグ管理したほうが良いと思います。
もう一度だけチャンスをください!

コード:

    //  処理のシュミレート
    for( int i=0 ; i<max ; i++ ) {
        printf("i=%d,intvec[%d] => send process \n", i, intvec[i]);
        if( intvec[i]==5 ) {
            max--;
            intvec.erase( intvec.begin()+i );
            i--; //←これを追加
        }
    }
これでどうでしょう?
intvecが5になったらi == 6を一個小さくしてforループに戻したので大丈夫なはず・・・・・・
結果もちゃんと10個表示されましたし。

Re: どうしてもコードが作れません(WinSock)

Posted: 2012年2月18日(土) 14:54
by softya(ソフト屋)
それは動くでしょうがメンテナンス性が低い&アルゴリズムが分かりづらいです。つまり、良くないプログラムです。
後々のことを考えるのなら組んではいけないプログラムですね。

妥協案としてはvector配列の後ろから処理していけば削除しても番号が抜ける恐れはありませんので、こんな変則的なことする必要がありません。
max--;i--;と言う姑息なこともしなくてよくなります。
ただし、何故後ろから処理するのかコメントを残しておきましょう。