System.IO系列:局域网内多线程使用命名管道在进程之间通信实例
有关管道的基本用法请看System.IO之使用管道在进程间通信 (System.IO.Pipes使用)。
本文介绍命名管道使用实例,文中例子是几个客户端都通过一台服务器获得新生成的int类型id。
服务器端功能:当客户端请求一个新的id时,将现有id自增1,然后返回给客户端。
服务器端实现:在程序启动时,启动n个线程,在每个线程中都声明一个NamedPipeServerStream的实例,并循环的WaitForConnection(),将新的id写入到命名管道中,然后断开连接。在程序退出时释放NamedPipeServerStream实例
如下代码实现:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.IO.Pipes; using System.Threading; namespace IDServer { class Program { /// <summary> /// 命名管道名字 /// </summary> private const string PIPE_NAME = "testNetworkPipe"; //定义线程数,也是NamedPipeServerStream的允许最多的实例数 private const int MAX_THREADS_COUNT = 3; private static volatile int _runingThreadCount = 0; private static volatile int _newId = 0; //实例数组 private static NamedPipeServerStream[] _serverStreams; static void Main(string[] args) { _serverStreams = new NamedPipeServerStream[MAX_THREADS_COUNT]; //在进程退出时释放所有NamedPipeServerStream实例 AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit); //启动线程 StartServers(); Console.Read(); } /// <summary> /// 在进程退出时释放命名管道 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> static void CurrentDomain_ProcessExit(object sender, EventArgs e) { if (_serverStreams != null) { foreach (NamedPipeServerStream item in _serverStreams) { item.Dispose(); } } } /// <summary> /// 启动服务器端线程 /// </summary> private static void StartServers() { for (int i = 0; i < MAX_THREADS_COUNT; i++) { Thread thread = new Thread(new ThreadStart(StartNewIDServer)); thread.Start(); } } /// <summary> /// 启动一个NamedPipeServerStream实例 /// </summary> private static void StartNewIDServer() { NamedPipeServerStream stream = null; Console.WriteLine("start server in thread " + Thread.CurrentThread.ManagedThreadId); stream = _serverStreams[_runingThreadCount] = new NamedPipeServerStream(PIPE_NAME, PipeDirection.InOut, MAX_THREADS_COUNT, PipeTransmissionMode.Message, PipeOptions.None); int threadNo = _runingThreadCount; _runingThreadCount += 1; while (true) { stream.WaitForConnection(); int newId = ++_newId; byte[] bytes = BitConverter.GetBytes(newId); stream.Write(bytes, 0, bytes.Length); stream.Flush(); Console.Write("threadNo:" + Thread.CurrentThread.ManagedThreadId + "\r"); stream.Disconnect(); } } } }
客户端的功能是不断的发出获得新id的请求,并打印新id,在客户端可以配置服务端的服务器IP。
如下代码:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace IDClient { class Program { private const string PIPE_NAME = "testNetworkPipe"; static void Main(string[] args) { Console.WriteLine("请输入任何字符回车开始执行程序.."); Console.Read(); do { //内网服务器ip,必须是局域网 string serverName = "127.0.0.1"; //声明NamedPipeClientStream实例 using (var clientStream = new System.IO.Pipes.NamedPipeClientStream(serverName, PIPE_NAME)) { //连接服务器 clientStream.Connect(1000); //设置为消息读取模式 clientStream.ReadMode = System.IO.Pipes.PipeTransmissionMode.Message; do { byte[] bytes = new byte[4]; clientStream.Read(bytes, 0, 4); int val = BitConverter.ToInt32(bytes, 0); Console.Write("NewID == " + val + "\r"); } while (!clientStream.IsMessageComplete); } Thread.Sleep(1); } while (true); } } }
在sql server中就使用了命名管道在局域网内挂进程通讯。
在声明NamedPipeServerStream实例是可以指定其实例个数,如果实例数超过这个数,就会抛出“所有管道范例都在使用中”的IO异常。
本例不可以在实际项目中使用。
推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架