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での表示がされません。
「ページを開けません。ネットワーク接続が切れました。」
と出てしまうのですが、どこがダメでそうなってしまってるのかわかりません。
解決法を教えてください。
WinSockでレスポンスの受け取り
Re: WinSockでレスポンスの受け取り
#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();
}
Re: WinSockでレスポンスの受け取り
_tcp_client関数の最後でWSACleanup()を実行しているため、
その後のWinSock系の関数、特に81行目のsendが機能しなくなったのではないでしょうか?(コンパイル・テストはしていません)
その後のWinSock系の関数、特に81行目のsendが機能しなくなったのではないでしょうか?(コンパイル・テストはしていません)
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: WinSockでレスポンスの受け取り
なるほど。みけCAT さんが書きました:_tcp_client関数の最後でWSACleanup()を実行しているため、
その後のWinSock系の関数、特に81行目のsendが機能しなくなったのではないでしょうか?(コンパイル・テストはしていません)
そういうがあるのですね。
ということは33行目でWSAStartup(MAKEWORD(2,0), &wsaData);をしているので、163行目もいらないということでしょうか?
Re: WinSockでレスポンスの受け取り
written by へにっくす
Re: WinSockでレスポンスの受け取り
ありがとうございます。
編集したところ開けるページと開けないページがありました。
開けるページはいいのですが、開けないページはiPhone上で
-------------------------------
ページを開けません。
発生したサーバエラー:
"RAWデータをデコードできません。"
------------------------------
となりました。
PC側でも200は返ってるのですが、欲しいレスポンスではありませんでした。
基本的にiPhoneから送られてくるリクエストをそのまま投げてるので、おかしなところはないと思うのですが・・・
Re: WinSockでレスポンスの受け取り
少し前まで
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を外して放置してました。
Re: WinSockでレスポンスの受け取り
ちゃんとデバッグしましょう。からしれんこん さんが書きました:このようにしていたのですが、「問題が発生したため、終了します」っていうエラーが出てしまって。。。
結局原因がわからず、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)ため,使わないことを推奨します。