大家应该都知道,在与其他系统交互的时候,通常有两种方式,webservice和socket通信。
这个接口项目我使用的是socket通信方式,内容以xml方式包装,按照约定的格式收发。socket通信一般用到的为TCP和UDP两种,具体区别以及它们的优缺点可以参考资料http://jingyan.baidu.com/article/6dad5075df3452a123e36ecb.html,这里我采用TCP方式。
在C#中可以使用.net提供的TcpListener建立服务端,TcpClient建立客户端。正常情况,因为TCP是全双工,只要作为服务端或者客户端连接上就可以收发信息。但是这个项目中由于第三方系统历史遗留的问题,我们同时建立了服务端和客户端,服务端用来收消息,客户端用来发消息。
背景介绍完毕,下面上代码
服务端代码
/// <summary> /// 服务端类,用于接收客户端消息 /// </summary> class TcpListen { /// <summary> /// 连接上的客户端集合 /// </summary> List<RemoteClient> remoteClientList = new List<RemoteClient>(); /// <summary> /// 服务端监听对象 /// </summary> private TcpListener myListener; /// <summary> /// 构造函数 /// </summary> /// <param name="localIP"></param> /// <param name="port"></param> public TcpListen(string localIP,int port) { /// <summary>使用的本机IP地址</summary> IPAddress localAddress = IPAddress.Parse(localIP); myListener = new TcpListener(localAddress, port); myListener.Start(); //创建一个线程监听客户端连接请求 Thread myThread = new Thread(ListenClientConnect); myThread.Start(); } /// <summary> /// 接收客户端连接 /// </summary> private void ListenClientConnect() { while (true) { TcpClient newClient = null; try { newClient = myListener.AcceptTcpClient(); } catch { //当退出时AcceptTcpClient()会产生异常 //因此可以利用此异常退出循环 break; } //每接受一个客户端连接,就创建一个对应的线程循环接收该客户端发来的信息 RemoteClient user = new RemoteClient(newClient); remoteClientList.Add(user); Thread threadReceive = new Thread(ReceiveData); threadReceive.Start(user); } } /// <summary> /// 处理接收的客户端数据 /// </summary> /// <param name="userState">客户端信息</param> private void ReceiveData(object userState) { RemoteClient user = userState as RemoteClient; TcpClient client = user.client; while (true) { byte[] BytesLenth = null; byte[] BytesContent = null; try { if (user == null || user.br == null || user.client.Connected == false) { break; } BytesLenth = user.br.ReadBytes(2); int lenth = BitConverter.ToInt16(BytesLenth, 0); BytesContent = user.br.ReadBytes(lenth - 2); string conContent = Encoding.Default.GetString(BytesContent); //如果收到是心跳信息 if (conContent == "0000") { ClassVar.frmMain.timeSpan = 0; } //如果是警情信息则加入到消息队列 else { lock (ClassVar.frmMain.lockQue) { ClassVar.frmMain.queueXml.Enqueue(conContent); } } } catch (Exception ex) { RemoveUser(user); } } } /// <summary>移除用户</summary> /// <param name="user">指定要删除的用户</param> private void RemoveUser(RemoteClient remoteClient) { remoteClientList.Remove(remoteClient); remoteClient.Close(); }
/// <summary> /// 服务端管理远程客户端类 /// </summary> class RemoteClient { public TcpClient client{get; private set;} public BinaryReader br{get; private set;} public BinaryWriter bw{get; private set;} public string userName {get; set; } /// <summary> /// 构造函数 /// </summary> /// <param name="client"></param> public RemoteClient(TcpClient client) { this.client = client; NetworkStream networkStream = client.GetStream(); br = new BinaryReader(networkStream); bw = new BinaryWriter(networkStream); this.userName = (client.Client.RemoteEndPoint as IPEndPoint).Address.ToString() + ":"+(client.Client.RemoteEndPoint as IPEndPoint).Port.ToString(); } /// <summary> /// 关闭与远程客户端的连接 /// </summary> public void Close() { br.Close(); bw.Close(); client.Close(); } }
客户端
/// <summary> /// 客户端类,用来给服务端发送消息 /// </summary> class Client { #region 变量 /// <summary> /// tcp通信对象 /// </summary> private TcpClient tcpClient; /// <summary> /// tcp通信中读取数据的对象 /// </summary> private BinaryReader br; /// <summary> /// tcp通信中写数据的对象 /// </summary> private BinaryWriter bw; /// <summary> /// 通信的远程服务器ip /// </summary> private string IP; /// <summary> /// 通信的远程服务器端口 /// </summary> private int port; #endregion #region 构造函数 /// <summary> /// 构造函数 /// </summary> /// <param name="ip">服务器IP</param> /// <param name="port">服务器开放的通信端口号</param> public Client(string ip,int port) { this.IP = ip; this.port = port; } #endregion // 连接服务器 /// <summary> /// 连接服务器 /// </summary> public void Connect() { try { tcpClient = new TcpClient(IP, port); //获取网络流 NetworkStream networkStream = tcpClient.GetStream(); //将网络流作为二进制读写对象 br = new BinaryReader(networkStream); bw = new BinaryWriter(networkStream); } catch (Exception ex) { ClassVar.WriteErrorLog("连接服务器出错:" + Environment.NewLine + "Connect" + ex.ToString()); } //后台心跳消息线程 Thread threadHeart = new Thread(new ThreadStart(SendHeart)); threadHeart.IsBackground = true; threadHeart.Start(); } // 重连服务器 /// <summary> /// 重连 /// </summary> public void Reconnect() { try { if (tcpClient!=null) { tcpClient.Close(); } tcpClient = new TcpClient(IP, port); //获取网络流 NetworkStream networkStream = tcpClient.GetStream(); //将网络流作为二进制读写对象 br = new BinaryReader(networkStream); bw = new BinaryWriter(networkStream); } catch(Exception ex) { ClassVar.WriteErrorLog("重连服务器出错:" + Environment.NewLine + "Reconnect" + ex.ToString()); } } // 给服务器的心跳,10秒一次 /// <summary> /// 心跳,10秒一次 /// </summary> private void SendHeart() { while (true) { Thread.Sleep(10000); SendMsg("0000"); } } // 发送消息到服务器的方法,带发送长度 /// <summary> /// 发送消息到服务器的方法,带发送长度 /// </summary> /// <param name="msg">消息内容</param> public void SendMsg(string msgs) { try { //消息中前两个字节表示发送的消息长度 byte[] msg = Encoding.UTF8.GetBytes(msgs); int length = msg.Length; short lengthall = (short)(length + 2); byte[] lengthByte = System.BitConverter.GetBytes(lengthall); byte[] all = lengthByte.Concat(msg).ToArray(); //然后将字节数组写入网络流 if (bw != null && tcpClient.Connected==true) { bw.Write(all); bw.Flush(); if (msgs == "0000")//心跳写单独的文件 { ClassVar.WriteLogCommonXT(Environment.NewLine + "成功给系统发送数据:" + msgs); } else { ClassVar.WriteLogCommon(Environment.NewLine + "成功给系统发送数据:" + msgs); } } else { this.Reconnect(); } } catch (Exception ex) { ClassVar.WriteErrorLog("发送消息到服务器出错:" + Environment.NewLine + "SendMsg" + ex.ToString()); } } // 发送消息到服务器的方法,老方法,不带长度 /// <summary> /// 发送消息到服务器的方法,老方法,不带长度 /// </summary> /// <param name="msg">消息内容</param> public void SendOldMsg(string msg) { byte[] sendMsg = Encoding.UTF8.GetBytes(msg); //然后将字节数组写入网络流 bw.Write(sendMsg); bw.Flush(); } // 将无符号32位整数转换为长度为2的字节数组,大端对齐 /// <summary> /// 将无符号32位整数转换为长度为2的字节数组,大端对齐 /// </summary> /// <param name="value">无符号32位整数</param> /// <returns>长度为2的字节数组</returns> public byte[] ToBytesArray(int value) { byte[] buffer = new byte[2]; for (int i = 1; i >= 0; i--) { buffer[i] = (byte)(0x000000ff & value); value = value >> 8; } byte temp = buffer[0]; buffer[0] = buffer[1]; buffer[1] = temp; return buffer; } }
界面调用方法
/// <summary> /// 本地客户端对象 /// </summary> Client client; /// <summary> /// 第三方服务器IP地址 /// </summary> string _ServerIP; /// <summary> /// 第三方服务器端口 /// </summary> int _ServerPort; /// <summary> /// 第三方系统服务器给本机发送的心跳计时锁对象 /// </summary> public object objTimeSpan = new object(); /// <summary> /// 第三方系统服务器给本机发送的心跳计时 /// </summary> public int timeSpan = 0; /// <summary> /// 第三方系统服务器发送过来的消息队列锁对象 /// </summary> public object lockQue = new object(); /// <summary> /// 第三方系统服务器发送过来的消息队列 /// </summary> public Queue<string> queueXml = new Queue<string>(); // 初始化数据 /// <summary> /// 初始化数据 /// </summary> private void Ini() { //获取本机IPv4地址 string localIPAddr = GetLocalIP(); client = new Client(_ServerIP, _ServerPort); //连接第三方系统服务器,只用于发送消息用,发送心跳线程 client.Connect(); //与第三方系统服务器重连判断线程 Thread threadChenck = new Thread(new ThreadStart(CheckHeart)); threadChenck.IsBackground = true; threadChenck.Start(); //启动监听服务器,用于接收发送发送的消息 TcpListen _TcpListen = new TcpListen(localIPAddr, 11118); Thread threadRead = new Thread(ReadQueThread); threadRead.IsBackground = true; threadRead.Start(); } // 获取本机IPv4地址 /// <summary> /// 获取本机IPv4地址 /// </summary> /// <returns></returns> public static string GetLocalIP() { try { string HostName = Dns.GetHostName(); //得到主机名 IPHostEntry IpEntry = Dns.GetHostEntry(HostName); for (int i = 0; i < IpEntry.AddressList.Length; i++) { //从IP地址列表中筛选出IPv4类型的IP地址 //AddressFamily.InterNetwork表示此IP为IPv4, //AddressFamily.InterNetworkV6表示此地址为IPv6类型 if (IpEntry.AddressList[i].AddressFamily == AddressFamily.InterNetwork) { return IpEntry.AddressList[i].ToString(); } } return "127.0.0.1"; } catch (Exception ex) { return "127.0.0.1"; } } // 与第三方系统重连判断 /// <summary> /// 与第三方系统重连判断 /// </summary> private void CheckHeart() { while (true) { Thread.Sleep(3000); lock (objTimeSpan) { timeSpan = timeSpan + 2; } if (timeSpan >= maxTime) { client.Reconnect(); timeSpan = 0; } } } //读取第三方系统消息队列,处理后发送到协同 /// <summary> /// 读取第三方系统消息队列,处理后发送到协同 /// </summary> private void ReadQueThread() { while (true) { try { if (queueXml.Count > 0) { lock (lockQue) { string xml = queueXml.Dequeue(); //本地处理 FToLocal(xml); } } } catch (Exception ex) { ClassVar.WriteErrorLog("方法ReadQueThread出错,详细信息:" + ex.ToString()); } Thread.Sleep(5); } }
时间: 2024-10-12 17:41:00