/// <summary> /// 解析CSI类命令 /// </summary> /// <param name="data"></param> bool ParseCSICommand(RingQueue <byte> data) { /** * 命令基本为字母结尾 * h: 模式设定(SET) * l: 模式设定(RESET) * A: 光标上移指定行 CSI [num] A * B: 光标下移指定行 CSI [num] B * C: 光标左移指定列 CSI [num] C * D: 光标右移指定列 CSI [num] D * H: 指定光标位置 CSI [num|column] ; [num|row] H * f: 同H * g: 清除一个水平Tab停止位置 CSI [num] g * null/0: 清除当前位置的水平Tab停止 * 3:清除所有 * m: 设置图形渲染模式 CSI [num] ; [num] ... m * 0/null: 关闭所有 * 1: 粗体 * 4: 下划线 * 5: 闪烁 * 7: 反转显示 * 2 2: 一般 * 2 4: 不显示下划线 * 2 5: 不显示闪烁 * 2 7: 不反转 * L: 光标指定位置插入指定行 CSI [num] L * M: 从光标所在行开始删除指定行 CSI [num] M * @: 从光标当前位置插入指定个字符(仅VT200) CSI [num] @ * P: 从光标位置开始删除指定个字符 CSI [num] P * K: 删除字符 CSI [num] K * null/0: 删除光标所在行右侧的字符(包含光标字符) * 1: 删除光标所在行左侧的字符(包含光标字符) * 2: 删除整行 * * ? null/0: 删除光标所在行右侧的字符(包含光标字符|仅VT200) * ? 1: 删除光标所在行左侧的字符(包含光标字符|仅VT200) * ? 2: 删除整行(仅VT200) * J: 删除字符 CSI [num] J * null/0: 删除光标到屏幕结束的字符(包含光标字符) * 1: 删除光标到屏幕开始的字符(包含光标字符) * 2: 清除屏幕 * r: 设置上下的页边距 CSI [top] ; [bottom] r * i: 打印设定 CSI [num] i * ? 5: 开启自动打印模式 * ? 4: 关闭自动打印模式 * 5: 开启打印控制模式 * 4: 关闭打印控制模式 * ? 1: 打印当前行 * null/0: 打印当前屏幕 * */ while (!Encoding.ASCII.GetBytes("hlABCDHgmLMPJK@rif").Contains(data.GetNextValue()) && data.Current != data.FOOT) { ; } if (!Encoding.ASCII.GetBytes("hlABCDHgmLMPJK@rif").Contains(data.Value)) { return(false); } var cmdbytes = data.LTrim().Select(o => Convert.ToByte(o)).ToArray(); var cmd = Encoding.ASCII.GetString(cmdbytes, 2, cmdbytes.Length - 3); switch (cmdbytes.Last()) { case 0x66: //f if (cmdbytes.Length > 3) { if (cmd == ";") { Screen.CursorSetPos(0, 0); } else { var wh = cmd.Split(';'); wh[0] = wh[0] == "" ? "1" : wh[0]; wh[1] = wh[1] == "" ? "1" : wh[1]; Screen.CursorSetPos(int.Parse(wh[0]) - 1, int.Parse(wh[1]) - 1); } } break; case 0x48: //H if (cmdbytes.Length > 3) { if (cmd == ";") { Screen.CursorSetPos(0, 0); } else { var wh = cmd.Split(';'); Screen.CursorSetPos(int.Parse(wh[0]) - 1, int.Parse(wh[1]) - 1); } } break; case 0x4A: //J if (cmdbytes.Length == 3) { Screen.ScreenClearAfterCursor(); } else { switch (cmd) { case "0": Screen.ScreenClearAfterCursor(); break; case "1": Screen.ScreenClearBeforeCursor(); break; case "2": Screen.ScreenClear(); break; } } break; case 0x4B: //K if (cmdbytes.Length == 3) { Screen.LineClearAfterCursor(); } else { switch (cmd) { case "0": Screen.LineClearAfterCursor(); break; case "1": Screen.LineClearBeforeCursor(); break; case "2": Screen.LineClear(); break; } } break; case 0x68: //h Logger.Debug("模式设定(SET):" + cmd.Substring(1)); break; case 0x6C: //l Logger.Debug("模式设定(RESET):" + cmd.Substring(1)); break; case 0x6D: //m if (cmdbytes.Length == 3) { Screen.IsBold = false; Screen.IsUnderline = false; Screen.IsBlinking = false; Screen.IsReverse = false; } else { foreach (var v in cmd.Split(';')) { switch (v) { case "": case "0": Screen.IsBold = false; Screen.IsUnderline = false; Screen.IsBlinking = false; Screen.IsReverse = false; break; case "1": Screen.IsBold = true; break; case "4": Screen.IsUnderline = true; break; case "5": Screen.IsBlinking = true; break; case "7": Screen.IsReverse = true; break; case "22": Screen.IsBold = false; break; case "24": Screen.IsUnderline = false; break; case "25": Screen.IsBlinking = false; break; case "27": Screen.IsReverse = false; break; } } } Logger.Debug("样式渲染:" + cmd); break; case 0x69: // i if (cmdbytes.Length == 3) { Logger.Debug("打印当前屏幕"); } else { switch (cmd) { case "5": if (PrintFileName == "") { PrintFileName = Environment.CurrentDirectory + "\\print.txt"; } OnBeginPrint(new PrintEventArgs(PrintFileName)); IsPrinting = true; Output = new System.IO.FileStream(PrintFileName, System.IO.FileMode.Create); TickCount = Environment.TickCount; Logger.Warn(string.Format("开始输出打印数据({0})", PrintFileName)); break; case "4": Logger.Warn("输出打印数据完成"); IsPrinting = false; Output.Flush(); Output.Close(); Output = null; OnEndPrint(new PrintEventArgs(PrintFileName)); Logger.Info("共耗时: " + (Environment.TickCount - TickCount) / 1000.0F + " 秒"); break; } } break; default: Logger.Error("无法识别CSI指令: " + Encoding.ASCII.GetString(cmdbytes)); break; } return(true); }
/// <summary> /// 处理Telnet命令并返回数据 /// </summary> /// <param name="recv"></param> /// <param name="send"></param> /// <param name="data"></param> void ParseTelnetCommand(RingQueue <byte> recv, RingQueue <byte> send, RingQueue <byte> data) { while (recv.Count > 0 && data.Available > 0) { var b = recv.Shift(); if (b == IAC) { //命令处理 if (recv.Count < 2) { recv.Unshift(b); break; } var action = recv.Shift(); var option = recv.Shift(); switch (option) { case ECHO: case NOGA: case TERMTYPE: case NAWS: switch (action) { case WILL: send.PushValues(new byte[] { IAC, DO, option }); break; case DO: send.PushValues(new byte[] { IAC, WILL, option }); break; case SB: switch (option) { case TERMTYPE: if (recv.Count < 3) { recv.UnshiftValues(new byte[] { option, action, b }); break; } if (recv.Shift() == 1) { recv.Shift(); recv.Shift(); //发送客户端型号 send.PushValues(new byte[] { IAC, SB, TERMTYPE, 0 }); send.PushValues(Encoding.Default.GetBytes(TermType)); send.PushValues(new byte[] { IAC, SE }); } break; } break; } if (action == WILL && option == NAWS) { //发送窗体大小 send.PushValues(new byte[] { IAC, SB, NAWS, (byte)((ScreenColumns >> 8) & 0xFF), (byte)(ScreenColumns & 0xFF), (byte)((ScreenRows >> 8) & 0xFF), (byte)(ScreenRows & 0xFF), IAC, SE }); } break; default: //拒绝其他一切选项 switch (action) { case WILL: case WONT: send.PushValues(new byte[] { IAC, DONT, option }); break; case DO: case DONT: send.PushValues(new byte[] { IAC, WONT, option }); break; } break; } } else { //丢给VT220解析器 data.Push(b); } } }
/// <summary> /// 处理VT220协议数据 /// </summary> /// <param name="data"></param> void ParseVT220Command(RingQueue <byte> data) { while (data.Count > 0) { var b = data.GetFirstValue(); if (b == ESC) { if (data.Count < 2) { break; } /** * 第二指令说明: ESC [opt] * * N: 转交SS2命令处理 * O: 转交SS3命令处理 * P: 转交DCS命令处理 * [: 转交CSI命令处理 * \: 转交ST命令处理 * D: 光标向下移动一行(同IND命令),若光标在底部则内容向上滚动 * M: 光标向上移动一行(同RI命令),若光标在顶部则内容向下滚动 * E: 光标移动到下一行首位(同NEL命令),若光标在底部则内容向上滚动 * 7: 保存光标状态(光标位置,图形渲染,字符偏移状态,自动换行,参考点,删除内容区域) * 8: 重置光标状态 * H: 设置水平Tab光标停留位置(同HTS命令) * #: 行属性 * ESC # 3 上半部 * ESC # 4 下半部 * ESC # 5 单倍宽度 * ESC # 6 双倍宽度 * c: 硬重置终端 * =: 键盘模式(应用模式) * >:键盘模式(数字模式) * (: G0字符集 ESC ( [final] * ): G1字符集 ESC ) [final] * *: G2字符集 ESC * [final] * +: G3字符集 ESC + [final] */ switch (data.GetNextValue()) { case 0x5B: // [ ==> CSI if (!ParseCSICommand(data)) { return; } break; #region 编码集设定 case 0x28: // ( //Console.WriteLine("G0 " + data.GetNextValue().ToString()); Logger.Debug("编码集设定: " + "G0 " + data.GetNextValue().ToString()); Screen.Charset0 = ParseCharsets(data.Value); data.LTrim(); break; case 0x29: // ) Logger.Debug("编码集设定: " + "G1 " + data.GetNextValue().ToString()); //Console.WriteLine("G1 " + data.GetNextValue().ToString()); Screen.Charset1 = ParseCharsets(data.Value); data.LTrim(); break; case 0x2A: // * Logger.Debug("编码集设定: " + "G2 " + data.GetNextValue().ToString()); //Console.WriteLine("G2 " + data.GetNextValue().ToString()); Screen.Charset2 = ParseCharsets(data.Value); data.LTrim(); break; case 0x2B: // + Logger.Debug("编码集设定: " + "G3 " + data.GetNextValue().ToString()); //Console.WriteLine("G3 " + data.GetNextValue().ToString()); Screen.Charset3 = ParseCharsets(data.Value); data.LTrim(); break; #endregion case 0x3E: // > 键盘模式(数字键盘) 参考4.6.18 Logger.Debug("键盘模式:数字模式"); //Console.WriteLine("键盘模式:数字模式"); data.LTrim(); break; } } else if (b == 0x0F) //LS0 将G0字符集映射到GL { Logger.Debug("G0字符集映射到GL"); Screen.Charset = Screen.Charset0; data.LTrim(); } else if (b == 0x0E) //LS1 将G1字符集映射到GL { Logger.Debug("G1字符集映射到GL"); Screen.Charset = Screen.Charset1; data.LTrim(); } else if (b == 0x07) //Beep { Console.Beep(250, 100); data.LTrim(); } else if (b == 0x08) //退格 { var pos = Screen.CursorPos; pos.X--; Screen.CursorPos = pos; Screen.WriteByte(0); Screen.CursorPos = pos; data.LTrim(); } else { data.LTrim(); if (b != 0) { if (IsPrinting) { //输出到文件 Output.WriteByte(b); } else { Screen.WriteByte(b); } } } } }
/// <summary> /// 进入消息等待 /// </summary> public void Process() { //用户发送队列 m_userQueue = new RingQueue <byte>(1024); //vt220待解析数据 m_telnetDataQueue = new RingQueue <byte>(8192); //消息处理 new System.Threading.Thread(new System.Threading.ThreadStart(delegate() { //接收缓存队列 var recvQueue = new RingQueue <byte>(8192); //发送缓存队列 var sendQueue = new RingQueue <byte>(1024); var io = m_conn.GetStream(); var sendStream = new System.IO.BufferedStream(io); var recvStream = new System.IO.BufferedStream(io); while (m_conn != null && m_conn.Connected) { //接收服务端数据 while (io.DataAvailable && recvQueue.Available > 0) { var b = io.ReadByte(); if (b == -1) { break; } recvQueue.Push((byte)b); } //解析Telnet协议 并 响应服务器 ParseTelnetCommand(recvQueue, sendQueue, m_telnetDataQueue); //解析VT220协议 ParseVT220Command(m_telnetDataQueue); //客户端发送数据 while (sendQueue.Count > 0 && m_conn.Connected) { var data = sendQueue.ToArray(); sendStream.Write(data, 0, data.Length); sendStream.Flush(); sendQueue.Clear(); } //发送用户数据 while (m_userQueue.Count > 0 && m_conn.Connected) { var data = m_userQueue.ToArray(); sendStream.Write(data, 0, data.Length); sendStream.Flush(); m_userQueue.Clear(); } //休眠100ms(文件输出时20ms) System.Threading.Thread.Sleep(IsPrinting?10:100); //检测终止标志(不确定) if (m_conn != null && m_conn.Client.Poll(1, SelectMode.SelectRead) && m_conn.Client.Available == 0) { this.Disconnect(); Logger.Info("Telnet 连接关闭(收到服务端指令)"); } } })).Start(); //绑定控件 //绑定用户输入事件 Screen.KeyPress += new System.Windows.Forms.KeyPressEventHandler(delegate(object sender, System.Windows.Forms.KeyPressEventArgs e) { m_userQueue.Push(Convert.ToByte(e.KeyChar)); }); Screen.KeyDown += new System.Windows.Forms.KeyEventHandler(delegate(object sender, System.Windows.Forms.KeyEventArgs e) { SendKey(e.KeyCode); //Console.WriteLine(e.KeyCode); }); //绑定用户输入事件 }