1

tl:dr(長すぎる、読んでいない)の投稿を決定する前に、少なくともいくつかを読んでみてください。その質問は多くの小さな断片に分解されているからです。そのうちのいくつかはおそらく答えて私を助けることができます。

できる限り助けてください。これらのタイプの問題はインターネット上で非常に一般的であり、あなたは私と私の後のはるかに多くの人々を助けてくれると思います。

私は現在、HTTPサービスとプロトコル自体を調査しており、それが自分にとって役立つかどうかを知ることができます。
いくつかの基本的な質問と、議論する必要のあるコードがあります。

まず、コミュニケーションはどのように始まるのか知りたいのですが?クライアントがリソースを要求するメッセージを送信することを発見しました(これは正しいですか?)。では、どうなりますか?私は(サーバーとして)何で返信する必要がありますか?
すべての応答の後に、キャリッジリターンとラインフィードを追加する必要がありますか?どこかに、2つ必要であると書かれています(\ r \ n \ r \ n)。
非同期書き込みを確立するにはどうすればよいですか?(この質問が理解できることを願っています)私の主な目標は、クライアントとサーバー間の接続を実現し、サーバーからクライアントへの継続的なデータストリームを実現することです。クライアントは、受信するすべてのメッセージに返信する必要がありますか?
私はこれらのことの専門家ではないので、質問を明確にしたいと思います(それでも、私はそれに非常に興味があります)。

そして、私の問題のプログラミング部分について。
私は、C ++(サーバー側)のQtで簡単なプログラムを作成し、Objective C(iOS)で簡単なクライアントを作成することができました。クライアントが接続し、リクエストヘッダーを読み取ることができます。これは次のようなものです。

Data available, incoming:  "GET / HTTP/1.1
Host: localhost:9990
Connection: close
User-Agent: CFStream%20test/1.0 CFNetwork/609 Darwin/12.2.0

このヘッダーに手動で返信する必要がありますか?もしそうなら、何ですか?

クライアント側のコードは次のようになります(疑似ではないことはわかっていますが、かなり自明だと思います)。

- (void)setupStream
{
    NSURL *url = [NSURL URLWithString:@"http://localhost:9990"];

    CFHTTPMessageRef message = CFHTTPMessageCreateRequest(NULL, (CFStringRef)@"GET", (CFURLRef)url, kCFHTTPVersion1_1);

    stream = CFReadStreamCreateForHTTPRequest(NULL, message);
    CFRelease(message);

    if (!CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPShouldAutoredirect, kCFBooleanTrue))
    {
        NSLog(@"Some error.");
    }

    CFDictionaryRef proxySettings = CFNetworkCopySystemProxySettings();
    CFReadStreamSetProperty(stream, kCFStreamPropertyHTTPProxy, proxySettings);
    CFRelease(proxySettings);

    if (!CFReadStreamOpen(stream))
    {

        CFRelease(stream);
        NSLog(@"Error opening stream.");
    }

    CFStreamClientContext context = {0, self, NULL, NULL, NULL};
    CFReadStreamSetClient(stream, kCFStreamEventHasBytesAvailable | kCFStreamEventErrorOccurred, readStreamCallback, &context);
    CFReadStreamScheduleWithRunLoop(stream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
    NSLog(@"Done");
}

これがセットアップストリーム方式です。stream変数は、型のクラス変数ですCFReadStreamRef

コールバックは次のようになります。

static void readStreamCallback(CFReadStreamRef aStream, CFStreamEventType event, void *client)
{
    ViewController *controller = (ViewController*)client;
    [controller handleEvent:event forStream:aStream];
}

そして、このようなハンドルイベント:

- (void)handleEvent:(CFStreamEventType)event forStream:(CFReadStreamRef)aStream
{
    if (aStream != stream)
    {
        return;
    }

    NSLog(@"Handle event callback");

    switch (event)
    {
        case kCFStreamEventHasBytesAvailable:
            NSLog(@"Work log");
            UInt8 bytes[11];
            CFIndex length;
            length = CFReadStreamRead(stream, bytes, 11); //I know 11 bytes is hard coded, its in testing stage now. Feel free to suggest me how to do it better.

            if (length == -1)
            {
                NSLog(@"Error, data length = -1");
                return;
            }

            NSLog(@"Len: %li, data: %s", length, bytes);
        break;
        default:
            NSLog(@"Other event");
        break;
    }
}

そして、それは事実上、言及する価値のあるすべてのクライアントコードです。Qtサーバーの部分(重要な部分のみを投稿します)は次のように実行されます:(これはサブクラス化されたQTcpServerクラスです)。最初にstartServer();呼び出されます:

bool Server::startServer()
{
    if (!this->listen(QHostAddress::Any, 9990))
        return false;

    return true;
}

着信接続がある場合incomingConnection、パラメータとしてソケット記述子を使用して起動されます。

void Server::incomingConnection(int handle)
{
    qDebug("New client connected");
    ServerClient *client = new ServerClient(handle, this); //The constructor takes in the socket descriptor needed to set up the socket and the parent (this)
    client->setVectorLocation(clients.count()); //This is a int from a Qvector in which i append the clients, its not important for understanding right now.
    connect(client, SIGNAL(clientDisconnected(int)), this, SLOT(clientDisconnected(int)), Qt::QueuedConnection); //When the client socket emits a disconnected signal the ServerClient class emits a client disconnected signal which the server uses to delete that client from the vector (thats why I use "setVectorLocation(int)") - not important right now
    clients.push_back(client); //And then I append the client to the QVector - not important right now
}

ClientServerクラスコンストラクターは、新しいソケットを作成し、必要なメソッドを接続するだけです。

ServerClient::ServerClient(int handle, QObject *parent) :
QObject(parent)
{
    socket = new QTcpSocket(this); //Socket is a class variable

    connect(socket, SIGNAL(disconnected()), this, SLOT(disconnected()));
    connect(socket, SIGNAL(readyRead()), this, SLOT(readyRead()));

    socket->setSocketDescriptor(handle);
}

Ready readは、着信データを書き込むだけです(後でユーザーが増えることはないと思います)。

void ServerClient::readyRead()
{
    qDebug() << "Data available, incoming: " << socket->readAll();
}

そして最後に書き込みデータ:

void ServerClient::writeData(QByteArray *data)
{
    data->append("\r\n\r\n"); //I have read this must be appended to all outgoing data from a HTTP server
    socket->write(*data);
    socket->flush();
    qDebug() << "Written data to client: " << *data;
}

ただし、このコードは常に機能するとは限りません。「メッセージ」のようなメッセージを書くと、クライアントはすべてのデータとそこにあるべきではないいくつかのものを受け取ることがあります(新しい行と奇妙な記号-NSLogこれを引き起こす可能性がありますか?)。"Hellow"時々私がクライアントを送るとき、"Hel"他のいくつかのファンキーなものだけを手に入れます。

問題は何ですか?何にもっと注意を払うべきですか?私を助けるものは何でも大いに感謝されるでしょう。そして、数百ページの本を含むいくつかのリンクを貼り付けないでください。これは私に物事を説明するだけで解決できると確信しています。

どうもありがとう!
1月

4

2 に答える 2

0

あなたの質問は「HTTP はどのように機能するか」に相当し、完全な答えは仕様にあります。

于 2012-12-04T17:53:44.053 に答える
0

あなたは多くの質問をしました...そしてそれは完全に正当なことです:)

私は告白します - それ長すぎました、私は読んでいませんでした:(

しかし ...

1) はい、HTTP プロトコルは "CRLF" ("\r\n") を想定しています。多くのサーバーと多くのクライアントは「寛容」ですが、厳密に言えば、必要です。

参照: RFC 2616

2) HTTP の「内部」を理解したいと思うことも完全に正当です - 私はあなたに拍手を送ります。

1 つの良い方法は、RFC を読むことです。

もう 1 つは、「telnet」クライアントを使用することです: http://blog.tonycode.com/tech-stuff/http-notes/making-http-requests-via-telnet

もう1つは、FF Firebugでリクエストとレスポンスを研究することです

3) ソケット プログラミングは別の問題です。これは、"hello world" と読み上げたり、単に "hel" と読み上げたりする理由を説明しています。

強くお勧めします: Beej's Guide to Network Programming

4) 最後に、実際に C++ を使用して Qt でサーバーを作成する方法はありません (おもちゃの「科学実験」として、またはいくつかの本当に壁から離れた要件を除いて)。

C# (Windows サーバーの場合)、Java (その他すべての場合)、または使い慣れたスクリプト言語 (Perl、Ruby/RoR、Python、Lua が思い浮かびます) でサーバー コードを書くことは間違いありません。

私見..そしてそれが役立つことを願っています!

于 2012-12-04T17:59:26.083 に答える