I'm trying to write a network interface for this 'asynchronous' embedded card for an LED sign. There is existing software, called "PlutoManager", but it's made in China and it's too difficult for our older customers to use.
The software does a number of things by interacting with the embedded card (called a PSD100) via an Ethernet cable.
I looked at some documentation, and the documentation states that the card communicates through standard TCP/IP protocols. (Or something similar to TCP/IP, not quite sure)
I translated some things from the Chinese document I got ahold of, and this is what I've come to find out about the card's protocol:
(I'm not so knowledgeable about TCP/IP so this translation may be rough, keep in mind these words might be the wrong words, which could be a big part of my problem.)
So for every communication with the card (sending files, handshakes, changing brightness of the LED sign, etc) two things have to happen:
- A message is sent to the card (request packet)
- A reply is received from the card (reply packet)
Request packet structure is as follows: (from Chinese, and my translation sucks)
> 1. Header: 1 byte (16 bits) with a hex value of "0x02"
>2. Card Address(??): 2 bytes (32 bits)
>3. Packet Type: 2 bytes (32 bits)
>4. data: indeterminate length
>5. CRC Check: 2 bytes (32 bits)
>6. End of Text Character: 1 byte (16 bits) (value: "0x03" [I guess that's equal to ^c ?]
Does this look like normal TCP/IP structure, before I get carried away with custom packets?
I figured I could use Wireshark to sniff the packets being sent when PlutoManager does the handshake. I also wrote some code in C# trying to make a connection with the device's port. Here are the two side by side. Note that this is just the TCP packet part of the dump, the TCP part of the wireshark output is the only part that differs.
TCP SEGMENT CAPTURED FROM WIRESHARK HEX + ASCII DUMP (FROM MY C# CODE)
HEX
0000 d0 6b 7a 43 5e a3 79 62 67 78 dc bf 50 10 80 51 ASCII: .kzC^.ybgx..P..Q
0010 46 60 00 00 F`..
TCP SEGMENT CAPTURED FROM WIRESHARK HEX + ASCII DUMP (PLUTOMANAGER CODE)
HEX
0000 7a 42 d0 6a 34 17 04 36 5e a3 0b 1d 50 10 01 00 ASCII: zB.j4..6^...P...
0010 82 50 00 00
I figured, "hey, I can just send a custom payload to the card with a Send() command and replicate what the PlutoManager code is doing!"
I don't know if this Chinese software uses some special TCP payload to send messages to the sign, or if it's using a standard protocol. And I wouldn't know how to spot the difference. I have tried to use Pcap.net to send a custom payload, but before I keep going that far down the rabbithole, does it seem like that is necessary? Is the second Wireshark output something commonly seen in TCP/IP protocols? Is it possible to just send the string "zB/^T3mPP" (which is what the hex dump output is of that handshake) in order to make the handshake happen?
This is how I currently have my program structured (basically str:
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Text;
// State object for receiving data from remote device.
public class StateObject
{
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 256;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
public class AsynchronousClient
{
// The port number for the remote device.
private const int port = 31298;
// ManualResetEvent instances signal completion.
private static ManualResetEvent connectDone =
new ManualResetEvent(false);
private static ManualResetEvent sendDone =
new ManualResetEvent(false);
private static ManualResetEvent receiveDone =
new ManualResetEvent(false);
// The response from the remote device.
private static String response = String.Empty;
private static void StartClient()
{
// Connect to a remote device.
try
{
// Establish the remote endpoint for the socket.
// The name of the
// remote device is "host.contoso.com".
//IPHostEntry ipHostInfo = Dns.Resolve("host.contoso.com");
IPAddress ipAddress = IPAddress.Parse("192.168.0.59"); //ipHostInfo.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
// Create a TCP/IP socket.
Socket client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
// Connect to the remote endpoint.
client.BeginConnect(remoteEP,
new AsyncCallback(ConnectCallback), client);
connectDone.WaitOne();
// Send test data to the remote device.
Send(client, "This is a test<EOF>");
sendDone.WaitOne();
// Receive the response from the remote device.
Receive(client);
receiveDone.WaitOne();
// Write the response to the console.
Console.WriteLine("Response received : {0}", response);
// Release the socket.
client.Shutdown(SocketShutdown.Both);
client.Close();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void ConnectCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
Console.WriteLine("Socket connected to {0}",
client.RemoteEndPoint.ToString());
// Signal that the connection has been made.
connectDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void Receive(Socket client)
{
try
{
// Create the state object.
StateObject state = new StateObject();
state.workSocket = client;
// Begin receiving the data from the remote device.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void ReceiveCallback(IAsyncResult ar)
{
try
{
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
// Get the rest of the data.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
else
{
// All the data has arrived; put it in response.
if (state.sb.Length > 1)
{
response = state.sb.ToString();
}
// Signal that all bytes have been received.
receiveDone.Set();
}
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
private static void Send(Socket client, String data)
{
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
client.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), client);
}
private static void SendCallback(IAsyncResult ar)
{
try
{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
Console.WriteLine("Sent {0} bytes to server.", bytesSent);
// Signal that all bytes have been sent.
sendDone.Set();
}
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
public static int Main(String[] args)
{
StartClient();
return 0;
}
}
Main() runs the command StartClient() which tries to connect, but ultimately outputs the error message:
System.Net.Sockets.SocketException (0x80004005): No connection could be made because the target machine actively refused it 192.168.0.59:31298
at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)
at AsynchronousClient.ConnectCallback(IAsyncResult ar) in C:\Users\xxxxx\Desktop\SocketListenerTest\SocketListenerTest\SocketListenerTest\Program.cs:line 87
Line 87 is:
client.EndConnect(ar);
This makes me think that I am connecting to the right IP, and the right port, but that the protocol that is built into .NET and the protocol being used by this embedded device are different.
I have access to a Chinese document (I would post it but it's under NDA) with some of the specifications of the device. If I am missing something, or if you need more information from the document, I'll post what I can. I tried to provide the most relevant information I could, but this is pretty foreign to me.
I guess I could have simplified the question to "How can I modify the Sockets.Connect() method to use a custom TCP protocol?" but I figured it would be better to give a more general overview of what I'm trying to accomplish, because that might not be what I even need to do.
Thanks for taking the time to look at this problem. If you have any suggestions, even pointing me to a library or book or some sort of reading material, I'd love to hear it. Thanks.