通过套接字 c++/cli 对 AT 命令的错误响应
Wrong response to AT command via socket c++/cli
我想创建一个简单的套接字,通过 AT 命令与另一个设备通信。
我在 Visual 2017 上使用 C++/CLI。
这是我的代码
#include "stdafx.h"
#include <conio.h>
using namespace System;
using namespace System::Net;
using namespace System::Net::Sockets;
using namespace System::IO;
int main(array<System::String ^> ^args)
{
int bufferSize = 1024;
array<Byte>^ sendBuffer = gcnew array<Byte>(bufferSize);
array<Byte>^ recvBuffer = gcnew array<Byte>(bufferSize);
try {
// Establish the remote endpoint for the socket.
IPHostEntry^ ipHostInfo = Dns::Resolve("192.168.1.1");
IPAddress^ ipAddress = ipHostInfo->AddressList[0];
IPEndPoint^ remoteEP = gcnew IPEndPoint(ipAddress, 1234);
// Create a TCP/IP socket.
Socket^ socket = gcnew Socket(AddressFamily::InterNetwork,SocketType::Stream, ProtocolType::Tcp);
// Connect the socket to the remote endpoint. Catch any errors.
try {
socket->Connect(remoteEP);
// Encode the data string into a byte array.
array<Byte>^ msg = Text::Encoding::ASCII->GetBytes("AT");
// Send the data through the socket.
int bytesSent = socket->Send(msg);
// Receive the response from the remote device.
int bytesRec = socket->Receive(recvBuffer);
Console::WriteLine("Echoed test = {0}", Text::Encoding::ASCII->GetString(recvBuffer, 0, bytesRec));
// Release the socket.
socket->Shutdown(SocketShutdown::Both);
socket->Close();
}
catch (ArgumentNullException^ ane) {
Console::WriteLine("ArgumentNullException : {0}", ane->ToString());
}
catch (SocketException^ se) {
Console::WriteLine("SocketException : {0}", se->ToString());
}
catch (Exception^ e) {
Console::WriteLine("Unexpected exception : {0}", e->ToString());
}
}
catch (Exception^ e) {
Console::WriteLine(e->ToString());
}
_getch();
return 0;
}
那里的命令,响应是:
Echoed test = ????????
在 ASCII 中有奇怪的值:255,251,3,255,251,1,255,254,1,255,253
答案应该是 OK 或 ERROR
我在 192.168.1.1 1234 上通过 Telnet 测试它,它工作正常。
标准警告:虽然可以用 C++/CLI 编写应用程序的主体,但不推荐这样做。 C++/CLI 适用于互操作场景:在 C# 或其他 .Net 代码需要与非托管 C++ 交互的情况下,C++/CLI 可以提供两者之间的转换。初级开发,如果要托管代码,建议使用C#,如果要非托管代码,建议使用C++。
也就是说...
与普遍看法相反,telnet 协议不是原始 TCP 套接字。有一个用于在 telnet 客户端之间通信选项的协议。
您看到的是从服务器发送的 telnet 命令。这些将由您的 telnet 客户端接收,并用于修改它的行为方式。这就是当您使用真正的 telnet 客户端时一切正常的原因:它获取这些字节并正确解释命令。
我阅读了几分钟的 Telnet 规范,这是我能够从您发布的数据中解码出来的内容:
- 255:IAC,“解释为命令”。这是所有 Telnet 命令的转义字符。
- 251: WILL: 表明服务器想要to/is执行一个选项。
- 3: SUPPRESS-GO-AHEAD:显然 Telnet 默认是半双工协议,“Go Ahead”是一方告诉另一方“好的,轮到你了”的标记。此选项将其变成全双工连接。
- 255:IAC
- 251:将
- 1: ECHO: 表示服务器将回显它接收到的字符。
- 255:IAC
- 254: DON'T: 表示服务器请求客户端不要做某事。
- 1: ECHO:表示服务器希望客户端不回显接收到的字符。
- 255:IAC
- 253: DO: 表明服务器希望客户端打开一些选项。
好的,现在我们知道发生了什么,我们如何解决这个问题?我看到几个选项:
- 你说你想使用“AT 命令”与设备对话。 AT 命令是您用来与调制解调器通信的命令。我假设您有一些串行设备,可能是调制解调器,您已经连接到一个将串行端口公开为 TCP 连接的小型转换器设备。如果是这样,那么该转换器设备可能有一些选项可以禁用 Telnet 协议,并将其公开为“原始”或类似的东西。如果这是真的,那么这可能是最好的选择。
- 您可以在程序中添加代码以查找
IAC
字节,并处理它后面的字节。如果只是连接开始时的几个命令,那么你可以期待那些固定的字节;如果命令是在连接期间发送的,则您需要在任何地方处理它们。这取决于你有多想处理它们。 (例如,如果服务器说 DON'T SUPPRESS-GO-AHEAD,你会发送继续命令吗?理想情况下你会,但如果你的特定连接从未这样说,那么可能不会。)
- 可能有一个 telnet 库可以为您处理协议内容。我没有搜索过这个。
Telnet 引用:
- 主要的 RFC,其中定义了 IAC 字节和命令字节:https://www.rfc-editor.org/rfc/rfc854
- ECHO 选项:https://www.rfc-editor.org/rfc/rfc857
- SUPPRESS-GO-AHEAD 选项:https://www.rfc-editor.org/rfc/rfc858
- 不是官方参考,但确实列出了可以用 WILL、WON'T、DO 和 DON'T 指定的选项,以及它们定义的 RFC:http://mars.netanya.ac.il/~unesco/cdrom/booklet/HTML/NETWORKING/node300.html
我想创建一个简单的套接字,通过 AT 命令与另一个设备通信。
我在 Visual 2017 上使用 C++/CLI。
这是我的代码
#include "stdafx.h"
#include <conio.h>
using namespace System;
using namespace System::Net;
using namespace System::Net::Sockets;
using namespace System::IO;
int main(array<System::String ^> ^args)
{
int bufferSize = 1024;
array<Byte>^ sendBuffer = gcnew array<Byte>(bufferSize);
array<Byte>^ recvBuffer = gcnew array<Byte>(bufferSize);
try {
// Establish the remote endpoint for the socket.
IPHostEntry^ ipHostInfo = Dns::Resolve("192.168.1.1");
IPAddress^ ipAddress = ipHostInfo->AddressList[0];
IPEndPoint^ remoteEP = gcnew IPEndPoint(ipAddress, 1234);
// Create a TCP/IP socket.
Socket^ socket = gcnew Socket(AddressFamily::InterNetwork,SocketType::Stream, ProtocolType::Tcp);
// Connect the socket to the remote endpoint. Catch any errors.
try {
socket->Connect(remoteEP);
// Encode the data string into a byte array.
array<Byte>^ msg = Text::Encoding::ASCII->GetBytes("AT");
// Send the data through the socket.
int bytesSent = socket->Send(msg);
// Receive the response from the remote device.
int bytesRec = socket->Receive(recvBuffer);
Console::WriteLine("Echoed test = {0}", Text::Encoding::ASCII->GetString(recvBuffer, 0, bytesRec));
// Release the socket.
socket->Shutdown(SocketShutdown::Both);
socket->Close();
}
catch (ArgumentNullException^ ane) {
Console::WriteLine("ArgumentNullException : {0}", ane->ToString());
}
catch (SocketException^ se) {
Console::WriteLine("SocketException : {0}", se->ToString());
}
catch (Exception^ e) {
Console::WriteLine("Unexpected exception : {0}", e->ToString());
}
}
catch (Exception^ e) {
Console::WriteLine(e->ToString());
}
_getch();
return 0;
}
那里的命令,响应是:
Echoed test = ????????
在 ASCII 中有奇怪的值:255,251,3,255,251,1,255,254,1,255,253
答案应该是 OK 或 ERROR
我在 192.168.1.1 1234 上通过 Telnet 测试它,它工作正常。
标准警告:虽然可以用 C++/CLI 编写应用程序的主体,但不推荐这样做。 C++/CLI 适用于互操作场景:在 C# 或其他 .Net 代码需要与非托管 C++ 交互的情况下,C++/CLI 可以提供两者之间的转换。初级开发,如果要托管代码,建议使用C#,如果要非托管代码,建议使用C++。
也就是说...
与普遍看法相反,telnet 协议不是原始 TCP 套接字。有一个用于在 telnet 客户端之间通信选项的协议。
您看到的是从服务器发送的 telnet 命令。这些将由您的 telnet 客户端接收,并用于修改它的行为方式。这就是当您使用真正的 telnet 客户端时一切正常的原因:它获取这些字节并正确解释命令。
我阅读了几分钟的 Telnet 规范,这是我能够从您发布的数据中解码出来的内容:
- 255:IAC,“解释为命令”。这是所有 Telnet 命令的转义字符。
- 251: WILL: 表明服务器想要to/is执行一个选项。
- 3: SUPPRESS-GO-AHEAD:显然 Telnet 默认是半双工协议,“Go Ahead”是一方告诉另一方“好的,轮到你了”的标记。此选项将其变成全双工连接。
- 255:IAC
- 251:将
- 1: ECHO: 表示服务器将回显它接收到的字符。
- 255:IAC
- 254: DON'T: 表示服务器请求客户端不要做某事。
- 1: ECHO:表示服务器希望客户端不回显接收到的字符。
- 255:IAC
- 253: DO: 表明服务器希望客户端打开一些选项。
好的,现在我们知道发生了什么,我们如何解决这个问题?我看到几个选项:
- 你说你想使用“AT 命令”与设备对话。 AT 命令是您用来与调制解调器通信的命令。我假设您有一些串行设备,可能是调制解调器,您已经连接到一个将串行端口公开为 TCP 连接的小型转换器设备。如果是这样,那么该转换器设备可能有一些选项可以禁用 Telnet 协议,并将其公开为“原始”或类似的东西。如果这是真的,那么这可能是最好的选择。
- 您可以在程序中添加代码以查找
IAC
字节,并处理它后面的字节。如果只是连接开始时的几个命令,那么你可以期待那些固定的字节;如果命令是在连接期间发送的,则您需要在任何地方处理它们。这取决于你有多想处理它们。 (例如,如果服务器说 DON'T SUPPRESS-GO-AHEAD,你会发送继续命令吗?理想情况下你会,但如果你的特定连接从未这样说,那么可能不会。) - 可能有一个 telnet 库可以为您处理协议内容。我没有搜索过这个。
Telnet 引用:
- 主要的 RFC,其中定义了 IAC 字节和命令字节:https://www.rfc-editor.org/rfc/rfc854
- ECHO 选项:https://www.rfc-editor.org/rfc/rfc857
- SUPPRESS-GO-AHEAD 选项:https://www.rfc-editor.org/rfc/rfc858
- 不是官方参考,但确实列出了可以用 WILL、WON'T、DO 和 DON'T 指定的选项,以及它们定义的 RFC:http://mars.netanya.ac.il/~unesco/cdrom/booklet/HTML/NETWORKING/node300.html