在C#套接字,我怎么能异步通过的NetworkStream读取和写入数据 [英] Sockets in C#, how can I asynchronously read and write data through a NetworkStream

查看:1834
本文介绍了在C#套接字,我怎么能异步通过的NetworkStream读取和写入数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

[我仅限于Visual Studio 2010中,因此,C#4异步和等待都没有提供给我。]

[I am limited to Visual Studio 2010, and therefore, C# 4. async and await are not available to me.]

我正在网络架构为我的一个项目中,有超过一个的服务器和客户端之间的网络发送数据分组,但客户端和服务器必须继续在等待运行,所以code的至非阻塞,所以我想用异步方法。然而,除了简单的同步一次性IO,我不知道很多关于做什么,使用的NetworkStream尤其是当。我试图做的是:

I'm working on network architecture for a project of mine, that sends packets of data over a network between a server and a client, but the client and server must continue running while waiting, so the code has to be non-blocking, so I thought to use the asynchronous methods. However, except for simple synchronous one-time IO, I don't know much about what to do, especially when using a NetworkStream. What I'm trying to do is:

1)客户端连接到服务器

1) Client connects to server

2)服务器将接受连接

2) Server accepts connection

3)服务器等待来自客户端的数据

3) Server waits for data from client

4)服务器处理数据

5)服务器响应客户端

5) Server responds to client

6)当连接被打开,从3重复。

6) While connection is open, repeat from 3.

和我想使用的NetworkStream来包装插座。但是,我是新来的异步I / O和我真的不知道如何做到这一点无需等待响应的时候,尤其是用的NetworkStream阻塞服务器/客户端code的其他部分。在我的研究,我发现使用类似这样的例子:

And I would like to use a NetworkStream to wrap the socket. But I am new to asynchronous I/O and i'm not really sure how to do this without blocking other parts of the server/client code when waiting for a response, especially with a NetworkStream. In my research I see examples that use something like this:

while(true){
    socket.BeginAccept(new AsyncCallback(AcceptCallback), socket );
}

但好像这个循环仍然会撑起这个应用程序。谁能给我究竟是如何做到这一点一些指针(HA)?我一直没能找到保持连接打开,仅客户端连接许多例子 - >客户端发送 - >服务器收到 - >服务器发送 - >断开连接。我不要求全code,只是一些片段的总体思路。

But it seems like that loop would still hold up the application. Can anyone give me some pointers (ha) on how exactly to do this? I haven't been able to find many examples that keep the connection open, only Client Connect -> Client Send -> Server Recieve -> Server Send -> Disconnect. I'm not asking for full code, just the general idea with a few snippets.

推荐答案

下面是使用异步的一个很简单的例子/等待与的NetworkStream

Here is a very simple example of using async/await with a NetworkStream:

SocketServer.cs:

SocketServer.cs:

class SocketServer
{
    private readonly Socket _listen;

    public SocketServer(int port)
    {
        IPEndPoint listenEndPoint = new IPEndPoint(IPAddress.Loopback, port);
        _listen = new Socket(SocketType.Stream, ProtocolType.Tcp);
        _listen.Bind(listenEndPoint);
        _listen.Listen(1);
        _listen.BeginAccept(_Accept, null);
    }

    public void Stop()
    {
        _listen.Close();
    }

    private async void _Accept(IAsyncResult result)
    {
        try
        {
            using (Socket client = _listen.EndAccept(result))
            using (NetworkStream stream = new NetworkStream(client))
            using (StreamReader reader = new StreamReader(stream))
            using (StreamWriter writer = new StreamWriter(stream))
            {
                Console.WriteLine("SERVER: accepted new client");

                string text;

                while ((text = await reader.ReadLineAsync()) != null)
                {
                    Console.WriteLine("SERVER: received \"" + text + "\"");
                    writer.WriteLine(text);
                    writer.Flush();
                }
            }

            Console.WriteLine("SERVER: end-of-stream");

            // Don't accept a new client until the previous one is done
            _listen.BeginAccept(_Accept, null);
        }
        catch (ObjectDisposedException)
        {
            Console.WriteLine("SERVER: server was closed");
        }
        catch (SocketException e)
        {
            Console.WriteLine("SERVER: Exception: " + e);
        }
    }
}

Program.cs的:

Program.cs:

class Program
{
    private const int _kport = 54321;

    static void Main(string[] args)
    {
        SocketServer server = new SocketServer(_kport);
        Socket remote = new Socket(SocketType.Stream, ProtocolType.Tcp);
        IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Loopback, _kport);

        remote.Connect(remoteEndPoint);

        using (NetworkStream stream = new NetworkStream(remote))
        using (StreamReader reader = new StreamReader(stream))
        using (StreamWriter writer = new StreamWriter(stream))
        {
            Task receiveTask = _Receive(reader);
            string text;

            Console.WriteLine("CLIENT: connected. Enter text to send...");

            while ((text = Console.ReadLine()) != "")
            {
                writer.WriteLine(text);
                writer.Flush();
            }

            remote.Shutdown(SocketShutdown.Send);
            receiveTask.Wait();
        }

        server.Stop();
    }

    private static async Task _Receive(StreamReader reader)
    {
        string receiveText;

        while ((receiveText = await reader.ReadLineAsync()) != null)
        {
            Console.WriteLine("CLIENT: received \"" + receiveText + "\"");
        }

        Console.WriteLine("CLIENT: end-of-stream");
    }
}

这是一个非常简单的例子,在相同的处理托管在服务器和客户端和接收在一个时间只有一个连接。这真的只是为了演示。真实世界的场景无疑将包含其他功能,以满足他们的需求。

It's a very simple example, hosting both the server and client in the same process and accepting just one connection at a time. It's really just for illustration purposes. Real-world scenarios will no doubt include other features to suit their needs.

在这里,我包裹的NetworkStream S IN 的StreamReader 的StreamWriter 秒。请注意,您必须调用同花顺()来确保数据实际发送。对于在我更好的控制/ O,你当然可以使用的NetworkStream 直接。只需使用 Stream.ReadAsync()方法,而不是 StreamReader.ReadLineAsync()。还要注意,在我的例子,写作是同步的。你可以让这个异步的,以及如果你喜欢,使用相同的基本技术,如阅读。

Here, I'm wrapping the NetworkStreams in StreamReaders and StreamWriters. Note that you have to call Flush() to ensure that the data is actually sent. For better control over the I/O, you can of course use the NetworkStream directly. Just use the Stream.ReadAsync() method instead of StreamReader.ReadLineAsync(). Note also that in my example, writing is synchronous. You can make this asynchronous as well if you like, using the same basic technique as shown for reading.

编辑:

该任择议定书表示,他们无法使用异步 / 等待。这里是一个版本,它使用客户端的NetworkStream 和旧式开始/ EndXXX()的API(类似的变化将作出当然服务器):

The OP indicates they are unable to use async/await. Here is a version of the client which uses NetworkStream and the old-style Begin/EndXXX() API (similar changes would be made to the server of course):

using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace TestOldSchoolNetworkStream
{
    class Program
    {
        private const int _kport = 54321;

        static void Main(string[] args)
        {
            SocketServer server = new SocketServer(_kport);
            Socket remote = new Socket(SocketType.Stream, ProtocolType.Tcp);
            IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Loopback, _kport);

            remote.Connect(remoteEndPoint);

            using (NetworkStream stream = new NetworkStream(remote))
            {
                // For convenience, These variables are local and captured by the
                // anonymous method callback. A less-primitive implementation would
                // encapsulate the client state in a separate class, where these objects
                // would be kept. The instance of this object would be then passed to the
                // completion callback, or the receive method itself would contain the
                // completion callback itself.
                ManualResetEvent receiveMonitor = new ManualResetEvent(false);
                byte[] rgbReceive = new byte[8192];
                char[] rgch = new char[Encoding.UTF8.GetMaxCharCount(rgbReceive.Length)];
                Decoder decoder = Encoding.UTF8.GetDecoder();
                StringBuilder receiveBuffer = new StringBuilder();

                stream.BeginRead(rgbReceive, 0, rgbReceive.Length, result =>
                {
                    _Receive(stream, rgbReceive, rgch, decoder, receiveBuffer, receiveMonitor, result);
                }, null);

                string text;

                Console.WriteLine("CLIENT: connected. Enter text to send...");

                while ((text = Console.ReadLine()) != "")
                {
                    byte[] rgbSend = Encoding.UTF8.GetBytes(text + Environment.NewLine);

                    remote.BeginSend(rgbSend, 0, rgbSend.Length, SocketFlags.None, _Send, Tuple.Create(remote, rgbSend.Length));
                }

                remote.Shutdown(SocketShutdown.Send);
                receiveMonitor.WaitOne();
            }

            server.Stop();
        }

        private static void _Receive(NetworkStream stream, byte[] rgb, char[] rgch, Decoder decoder, StringBuilder receiveBuffer, EventWaitHandle monitor, IAsyncResult result)
        {
            try
            {
                int byteCount = stream.EndRead(result);
                string fullLine = null;

                if (byteCount > 0)
                {
                    int charCount = decoder.GetChars(rgb, 0, byteCount, rgch, 0);

                    receiveBuffer.Append(rgch, 0, charCount);

                    int newLineIndex = IndexOf(receiveBuffer, Environment.NewLine);

                    if (newLineIndex >= 0)
                    {
                        fullLine = receiveBuffer.ToString(0, newLineIndex);
                        receiveBuffer.Remove(0, newLineIndex + Environment.NewLine.Length);
                    }

                    stream.BeginRead(rgb, 0, rgb.Length, result1 =>
                    {
                        _Receive(stream, rgb, rgch, decoder, receiveBuffer, monitor, result1);
                    }, null);
                }
                else
                {
                    Console.WriteLine("CLIENT: end-of-stream");
                    fullLine = receiveBuffer.ToString();
                    monitor.Set();
                }

                if (!string.IsNullOrEmpty(fullLine))
                {
                    Console.WriteLine("CLIENT: received \"" + fullLine + "\"");
                }
            }
            catch (IOException e)
            {
                Console.WriteLine("CLIENT: Exception: " + e);
            }
        }

        private static int IndexOf(StringBuilder sb, string text)
        {
            for (int i = 0; i < sb.Length - text.Length + 1; i++)
            {
                bool match = true;

                for (int j = 0; j < text.Length; j++)
                {
                    if (sb[i + j] != text[j])
                    {
                        match = false;
                        break;
                    }
                }

                if (match)
                {
                    return i;
                }
            }

            return -1;
        }

        private static void _Send(IAsyncResult result)
        {
            try
            {
                Tuple<Socket, int> state = (Tuple<Socket, int>)result.AsyncState;
                int actualLength = state.Item1.EndSend(result);

                if (state.Item2 != actualLength)
                {
                    // Should never happen...the async operation should not complete until
                    // the full buffer has been successfully sent, 
                    Console.WriteLine("CLIENT: send completed with only partial success");
                }
            }
            catch (IOException e)
            {
                Console.WriteLine("CLIENT: Exception: " + e);
            }
        }
    }
}

请注意,此code,即使在尽管离开了一堆异常处理逻辑的,是相当长的,至少部分是由于这样的事实,的TextReader 已经没有内置的异步的API,所以输入数据的处理是更详细的在这里。当然,这是一个简单的基于行的文本交换协议。其他协议可能会或多或少地在复杂的数据拆包方面的条件,但底层的读写元素的NetworkStream 将是一样的。

Note that this code, even in spite of leaving out a bunch of exception-handling logic, is considerably longer, at least in part due to the fact that TextReader has no built-in asynchronous API, and so the processing of the input data is much more verbose here. Of course, this is for a simple line-based text exchange protocol. Other protocols may be more or less complex in terms of the data-unpacking aspects, but the underlying read and write elements of the NetworkStream would be the same.

这篇关于在C#套接字,我怎么能异步通过的NetworkStream读取和写入数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆