首先,串口、UART口、COM口、USB口是指的物理接口形式(硬件)。而TTL、RS-232、RS-485是指的电平标准(电信号)。
串口:串口是一个泛称,UART、TTL、RS232、RS485都遵循类似的通信时序协议,因此都被通称为串口
工控中常用的协议:RS232 RS485
RS232:是电子工业协会(Electronic Industries Association,EIA) 制定的异步传输标准接口,同时对应着电平标准和通信协议(时序),其电平标准: 3V~ 15V对应0,-3V~-15V对应1。rs232 的逻辑电平和TTL 不一样但是协议一样
RS485:RS485是一种串口接口标准,为了长距离传输采用差分方式传输,传输的是差分信号,抗干扰能力比RS232强很多。两线压差为-(2~6)V表示0,两线压差为 (2~6)V表示1
常见的D型9针串口(通俗说法)。在台式电脑后边都可以看到。这种接口的协议只有两种:RS-232和RS-485
二.网口通讯方式TCP/IP 是互联网相关的各类协议族的总称,比如:TCP,UDP,IP,FTP,HTTP,ICMP,SMTP 等都属于 TCP/IP 族内的协议。像这样把与互联网相关联的协议集合起来总称为 TCP/IP。也有说法认为,TCP/IP 是指 TCP 和 IP 这两种协议。还有一种说法认为,TCP/IP 是在 IP 协议的通信过程中,使用到的协议族的统称。
工控中常用的通讯有TCP和UDP
这两个区别就是:TCP 用于在传输层有必要实现可靠传输的情况。由于它是面向有链接并具备顺序控制、重发控制等机制的,所以他可以为应用提供可靠的传输。 而在一方面,UDP 主要用于那些对高速传输和实时性有较高要求的通信或广播通信。 我们举一个通过 IP 电话进行通话的例子。如果使用 TCP,数据在传送途中如果丢失会被重发,但这样无法流畅的传输通话人的声音,会导致无法进行正常交流。而采用 UDP,他不会进行重发处理。从而也就不会有声音大幅度延迟到达的问题。即使有部分数据丢失,也支持会影响某一小部分的通话。此外,在多播与广播通信中也是用 UDP 而不是 TCP。
三.确定PLC硬件首先要确定PLC是否支持网口或者串口通信,大多数都支持RS232,TCP或者UDP,有的可能要购买拓展模块才能进行通信
四.确定PLC通信协议通信协议:是指双方实体完成通信或服务所必须遵循的规则和约定。通过通信信道和设备互连起来的多个不同地理位置的数据通信系统,要使其能协同工作实现信息交换和资源共享,它们之间必须具有共同的语言。交流什么、怎样交流及何时交流,都必须遵循某种互相都能接受的规则。这个规则就是通信协议。
以下以欧姆龙FINS通信协议为例
一,握手命令1、客户端向服务器发送命令00000000。这个命令长20字节,分成5组4字节。分别是:头(FINS) 长度(Hex0C) 命令(00000000) 错误码(00000000) 客户机节点地址。46494E53是FINS的ASCII码值,即命令头。0000000C是命令长度20。00000000是命令码。00000000是错误码。00000005是客户节点地址,即电脑IP地址的末位。在发送区输入:46494E53 0000000C 00000000 00000000 00000005点击发送,PLC立即回应:46494E53 00000010 00000001 00000000 00000005 00000020到此我们已经成功地完成了第一步!接下来需要的就是之前介绍过的HostLink协议里面FINS的知识了。
图3 网络调试助手 握手成功2、这个是服务器端(PLC)向客户端(电脑)发送的命令00000001。这个命令长24字节,分成6组4字节。分别是:头(FINS) 长度(Hex10) 命令(00000001) 错误码 客户机节点地址 服务器地址。上面的命令错误代码为0,客户端ip地址05已被服务器32(hex20)成功记录。如果发生错误,服务器回应的命令会包含错误码,连接断开,端口立刻关闭。当连接建立之后,不要再次发送这个命令,否则服务器会返回03错误码,即不支持的命令。全部的错误代码如下:十六进制错误码 含义00000000 正常00000001 头不是‘FINS’ (ASCII code)。00000002 数据太长。00000003 不支持的命令。00000020 所有的连接被占用。00000021 制定的节点已经连接。00000022 未被指定的IP地址试图访问一个被保护的节点。00000023 客户端FINS节点地址超范围。00000024 相同的FINS节点地址已经被使用。00000025 所有可用的节点地址都已使用。二、FINS帧发送命令如果向服务器发送FINS帧,就要用到这个命令。由于FINS帧长度是12-2012,因此命令长度可变,头(FINS) 长度 命令(00000002) 错误码 FINS帧。FINS命令帧内容可参考欧姆龙OMRON PLC之HostLink通讯协议-FINS命令W字/位操作篇,里面有存储区代码和操作代码的内容。例2-1、读DM0开始的2个通道:发送: 46494E53 0000001A 00000002 00000000 80000200 20000005 00FF0101 82000000 000220000005:20是目标地址,05是源地址;00FF0101 :0101是读操作;82000000:82是DM存储区代码,000000是起始地址;0002:是数量。返回: 46494E53 0000001A 00000002 00000000 C0000200 05000020 00FF0101 00001234 567800001234:0000代表操作成功,1234是读回的第一个字,即D0=Hex1234,5678:D1=Hex5678例2-2、W210寄存器写入Hex0388:发送: 46494E53 0000001C 00000002 00000000 80000200 20000005 00FF0102 B100D200 0001038820000005:20是目标地址,05是源地址;00FF0102:0102是写操作代码;B100D200:B1是W字代码,00D2是起始地址,Hex00D2=212,;00010388:是写入数量,0388是写入首个内容;回应: 46494E53 00000016 00000002 00000000 C0000200 05000020 00FF0102 00000102后面紧跟的0000代表写入成功。例2-3、W210寄存器读取:发送: 46494E53 0000001A 00000002 00000000 80000200 20000005 00FF0101 B100D200 000120000005:20是目标地址,05是源地址;00FF0101:0101是读操作代码;B100D200:B1是W字代码,00D2是起始地址,Hex00D2=212,;0001:是读取数量。回应: 46494E53 00000018 00000002 00000000 C0000200 05000020 00FF0101 000003880102后面紧跟的0000代表读取成功,W210=Hex0388例2-4、强制W212.01=On:发送: 46494E53 0000001C 00000002 00000000 80000200 20000005 00FF2301 00010001 3100D40120000005:20是目标地址,05是源地址;00FF2301:2301是强制操作代码;00010001:前面的0001是数量,后面的0001代表强制置位操作;3100D401:31是W位代码,00D401是起始地址,Hex00D4.01=212.01。回应: 46494E53 00000016 00000002 00000000 C0000200 05000020 00FF2301 00002301后面紧跟的0000表示操作成功。注意在CX-Programmer查看窗口中W212.01的值1后面的(强制)字样。
图4 网络调试助手 强制置位
图5 CX-Programmer 强制置位成功例2-5、强制W212.01=Off:发送: 46494E53 0000001C 00000002 00000000 80000200 20000005 00FF2301 00010000 3100D40120000005:20是目标地址,05是源地址;00FF2301:2301是强制操作代码;00010000:0001是数量,0000代表强制复位操作;3100D401:31是W位代码,00D401是起始地址,Hex00D4.01=212.01。回应: 46494E53 00000016 00000002 00000000 C0000200 05000020 00FF2301 00002301后面紧跟的0000表示操作成功。例2-6、取消W212.01强制:发送: 46494E53 0000001C 00000002 00000000 80000200 20000005 00FF2301 0001FFFF 3100D40120000005:20是目标地址,05是源地址;00FF2301:2301是强制操作代码;0001FFFF:0001是数量,FFFF代表取消强制操作;3100D401:31是W位代码,00D401是起始地址,Hex00D4.01=212.01。回应: 46494E53 00000016 00000002 00000000 C0000200 05000020 00FF2301 00002301后面紧跟的0000表示操作成功。注意在CX-Programmer查看窗口中W212.01的值0后面的(强制)字样不见了,表示已经成功地取消了强制。
图6 网络调试助手 取消强制
图7 CX-Programmer 取消强制成功
与CIO不同,对于W、A、H及D 这样的寄存器进行位操作,其实不用强制操作,直接写入更简洁,可以减少操作步骤,下面以W位操作为例介绍。
例2-7、W212.01 按位置位:
发送: 46494E53 0000001B 00000002 00000000 80000200 20000005 00FF0102 3100D401 00010120000005:20是目标地址,05是源地址;00FF0102:0102是寄存器写操作代码;3100D401:31是W位代码,00D401是地址,Hex00D4.01=212.01;
000101:0001是数量,01代表写入值1;回应:
46494E53 00000016 00000002 00000000 C0000200 20000005 00FF0102 0000
0102后面紧跟的0000表示操作成功。
例2-8、W212.01 按位复位:发送: 46494E53 0000001B 00000002 00000000 80000200 20000005 00FF0102 3100D401 000100
20000005:20是目标地址,05是源地址;00FF0102:0102是寄存器写操作代码;3100D401:31是W位代码,00D401是地址,Hex00D4.01=212.01;
000100:0001是数量,00代表写入值0;
回应:
46494E53 00000016 00000002 00000000 C0000200 20000005 00 FF0102 0000
0102后面紧跟的0000表示操作成功。
五.使用C#进行通信1.串口
串口在C#中使用的是SerialPort这个类
要注意通讯协议中报文内容是16进制,还是ASCII码,还是就是普通的字符串,有格式的话注意发送和接收的报文需要转换
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
using System.Threading;
using System.Windows.Forms;
public class SerialOP : IDisposable
{
private SerialPort Comport = new SerialPort();
private AutoResetEvent _manualEvent = new AutoResetEvent(false);
private object _lockRead = new object();
private object _lockWrite = new object();
private byte[] _reivedData;
private string reive=null;
private object onRecive =new object();
public delegate void UpdateByteDelegate(byte[] reive);
public event UpdateByteDelegate UpdateByte;
/// <summary>
/// 打开串口
/// </summary>
/// <param name="com"></param>
/// <param name="bps"></param>
/// <param name="databit"></param>
/// <param name="stopbit"></param>
/// <param name="check"></param>
public bool Open(string com, string bps, string databit, string stopbit, string check)
{
try
{
if (Comport.IsOpen)
Comport.Close();
Thread.Sleep(100);
//串口端口号
Comport.PortName = com;
//串口波特率
Comport.BaudRate = int.Parse(bps);
//串口数据位
Comport.DataBits = int.Parse(databit);
//串口停止位
switch (stopbit)
{
case "0":
Comport.StopBits = StopBits.None;
break;
case "1":
Comport.StopBits = StopBits.One;
break;
case "1.5":
Comport.StopBits = StopBits.OnePointFive;
break;
case "2":
Comport.StopBits = StopBits.Two;
break;
default:
Comport.StopBits = StopBits.None;
break;
}
//串口奇偶校验
switch (check)
{
case "无":
Comport.Parity = Parity.None;
break;
case "奇校验":
Comport.Parity = Parity.Odd;
break;
case "偶校验":
Comport.Parity = Parity.Even;
break;
default:
Comport.Parity = Parity.None;
break;
}
//Comport.ReceivedBytesThreshold = 1;//1字节触发数据获取
//Comport.DataReceived = new SerialDataReceivedEventHandler(OnReceived);
Comport.Open();
Comport.DtrEnable = true;
Comport.RtsEnable = true;
return true;
}
catch (Exception ex)
{
LogHelper.error(ex.ToString());
MessageBox.Show("连续打开关闭串口出现错误:" ex.Message,"SerialPort---Message", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
return false;
}
}
/// <summary>
/// 检测串口是否打开
/// </summary>
/// <returns></returns>
private bool IsOpen()
{
try
{
if (Comport == null)
return false;
if (Comport.IsOpen)
return true;
else
return false;
}
catch (Exception ex)
{
LogHelper.error(ex.ToString());
MessageBox.Show("检测串口是否打开出现错误:" ex.Message, "SerialPort---Message", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
return false;
}
}
private void OnReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
lock (onRecive)
{
byte[] reive = new byte[Comport.BytesToRead];
Comport.Read(reive, 0, Comport.BytesToRead);
UpdateByte.Invoke(reive);
}
}
catch (Exception ex)
{
LogHelper.error(ex.ToString());
MessageBox.Show("串口接收数据出现错误:" ex.Message, "SerialPort---Message", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
}
finally
{
_manualEvent.Set();
}
}
/// <summary>
/// 字符串转ASII码发送
/// </summary>
/// <returns></returns>
public void Send_ASCII(string str)
{
try
{
lock (_lockWrite)
{
var ByteSendW = Encoding.ASCII.GetBytes(str );//把发送数据转换为ASCII数组
Comport.Write(ByteSendW, 0, ByteSendW.Length);
}
}
catch (Exception ex)
{
LogHelper.error(ex.ToString());
MessageBox.Show("串口发送数据出现错误:" ex.Message, "SerialPort---Message", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
}
}
/// <summary>
/// 发送16进制数组,需要用,隔开
/// </summary>
/// <param name="Hex"></param>
/// <param name="check"></param>
public void Send_HEX(string Hex)
{
try
{
lock (_lockWrite)
{
//16进制字符串转换成16进制数组
var HEX = Hex.Split(',').Select(temp => "0x" temp).Select(temp => (byte)Convert.ToInt32(temp, 16)).ToArray();
Comport.Write(HEX, 0, HEX.Length);
}
}
catch (Exception ex)
{
LogHelper.error(ex.ToString());
MessageBox.Show("串口发送数据出现错误:" ex.Message, "SerialPort---Message", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
}
}
/// <summary>
/// 发送字符串不转换
/// </summary>
/// <param name="str"></param>
public void Send_String(string str)
{
try
{
lock (_lockWrite)
{
Comport.Write(str);
}
}
catch (Exception ex)
{
LogHelper.error(ex.ToString());
MessageBox.Show("串口发送数据出现错误:" ex.Message, "SerialPort---Message", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
}
}
/// <summary>
/// 接收ASCII码,并转换成字符串
/// </summary>
/// <param name="str"></param>
public void Read_ASCII(out string str)
{
str = "error";
try
{
lock (_lockRead)
{
if (Comport.BytesToRead == 0)
{
LogHelper.error("缓存区没有数据");
return;
}
for (int i = 0; i < 100; i )//读取100个字节
{
if (Comport.BytesToRead == 0)
{
str = reive;
reive = null;
break;
}
string reivestring = "";
_reivedData = new byte[1];
Comport.Read(_reivedData, 0, _reivedData.Length);
reivestring = Encoding.ASCII.GetString(_reivedData);
reive = reivestring;
//Comport.DiscardInBuffer();
}
}
}
catch (Exception ex)
{
str = ex.ToString();
LogHelper.error(ex.ToString());
MessageBox.Show("串口读取数据出现错误:" ex.Message, "SerialPort---Message", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
}
}
/// <summary>
/// 接收字符串,不进行任何转换
/// </summary>
/// <param name="str"></param>
public void Read_String(out string str)
{
str = "error";
try
{
lock (_lockRead)
{
if (Comport.BytesToRead == 0)
{
LogHelper.error("缓存区没有数据");
return;
}
for (int i = 0; i < 100; i )//读取100个字节
{
if (Comport.BytesToRead == 0)
{
str = reive;
reive = null;
break;
}
_reivedData = new byte[1];
Comport.Read(_reivedData, 0, _reivedData.Length);
reive = _reivedData[0].ToString() " ";
}
}
}
catch (Exception ex)
{
str = ex.ToString();
LogHelper.error(ex.ToString());
MessageBox.Show("串口读取数据出现错误:" ex.Message, "SerialPort---Message", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
}
}
/// <summary>
/// 接收到16进制,转成字符串
/// </summary>
/// <param name="str"></param>
public void Read_HEX_String(out string str)
{
str = "error";
try
{
lock (_lockRead)
{
if (Comport.BytesToRead == 0)
{
LogHelper.error("缓存区没有数据");
return;
}
for (int i = 0; i < 100; i )//读取100个字节
{
if (Comport.BytesToRead == 0)
{
reive = reive.Substring(0, reive.Length - 1);//移除最后一位逗号
str = reive;
reive = null;
break;
}
_reivedData = new byte[1];
Comport.Read(_reivedData, 0, _reivedData.Length);
reive = _reivedData[0].ToString("x2").ToUpper() ",";
}
}
}
catch (Exception ex)
{
str = ex.ToString();
LogHelper.error(ex.ToString());
MessageBox.Show("串口读取数据出现错误:" ex.Message, "SerialPort---Message", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
}
}
/// <summary>
/// 关闭串口
/// </summary>
public void Close()
{
try
{
if (Comport.IsOpen)
Comport.Close();
}
catch (Exception ex)
{
LogHelper.error(ex.ToString());
MessageBox.Show("关闭串口出现错误:" ex.Message, "SerialPort---Message", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
}
}
/// <summary>
/// 释放串口
/// </summary>
public void Dispose()
{
try
{
if (Comport.IsOpen)
Comport.Close();
Comport.Dispose();
}
catch (Exception ex)
{
LogHelper.error(ex.ToString());
MessageBox.Show("释放串口出现错误:" ex.Message, "SerialPort---Message", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, MessageBoxOptions.DefaultDesktopOnly);
}
//Comport.DataReceived -= new SerialDataReceivedEventHandler(Comport_DataReceived);
}
/// <summary>
/// CRC16校验
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public string CRCCalc(string data)
{
string[] datas = data.Split(' ');
List<byte> bytedata = new List<byte>();
foreach (string str in datas)
{
bytedata.Add(byte.Parse(str, System.Globalization.NumberStyles.AllowHexSpecifier));
}
byte[] crcbuf = bytedata.ToArray();
//计算并填写CRC校验码
int crc = 0xffff;
int len = crcbuf.Length;
for (int n = 0; n < len; n )
{
byte i;
crc = crc ^ crcbuf[n];
for (i = 0; i < 8; i )
{
int TT;
TT = crc & 1;
crc = crc >> 1;
crc = crc & 0x7fff;
if (TT == 1)
{
crc = crc ^ 0xa001;
}
crc = crc & 0xffff;
}
}
string[] redata = new string[2];
redata[1] = Convert.ToString((byte)((crc >> 8) & 0xff), 16);
redata[0] = Convert.ToString((byte)((crc & 0xff)), 16);
return redata[0].ToUpper() " " redata[1].ToUpper();
}
}
2.网口
网口分客户端和服务器,协议有TCP和UDP
以下以TCP为例
TCP客户端
public class TcpClient
{
public bool connected =false ;
Socket socketClient;//客户端接口
Task threadAcceptClient,//客户端接收线程
threadClient; //客户端线程
private string IpAddress = string.Empty;
private int Port = 0;
public TcpClient(string IpAddress, int Port)
{
this.IpAddress = IpAddress;
this.Port = Port;
ClientConnectSever();
}
/// <summary>
/// 客户端连接服务器
/// </summary>
public bool ClientConnectSever()
{
IPAddress ip = IPAddress.Parse(IpAddress);
IPEndPoint port = new IPEndPoint(ip, Port);//服务器port
//创建TCP类型端口
socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
socketClient.Connect(port);
connected = true;
return true;
}
catch (Exception ex)
{
LogHelper.error(IpAddress "连接服务器失败:" ex.Message);
return false;
}
}
public void ColseConnect()
{
socketClient.Close();
}
public bool ClientSendData(string data)
{
if (!connected)
{
LogHelper.error(IpAddress "未连接到服务器");
return false;
}
byte[] msg = Encoding.Default.GetBytes(data);
try
{
NetworkStream netStream = new NetworkStream(socketClient);
netStream.Write(msg, 0, msg.Length);
netStream.Flush();
return true;
}
catch (Exception ex)
{
LogHelper.error(IpAddress "发送失败:" ex.Message);
return false;
}
}
public string ClientReadData()
{
try
{
if (!connected)
{
LogHelper.error(IpAddress "未连接到服务器");
return "error";
}
NetworkStream netStream = new NetworkStream(socketClient);
byte[] dataSize = new byte[1024];
netStream.Read(dataSize, 0, dataSize.Length);
var Result = Encoding.Default.GetString(dataSize).TrimEnd('\0');
if (Result.Length > 1)
return Result;
else
return string.Empty;
//this.rtb_accept1.Rtf = Encoding.Unicode.GetString(message);
}
catch (Exception ex)
{
LogHelper.error(IpAddress "读取数据失败:" ex.Message);
return "error";
}
}
}
TCP服务器
public class TcpServer
{
public delegate void UpdateObjectDelegate(object sender);
public event UpdateObjectDelegate UpdataDataString;
public event UpdateObjectDelegate UpdateMessage;
private string IpAddress = string.Empty;
private int Port = 0;
public TcpServer(string IpAddress, int Port)
{
this.IpAddress = IpAddress;
this.Port = Port;
}
static Socket serverSocket;
List<Socket> ClientList = new List<Socket>(); //客户端列表
public bool connected = false;
public void StartListen()
{
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket = socket;
//ip port
socket.Bind(new IPEndPoint( IPAddress.Parse(IpAddress), Port));
//listen
socket.Listen(10);//连接等待队列
ThreadPool.QueueUserWorkItem(new WaitCallback(this.AcceptClientConnect), socket);
}
private void AcceptClientConnect(object socket)
{
var serverSocket = socket as Socket;
UpdateMessage.Invoke("服务器开始监听");
while (true)
{
try
{
var proxSocket = serverSocket.Accept();
ClientList.Add(proxSocket);
UpdateMessage.Invoke((object)("客户端" proxSocket.RemoteEndPoint.ToString() "连接上了"));
//接受消息
ThreadPool.QueueUserWorkItem(new WaitCallback(this.ReceiveData), proxSocket);
connected = true;
}
catch (Exception e)
{
UpdateMessage.Invoke("服务器监听错误:" e.ToString());
connected = false;
}
}
}
private void ReceiveData(object obj)
{
Socket proxSocket = obj as Socket;
byte[] data = new byte[1024 * 1024];
while (true)
{
int readLen = 0;
try
{
readLen = proxSocket.Receive(data, data.Length,0);
//if (readLen <= 0)
//{
// //客户端正常退出
// UpdateMessage.Invoke(string.Format("客户端{0}正常退出", proxSocket.RemoteEndPoint.ToString()));
// ClientList.Remove(proxSocket);
// CloseListen(proxSocket);
// connected = false;
// return;//方法结束->终结当前接受客户端数据的异步线程
//}
string txt = Encoding.Default.GetString(data, 0, readLen).TrimEnd('\0');
if(txt.Length>1)
UpdataDataString.Invoke(txt);
}
catch (Exception ex)
{
//异常退出时
UpdateMessage.Invoke(string.Format("客户端{0}非正常退出,原因{1}", proxSocket.RemoteEndPoint.ToString(), ex.ToString()));
ClientList.Remove(proxSocket);
CloseListen(proxSocket);
connected = false;
return;
}
}
}
private void CloseListen(Socket proxSocket)
{
try
{
if (proxSocket.Connected)
{
proxSocket.Shutdown(SocketShutdown.Both);
proxSocket.Close(100);
connected = false;
}
}
catch (Exception)
{
UpdateMessage("服务器关闭发生异常");
connected = false;
}
}
public bool ServerSendData(string msg)
{
try
{
foreach (Socket s in this.ClientList)
{ //服务端广播式发送给客户端
(s as Socket).Send(Encoding.Default.GetBytes(msg));
}
return true;
}
catch
{
return false;
}
}
}