【问题标题】:Waiting for messages on a StreamSocket in C#/Win8在 C#/Win8 中等待 StreamSocket 上的消息
【发布时间】:2012-10-20 10:41:59
【问题描述】:

我正在尝试作为一个辅助项目在 Windows 8 中构建 IRC 客户端,作为学习网络编程的一种方式。但是,我找不到构建监听器的最佳方式。

我有一个创建 StreamSocket 并连接到服务器的类。现在我需要让我的班级监听来自服务器的传入消息,并在消息传入时回调委托。据我所知,StreamSocket 只给了我一种方法来拉取当前在 Socket 上等待的任何内容,但对传入消息没有某种回调。最好的方法是什么?

【问题讨论】:

  • 能否展示一些当前状态的代码(针对问题)
  • 老实说,代码几乎还没有。它只需要一些变量并创建一个 StreamSocket。这就是我所拥有的。与其尝试调试我的代码,不如说是如何构建它的问题。

标签: c# windows sockets windows-8 windows-runtime


【解决方案1】:

我想您会将其实现为 Windows 服务,因此您的代码将与以下内容很相似。其中大部分是让您轻松调试服务的基础设施。我知道它会污染示例,但没有它是太多的工作。

服务实现在库中而不是直接在服务主机中,因为我经常编写通过 WCF 发布接口的服务,并且总是将主机与服务分开更容易。

服务主机

using IrcServiceLibrary;
using System.Collections.Generic;
using System.ServiceProcess;
using System.Threading;
using System.Xml;

namespace IrcServiceHost
{
  internal static class Program
  {
    /// <summary>
    /// Launches as an application when an argument "app" is supplied.
    /// Otherwise launches as a Windows Service.
    /// </summary>
    private static void Main(string[] arg)
    {
      var args = new List<string>(arg);
      ServiceBase[] ServicesToRun = new ServiceBase[]
      {
        new IrcServiceLibrary.Irc(
            Properties.Settings.Default.IrcTcpPort)
          };
      if (args.Contains("app"))
      {
        foreach (IExecutableService es in ServicesToRun)
          es.StartService();
        Thread.Sleep(Timeout.Infinite);
      }
      else
      {
        ServiceBase.Run(ServicesToRun);
      }
    }
  }
}

会话建立

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using System.ServiceModel;
using System.ServiceProcess;
using System.Threading;

namespace IrcServiceLibrary
{
  /// <summary>
  /// Listens for TCP connection establishment and creates an 
  /// IrcSession object to handle the test session.
  /// </summary>
  [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single)]
  public partial class Irc : ServiceBase, IExecutableService
  {
    AutoResetEvent _autoResetEvent = new AutoResetEvent(false);
    TcpListener _tcpListener;
    List<IrcSession> _sessions = new List<IrcSession>();

    /// <summary>
    /// Creates Irc and applies configuration supplied by the service host.
    /// </summary>
    /// <param name="tcpPort">Port on which the session is established.</param>
    public Irc(int tcpPort)
    {
      Trace.TraceInformation("NetReady service created {0}", GetHashCode());
      InitializeComponent();
      _tcpListener = new TcpListener(IPAddress.Any, tcpPort); 
      NetReadySession.Sessions = _sessions;
    }

    void AcceptTcpClient(IAsyncResult asyncResult)
    {
      var listener = asyncResult.AsyncState as TcpListener;
      var tcpClient = listener.EndAcceptTcpClient(asyncResult);
      try
      {
        new NetReadySession(tcpClient);
      }
      catch (IndexOutOfRangeException)
      {
        //no free session - NetReadySession ctor already informed client, no action here
      } 
      _tcpListener.BeginAcceptTcpClient(AcceptTcpClient, _tcpListener);
    }

    /// <summary>
    /// <see cref="StartService">ServiceStart</see> 
    /// </summary>
    /// <param name="args">Arguments passed by the service control manager</param>
    protected override void OnStart(string[] args)
    {
      StartService();
    }

    /// <summary>
    /// <see cref="StopService">ServiceStop</see> 
    /// </summary>
    protected override void OnStop()
    {
      StopService();
    }

    void Execute(object state)
    {
      Trace.TraceInformation("IRC service started");
      _tcpListener.Start();
      _tcpListener.BeginAcceptTcpClient(AcceptTcpClient, _tcpListener);
      _autoResetEvent.WaitOne();
      _tcpListener.Stop();
    }

    internal static int UdpPortLow { get; set; }

    internal static int UdpPortHigh { get; set; }

    /// <summary>
    /// Starts the service. OnStart uses this method to implement startup behaviour.
    /// This guarantees identical behaviour across application and service modes.
    /// </summary>
    public void StartService()
    {
      ThreadPool.QueueUserWorkItem(Execute);
    }

    /// <summary>
    /// Stops the service. OnStop uses this method to implement shutdown behaviour.
    /// This guarantees identical behaviour across application and service modes.
    /// </summary>
    public void StopService()
    {
      _autoResetEvent.Set();
    }
  }
}

会话行为

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace IrcServiceLibrary
{
  public class IrcSession
  {    
    /// <summary>
    /// Exists to anchor sessions to prevent premature garbage collection
    /// </summary>
    public static List<IrcSession> Sessions;
    TcpClient _tcpClient;
    NetworkStream _stream;
    byte[] _buf; //remain in scope while awaiting data

    /// <summary>
    /// Created dynamically to manage an Irc session. 
    /// </summary>
    /// <param name="tcpClient">The local end of the TCP connection established by a test client 
    /// to request and administer a test session.</param>
    public IrcSession(TcpClient tcpClient)
    {
      Sessions.Add(this);
      _tcpClient = tcpClient;
      _stream = _tcpClient.GetStream();
      _buf = new byte[1];
      _stream.BeginRead(_buf, 0, 1, IncomingByteHandler, _buf);
    }

    void IncomingByteHandler(IAsyncResult ar)
    {
      byte[] buf = ar.AsyncState as byte[];
      try
      {
        byte[] buf = ar.AsyncState as byte[];
        int cbRead = _stream.EndRead(ar);
        //do something with the incoming byte which is in buf
        //probably feed it to a state machine
      }
      finally
      {
        //restart listening AFTER digesting byte to ensure in-order delivery to this code
        _stream.BeginRead(buf, 0, 1, IncomingByteHandler, buf);
      }
    }

  }
}

【讨论】:

  • 不幸的是,这是针对 Metro/Windows 8/WinRT,所以没有 TCPClient 之类的。
  • 不同?没有 TcpClient?哇。我的 Surface RT 今天出现了,所以我想我会试一试。
  • 当我在我的 Windows 手机上试验 Silverlight 时,我通过将调用委托给托管在数据中心的一个盒子上的 Windows 服务中的 RIA 服务解决了同样的问题,这两者都消除了所有烦人的约束并将脆弱的网络代码置于低延迟和高带宽的受控环境中。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-04-23
  • 1970-01-01
  • 1970-01-01
  • 2015-11-01
  • 1970-01-01
  • 2020-07-02
  • 1970-01-01
相关资源
最近更新 更多