0

Javaで作成した別のサーバーへの非同期接続と通信するこのC#クライアントを実装しました。これはクライアントです:

public class NetworkTransaction
{
    private GenericRequest request;
    private const string serverName = "192.168.1.101";
    private const int serverPort = 7777;
    private const int TIMEOUT_MILLISECONDS = 3000;
    private Delegate method;

    public NetworkTransaction(GenericRequest request, Delegate method)
    {
        this.request = request;
        this.method = method;
    }

    public void SendRequest()
    {
        SocketAsyncEventArgs connectionOperation = new SocketAsyncEventArgs();
        DnsEndPoint hostEntry = new DnsEndPoint(serverName, serverPort);
        Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        connectionOperation.Completed += new EventHandler<SocketAsyncEventArgs>(SocketEventArg_Completed);
        connectionOperation.RemoteEndPoint = hostEntry;
        connectionOperation.UserToken = socket;

        try
        {
            socket.ConnectAsync(connectionOperation);
        }
        catch (SocketException ex)
        {
            throw new SocketException((int)ex.ErrorCode);
        }
    }

    private void SocketEventArg_Completed(object sender, SocketAsyncEventArgs e)
    {
        switch (e.LastOperation)
        {
            case SocketAsyncOperation.Connect:
                ProcessConnectCompleted(e);
                break;
            case SocketAsyncOperation.Receive:
                ProcessReceiveCompleted(e);
                break;
            case SocketAsyncOperation.Send:
                ProcessSendCompleted(e);
                break;
            default:
                throw new Exception("Invalid operation completed");
        }
    }

    private void ProcessConnectCompleted(SocketAsyncEventArgs e)
    {
        if (e.SocketError == SocketError.Success)
        {
            byte[] buffer = Encoding.UTF8.GetBytes(request.ToJson() + "\n\r");
            e.SetBuffer(buffer, 0, buffer.Length);
            Socket sock = e.UserToken as Socket;
            sock.SendAsync(e);
        }
    }

    private void ProcessSendCompleted(SocketAsyncEventArgs e)
    {
        if (e.SocketError == SocketError.Success)
        {
            Socket sock = e.UserToken as Socket;
            sock.ReceiveAsync(e);
        }
    }

    private void ProcessReceiveCompleted(SocketAsyncEventArgs e)
    {
        if (e.SocketError == SocketError.Success)
        {
            string dataFromServer = Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred);
            Socket sock = e.UserToken as Socket;
            sock.Shutdown(SocketShutdown.Send);
            sock.Close();

            method.DynamicInvoke(dataFromServer);
        }
    }
}

サーバーから長いメッセージを取得するにはどうすればよいですか?クライアントがより多くのデータを読み取って、実際にメッセージ全体を読んだかどうかを確認するには、何を変更する必要がありますか?行に問題がある可能性があるためstring dataFromServer = Encoding.UTF8.GetString(e.Buffer, 0, e.BytesTransferred);、メッセージの全体ではなく、メッセージの一部のみを受信します。

4

4 に答える 4

3

プロトコルの作成について私に与えられた答えは、私を最も助けてくれました。私が最終的に行ったのは、サイズを示すメッセージに「ヘッダー」を添付することでした。次に、クライアントはヘッダーを解析し、メッセージ サイズ ヘッダーと実際に受信したバイト数に従って、送信するデータがまだあるかどうかを確認します。読み取るデータがまだ残っている場合は、次のようにします。

if (messageSizeRemaining > 0)
{
      sock.ReceiveAsync(e);
      return;
}
于 2012-06-13T16:01:52.920 に答える
1

クライアントにメッセージ全体を実際に読んだかどうかを確認するためにさらにデータを読み取らせるには、何を変更する必要がありますか?

「ここに50バイトが来ました」または「これはすべてデータでした、さようなら」というプロトコルを実装する必要があります。

ProcessReceiveCompleted()データが受信されるたびに呼び出されます。これは、1バイトから大量のバイトの間であれば何でもかまいません。メッセージはそのレベルに存在しないため、ソケットは「メッセージ」が全体として送信されることを保証しません。受信するデータの量は、CPUやネットワークの使用率などの要因によって異なります。

そこでプロトコルが登場します。これは、サーバーとクライアントの両方で定義する必要があります。たとえば、HTTPは2つCRLFのを使用して、クライアントに「これは私の要求でした。今すぐ応答してください」と言わせます。一方、サーバーは(デフォルトで)すべてのデータが送信されると接続を閉じますが、Content-lengthヘッダーも送信するため、クライアントはすべてのデータを受信したことを確認できます。

したがって、ProcessReceiveCompleted()メッセージ全体を受信したら、データをに保存して処理を開始する必要があります。

于 2012-06-13T12:31:36.230 に答える
0

HTTP からデータを取得するために、次のメソッドを同期的に使用しました (非同期が必要なのはわかっています)。

    public static Stream getDataFromHttp(string strUrl)
    {
        Stream strmOutput = null;
        try
        {
            HttpWebRequest request;
            Uri targetUri = new Uri(strUrl);
            request = (HttpWebRequest)HttpWebRequest.Create(targetUri);
            request.Timeout = 5000;
            request.ReadWriteTimeout = 20000;
            request.Method = "Get";


            request.UserAgent = "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)";
            if (request != null)
            {
                request.GetResponse();
                if (request.HaveResponse)
                {
                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    strmOutput = response.GetResponseStream();
                }
            }

        }
        catch (WebException wex)
        {

        }
        catch (Exception ex)
        {

        }
        return strmOutput;
    }

ストリームをテキストに変換するには、次を使用します。

    Public Static String StreamToString(Stream stream)
    {
            StreamReader SR = new StreamReader(stream);
            try
            {
               String strOutput = SR.ReadToEnd();
               Return strOutput;
             }
             catch(Exception ex)
             {
                  return ex.message
             }
   }

これがうまくいくことを願っています

于 2012-06-13T12:44:48.190 に答える
0

Web サービスを使用する場合は、デュプレックス チャネルを使用できます。クライアントが次のようなものを使用してサーバーでデータ ストリームを登録する場所:

[OperationContract(IsOneWay = true)]
void RequestData();

そして、サーバーは次のように OneWay 操作を呼び出してデータをクライアントに送り返します。

[OperationContract(IsOneWay = true)]
void SendNewData(byte[] data, bool completed)

クライアントはすべてのデータを結合し、完了フラグを受信すると、サーバーから登録解除します。

DuplexChannels の詳細については、Duplex Servicesを参照してください。

于 2012-06-13T12:36:36.743 に答える