WinSockでレスポンスの受け取り

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
からしれんこん

WinSockでレスポンスの受け取り

#1

投稿記事 by からしれんこん » 5年前

http://dixq.net/forum/viewtopic.php?f=3 ... 51#p126951
前回質問させていただいた続きなのですが、

サーバーとクライアントを一体にしたプログラムです。
プログラム(サーバー)を起動してからiPhoneのWiFiのプロキシ設定のところで
ローカルIPとサーバーのポートを指定します。

①iPhone sifari(クライアント)でWebページ閲覧(HTTPリクエスト送信)

②ローカルプログラム(サーバー)でiPhoneから送られてくるHTTPリクエストを受信

③ローカルプログラム(クライアント)でWebページへHTTPリクエスト送信

④Webサーバー

⑤ローカルプログラム(クライアント)でWebサーバーからのHTTPレスポンスを受信

⑥ローカルプログラム(サーバー)でiPhoneへHTTPレスポンスを返信

⑦iPhone(safari)でHTTPレスポンスを受け取りWebページを表示

という流れの自前のProxyサーバーを作ろうとしています。

最期の⑦でiPhoneでの表示がされません。
「ページを開けません。ネットワーク接続が切れました。」
と出てしまうのですが、どこがダメでそうなってしまってるのかわかりません。

解決法を教えてください。

からしれんこん

Re: WinSockでレスポンスの受け取り

#2

投稿記事 by からしれんこん » 5年前

コード:

#include <stdio.h>
#include <winsock2.h>
#include "lib/BREGEXP.H"
#include "lib/crypto.h"
#include "lib/ssl.h"
#include "lib/err.h"
#include "lib/rand.h"

#pragma comment(lib,"wsock32.lib")
#pragma comment(lib,"lib/BREGEXP.LIB")
#pragma comment(lib,"lib/libeay32.lib")
#pragma comment(lib,"lib/ssleay32.lib")

void _tcp_client(char *,char *);

int main()
{
	WSADATA wsaData;
	SOCKET sock0;
	struct sockaddr_in addr;
	struct sockaddr_in client;
	int len;
	SOCKET sock;  
	BOOL yes = 1;

	char inbuf[20480];

	char *buf_data = "\0";
	char out[20480];

	int client_flag = 0;

	WSAStartup(MAKEWORD(2,0), &wsaData);

	sock0 = socket(AF_INET, SOCK_STREAM, 0);
	
	if(sock0 == INVALID_SOCKET)
	{
		printf("socket : %d\n", WSAGetLastError());
		return 1;
	}

	addr.sin_family = AF_INET;
	addr.sin_port = htons(57630);
	addr.sin_addr.S_un.S_addr = INADDR_ANY;

	setsockopt(sock0,SOL_SOCKET, SO_REUSEADDR, (const char *)&yes, sizeof(yes));

	if(bind(sock0, (struct sockaddr *)&addr, sizeof(addr)) != 0)
	{
		printf("bind : %d\n", WSAGetLastError());
		return 1;
	}

	if(listen(sock0, 5) != 0)
	{
		printf("listen : %d\n", WSAGetLastError());
		return 1;
	}

	while(1)
	{
		len = sizeof(client);
		sock = accept(sock0, (struct sockaddr *)&client, &len);

		if(sock == INVALID_SOCKET)
		{
			printf("accept : %d\n", WSAGetLastError());
			break;
		}

		memset(inbuf, 0, sizeof(inbuf));
		recv(sock, inbuf, sizeof(inbuf), 0);

		printf("端末からリクエストを受け取りました。\n\n"
		       "-------------------------------------\n");

		memset(out, 0, sizeof(out));
		_tcp_client(inbuf,out);

		send(sock,out,(int)strlen(out),0);

		printf("端末へレスポンスを返しました。\n\n"
		       "-------------------------------------\n");

		closesocket(sock);
	}

	WSACleanup();

	return 0;
}

void _tcp_client(char *buf,char *out_buf)
{
	SSL *ssl;
	SSL_CTX *ctx;
	WSADATA wsaData;
	SOCKET sock1;
	char r_buf[20480];
	unsigned int **addrptr;
	struct sockaddr_in server;
	struct hostent *host;
	int n = 0;

	BREGEXP *rxp = NULL;

	char *send_buf = buf;
	char msg[80];
	char patern1[] = "m/Host: (.*)\\r\\n/i";
	char patern2[] = "s/(GET|POST) http:\\/\\/(.+?)(\\/.*)/$1 $3/g";
	int ctr;
	int pos = 0;
	int i = 0;
	int ret;
	char server_host[1024];
	char *send_host = NULL;

	//////////////////////////////////////////////////
	//                                              //
	//          接続先Hostの取得(正規表現)          //
	//                                              //
	//////////////////////////////////////////////////

	memset(server_host,0,sizeof(server_host));

	BMatch(patern1,buf+pos,buf+strlen(buf),&rxp,msg);

	ret = rxp->endp[1] - rxp->startp[1];

	for(i=0; i<ret; i++)
	{
		server_host[i] = rxp->startp[1][i];
	}

	i++;

	server_host[i] = '\0';

	BRegfree(rxp);

	//////////////////////////////////////////////////
	//                                              //
	//       クエストヘッダーの編集(正規表現)       //
	//                                              //
	//////////////////////////////////////////////////

	rxp = NULL;

	ctr = BSubst(patern2,send_buf,send_buf+lstrlen(send_buf),&rxp,msg);

	memset(send_buf,0,sizeof(send_buf));
	sprintf(send_buf,"%s",rxp->outp);

	BRegfree(rxp);

	//////////////////////////////////////////////////
	//                                              //
	//          サーバー接続(ソケット開始)          //
	//                                              //
	//////////////////////////////////////////////////

	if(WSAStartup(MAKEWORD(2,0),&wsaData) != 0)
	{
		printf("ERROR '1' code -> ");
		printf("%d",WSAGetLastError());
		return;
	}

	sock1 = socket(AF_INET,SOCK_STREAM,0);

	if(sock1 == INVALID_SOCKET)
	{
		printf("ERROR '2' code -> ");
		printf("%d",WSAGetLastError());
		return;
	}

	server.sin_family = AF_INET;
	server.sin_port = htons(80);
	server.sin_addr.S_un.S_addr = inet_addr(server_host);

	host = gethostbyname(server_host);

	if(host == NULL)
	{
		if(WSAGetLastError() == WSAHOST_NOT_FOUND)
		{
			printf("ERROR '3' code -> ");
			printf("%d",WSAGetLastError());
			return;
		}

		printf("ERROR  '4' code -> ");
		printf("%d",WSAGetLastError());
		return;
	}

	addrptr = (unsigned int **)host->h_addr_list;

	server.sin_addr.S_un.S_addr = *(*addrptr);

	if(connect(sock1,(struct sockaddr *)&server,sizeof(server)) == -1)
	{
		printf("ERROR '5' code -> \n");
		printf("%d",WSAGetLastError());
		return;
	}

	printf("Webサーバーへ接続しました。\n"
	       "Host -> %s\n\n"
	       "-------------------------------------\n",server_host);

	n = send(sock1,send_buf,(int)strlen(send_buf),0);

	printf("リクエストを送信しました。\n\n"
	       "%s"
	       "-------------------------------------\n",send_buf);

	while(n > 0)
	{
		memset(r_buf,0,sizeof(r_buf));
		n = recv(sock1,r_buf,sizeof(r_buf),0);
		sprintf(out_buf,"%s%s",out_buf,r_buf);
	}

	sprintf(out_buf,"%s\0",out_buf);

	printf("Webサーバーからレスポンスを取得しました。\n\n"
	       "%s\n"
	       "-------------------------------------\n",out_buf);

	closesocket(sock1);

	WSACleanup();
}

アバター
みけCAT
記事: 6296
登録日時: 9年前
住所: 千葉県
連絡を取る:

Re: WinSockでレスポンスの受け取り

#3

投稿記事 by みけCAT » 5年前

_tcp_client関数の最後でWSACleanup()を実行しているため、
その後のWinSock系の関数、特に81行目のsendが機能しなくなったのではないでしょうか?(コンパイル・テストはしていません)
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

からしれんこん

Re: WinSockでレスポンスの受け取り

#4

投稿記事 by からしれんこん » 5年前

みけCAT さんが書きました:_tcp_client関数の最後でWSACleanup()を実行しているため、
その後のWinSock系の関数、特に81行目のsendが機能しなくなったのではないでしょうか?(コンパイル・テストはしていません)
なるほど。
そういうがあるのですね。
ということは33行目でWSAStartup(MAKEWORD(2,0), &wsaData);をしているので、163行目もいらないということでしょうか?

アバター
へにっくす
記事: 630
登録日時: 8年前
住所: 東京都

Re: WinSockでレスポンスの受け取り

#5

投稿記事 by へにっくす » 5年前

WSAStartupとWSACleanupはプロセスにつき一回だけ呼べばいいはずです。

WINSOCKのWSACleanupについて - okwave
written by へにっくす

からしれんこん

Re: WinSockでレスポンスの受け取り

#6

投稿記事 by からしれんこん » 5年前

へにっくす さんが書きました:WSAStartupとWSACleanupはプロセスにつき一回だけ呼べばいいはずです。

WINSOCKのWSACleanupについて - okwave
ありがとうございます。
編集したところ開けるページと開けないページがありました。

開けるページはいいのですが、開けないページはiPhone上で
-------------------------------
ページを開けません。

発生したサーバエラー:
"RAWデータをデコードできません。"
------------------------------
となりました。
PC側でも200は返ってるのですが、欲しいレスポンスではありませんでした。

基本的にiPhoneから送られてくるリクエストをそのまま投げてるので、おかしなところはないと思うのですが・・・

YuO
記事: 941
登録日時: 9年前
住所: 東京都世田谷区

Re: WinSockでレスポンスの受け取り

#7

投稿記事 by YuO » 5年前

なんでacceptしたSOCKETに対してのrecvsendの戻り値を確認していないのですか。
そして,recvの戻り値を知らずに,どうやって送られてきたデータの終端を判断しようとしているのですか。

からしれんこん

Re: WinSockでレスポンスの受け取り

#8

投稿記事 by からしれんこん » 5年前

YuO さんが書きました:なんでacceptしたSOCKETに対してのrecvsendの戻り値を確認していないのですか。
そして,recvの戻り値を知らずに,どうやって送られてきたデータの終端を判断しようとしているのですか。
少し前まで

コード:

		len = sizeof(client);
		sock = accept(sock0,(struct sockaddr *)&client,&len);

		if(sock == INVALID_SOCKET)
		{
			printf("accept : %d\n",WSAGetLastError());
			break;
		}

		while(n > 0)
		{
			memset(inbuf,0,sizeof(inbuf));
			n = recv(sock,inbuf,sizeof(inbuf),0);
			sprintf(buf,"%s%s",buf,inbuf);
		}

		sprintf(buf,"%s\0",buf);

		printf("端末からリクエストを受け取りました。\n\n"
		       "%s"
		       "-------------------------------------\n",buf);

		memset(out, 0, sizeof(out));
		_tcp_client(buf,out);
このようにしていたのですが、「問題が発生したため、終了します」っていうエラーが出てしまって。。。
結局原因がわからず、whileを外して放置してました。

YuO
記事: 941
登録日時: 9年前
住所: 東京都世田谷区

Re: WinSockでレスポンスの受け取り

#9

投稿記事 by YuO » 5年前

からしれんこん さんが書きました:このようにしていたのですが、「問題が発生したため、終了します」っていうエラーが出てしまって。。。
結局原因がわからず、whileを外して放置してました。
ちゃんとデバッグしましょう。
そもそも,「問題が発生したため……」はIDE等のデバッガを使っていない場合にWindowsが未処理の例外を検出した場合のメッセージだと思うのですが。
どこでそのエラーが起きたかを確認するためにも,IDEでのデバッグは必須です。

HTTPのヘッダは確かに文字列ですが,それに続く本体は文字列とは限りません。
image/pngなどの画像もありますし,application/octet-streamという「8ビットの並び」としか定義されないようなものも通信されます。
しかし,_tcp_clientという関数はそれを強引に(ヌル文字で終端する)文字列として扱ってしまっています。
20480バイト以上のデータを送信された場合にヌル文字が来なければ,文字列関数はオーバーランしますし,
来てしまうとデータが不完全になるため,サーバー / クライアントは正しくデータを処理できません。

よって,ヘッダと本体を分離して考え,ヘッダに関しては文字列として扱ってよいですが,
本体はバイナリデータとして扱わないと行けません。
バイナリデータを扱うには,「データ本体」と「サイズ」を保持する必要があります。
本来,サイズはrecvの戻り値によって取得できるのですが,proxyの入力部分でそれを捨ててしまっています。
ちゃんとrecvしたサイズを保持し,上流へ通信を媒介するときにそのサイズを基にsendする必要がありますし,
上流からrecvしたサイズも保持して,下流へ媒介するときにもそのサイズを基にsendする必要があります。
オフトピック
_tcp_clientという識別子はファイルスコープでの利用は予約されていて(ISO/IEC 9899:1999 7.1.3¶1),利用した場合は未定義の振る舞いとされている(同¶2)ため,使わないことを推奨します。

閉鎖

“C言語何でも質問掲示板” へ戻る