[英]Waiting for messages on a StreamSocket in C#/Win8
I'm trying as a side project to build an IRC client in Windows 8 as a way to learn Network programming. 我正在尝试作为辅助项目在Windows 8中构建IRC客户端,作为学习网络编程的一种方式。 However, I can't find the best way to structure the listener.
但是,我找不到构造侦听器的最佳方法。
I have a class that creates a StreamSocket and connects to the server. 我有一个创建StreamSocket并连接到服务器的类。 Now I need to have my class listen for incoming messages from the server, and call back to a delegate when a message comes in. As far as I can tell, StreamSocket only gives me a way to pull whatever is currently waiting on the Socket, but not have some sort of a callback on an incoming message.
现在我需要让我的类侦听来自服务器收到的消息,并在消息传入回调委托。据我所知,StreamSocket只给我一个方法来拉任何当前等待的插座,但没有对传入消息的某种回调。 What is the best way to do this?
做这个的最好方式是什么?
I imagine you will implement this as a Windows Service, so your code will look a lot like the below. 我想您将把它实现为Windows服务,因此您的代码看起来很像下面的代码。 Much of this is infrastructure to let you debug a service easily.
其中大部分是基础架构,可让您轻松调试服务。 I know it pollutes the example but not having it is far too much work.
我知道这会污染示例,但没有它会做太多工作。
The service implementation is in a library rather than directly in the service host because I often write services that publish interfaces via WCF and it's less hassle to always separate host from service. 服务实现是在库中而不是直接在服务主机中,因为我经常编写通过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);
}
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.