从长度未知的套接字接收数据

时间:2015-08-18 17:41:34

标签: c# sockets client-server win-universal-app

我使用 Windows Universal 应用程序架构使用 Windows 10 设置客户端 - 服务器网络。目的是让设备充当服务器,其他设备可以连接并向服务器发送数据。我现在至少要打算通过局域网来做这件事。我们来看看一些代码。

我提出的结构是几个类ServerClient。服务器有多个客户端将侦听从客户端发送的数据,这里是Client

注意:我会提供完整的代码,以便您可以更轻松地进行调试。

public class Client
{
    public StreamSocket Socket { get; internal set; }
    public bool IsConnected { get; internal set; }

    public async Task ConnectAsync(string ip, string port = "80")
    {
        try
        {
            //Create a socket and connect to the IP address.
            Socket = new StreamSocket();

            await Socket.ConnectAsync(new HostName(ip), port);

            IsConnected = true;
        }
        catch (Exception)
        {
            IsConnected = false;

            throw;
        }
    }

    public async void SendDataAsync(string data)
    {
        //If we're not connected, then bugger this.
        if (!IsConnected)
            throw new InvalidOperationException("The client is not connected to the server, connect first, then try again.");

        //Send the data.
        DataWriter stream = new DataWriter(Socket.OutputStream);

        //Write and commit the data to the server.
        stream.WriteString(data);
        await stream.StoreAsync();
    }

    public void Disconnect()
    {
        if (IsConnected)
        {
            //TODO: Disconnect safely (Still working this one out)

            //Dispose of the socket.
            Socket.Dispose();
            Socket = null;
        }
    }
}

我们感兴趣的是SendDataAsync方法,这是我们向服务器发送数据的地方。这是Server

public class Server
{
    public List<Client> Clients { get; private set; } = new List<Client>();

    public StreamSocketListener Listener { get; private set; }
    public string Port { get; private set; }

    /// <summary>
    /// Gets the local IP address of the device.
    /// </summary>
    public HostName HostName
    {
        get
        {
            return NetworkInformation.GetHostNames()
                .FirstOrDefault(x => x.IPInformation != null 
                                  && x.Type == HostNameType.Ipv4);
        }
    }

    public Server()
        : this("80")
    { }

    public Server(string port)
    {
        if (string.IsNullOrEmpty(port))
            throw new ArgumentNullException("port");

        Port = port;
    }

    public async Task Setup()
    {
        Listener = new StreamSocketListener();

        //Open a port to listen for connections   
        await Listener.BindServiceNameAsync(Port, SocketProtectionLevel.PlainSocket);

        Listener.ConnectionReceived += Listener_ConnectionReceived;
    }

    private void Listener_ConnectionReceived(StreamSocketListener sender, StreamSocketListenerConnectionReceivedEventArgs args)
    {
        Client client = new Client()
        {
            Socket = args.Socket
        };

        //Add the client to the collection.
        Clients.Add(client);

        //Wait for some data.
        WaitForMessage(client);
    }

    private async void WaitForMessage(Client client)
    {
        //Open up a stream
        DataReader stream = new DataReader(client.Socket.InputStream);

        //Wait for 12 bytes (wtf, what if I don't know for sure how much data is arriving?)
        await stream.LoadAsync(12);

        //Get the message that was sent.
        string message = stream.ReadString(12);
    }
}

我们感兴趣的是WaitForMessage方法,这是我们等待客户向我们发送一些数据的地方,然后我们会做一些有用的事情。它

以下是使用这些类的MainPage.xaml.cs代码:

public sealed partial class MainPage : Page
{
    private Server _Server;
    private Client _Client;

    public MainPage()
    {
        this.InitializeComponent();

        SetupServer();
        ConnectToServer();
    }

    private async void SetupServer()
    {
        //Create the server
        _Server = new Server();

        await _Server.Setup();
    }

    private async void ConnectToServer()
    {
        //Create a client and connect to the server.
        _Client = new Client();

        //Note, this may not be your IP address, input your local IP instead (ipconfig in command prompt)
        await _Client.ConnectAsync("192.168.1.5");

        //Send some data to the server.
        _Client.SendDataAsync("Hello World!");
    }
}

所以,对实际问题。以下代码给我带来了麻烦:

//Wait for 12 bytes (wtf, what if I don't know for sure how much data is arriving?)
await stream.LoadAsync(12);

//Get the message that was sent.
string message = stream.ReadString(12);

这里的问题是它可能并不总是 12个字节被发送到服务器。它目前只有12个,因为&#34; Hello World!&#34; 字符串的大小是,否则,如果缓冲区更长(大于12),它永远完成接收数据,因为它期望更多的字节。

就像我提到的那样,我对网络很陌生,但这并没有任何意义,这就是我所期待的:

  1. 客户端连接到服务器。
  2. 客户端发送一些数据然后关闭流。
  3. 服务器识别出数据流已关闭(可能是事件或其他事件)并采取相应措施。
  4. 等待给定的字节数无意义。

    也许我完全错误地看待这个问题,我应该采取不同的方法吗?

    这是我一直在考虑的一个想法:

    1. 客户端告诉服务器它将发送多少字节。
    2. 然后,服务器设置流并等待这些字节。
    3. 客户端发送字节。
    4. ...
    5. 利润!
    6. 然而,事实是,由于我完全不熟悉这些内容,是否有针对客户端 - 服务器架构的最佳实践方法?

1 个答案:

答案 0 :(得分:0)

使用:

DataReader reader = new DataReader(...);
reader.InputStreamOptions = InputStreamOptions.Partial;
uint bytesLoaded = await reader.LoadAsync(12);

然后,LoadAsync()将加载1到12个字节之间的内容。如果它加载零字节,您可以假设流已结束。

还有其他InputStreamOptions,有时您可以将它们混合使用。