Home > APIs > AMP protocol > AMP socket connection example
Below is a simple C# program that shows how to connect to and communicate with a K2 using an AMP socket connection. This program connects, sends two commands, and then disconnects.
A full project of this can be downloaded here: AMPSocketExample.zip
namespace AMPSocketExample { class AMPClient { private Socket _socket; ////// Creates an AMP connection to the hostname and channel specified. /// public AMPClient(string hostName, string channelName) { if (hostName == null || hostName=="") throw new Exception("Hostname cannot be blank."); // Resolve the hostname using DNS IPHostEntry hostEntry = Dns.GetHostEntry(hostName); if (hostEntry.AddressList.Length == 0) throw new Exception("Could not resolve hostname."); IPAddress address = hostEntry.AddressList[0]; // Create an endpoint to the IP address that was resolved, using AMP port # 3811. IPEndPoint endPoint = new IPEndPoint(address, 3811); // Create and connect a TCP socket Console.WriteLine("Connecting to host {0}...", hostName); _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); _socket.Connect(endPoint); // Set the send and receive timeouts. _socket.ReceiveTimeout = 5000; _socket.SendTimeout = 5000; // Create the AMP connection. Refer to the document "K2 Protocol Developer's Guide" for details. string strCommand; if (channelName == null || channelName == "") { // AMP channel-less connection Console.WriteLine("Creating AMP channel-less connection..."); strCommand = ("CRAT00014\n"); } else { // AMP channel connection Console.WriteLine("Creating AMP connection to channel {0}...", channelName); strCommand = String.Format("CRAT{0:0000}{1}{2:00}{3}\n", channelName.Length + 3, 2, channelName.Length, channelName); } // Convert the command string to a byte array, and send the data to the socket. byte[] byteData = System.Text.Encoding.ASCII.GetBytes(strCommand); if (_socket.Send(byteData) != byteData.Length) throw new Exception("Error sending socket data."); // Receive the response to the connection request. The reply will either be "1001" (ACK) or "1111" (NAK). const int replySize = 4; byte[] receiveBuffer = new byte[replySize]; if (_socket.Receive(receiveBuffer) != replySize) { throw new Exception("Response to connection request did not return the expected number of bytes."); } // Make sure the connection was successful. string strReply = System.Text.Encoding.ASCII.GetString(receiveBuffer, 0, replySize); if (strReply == "1111") { throw new Exception("CRAT command failed -- system returned NAK reply. Verify that the channel is configured for AMP control."); } else if (strReply != "1001") { throw new Exception(string.Format("CRAT command failed. System returned unknown reply {0}", strReply)); } } ////// Sends the "STOP" command to close the AMP connection, and then closes the socket connection. /// public void Disconnect() { if (_socket == null || !_socket.Connected) throw new Exception("Socket is not yet connected."); string strCommand = "STOP0000\n"; byte[] byteData = System.Text.Encoding.ASCII.GetBytes(strCommand); if (_socket.Send(byteData) != byteData.Length) throw new Exception("Error sending socket data."); // Receive the response. The reply will either be "1001" (ACK) or "1111" (NAK). byte[] receiveBuffer = new byte[4]; int replyLen = _socket.Receive(receiveBuffer); string strReply = System.Text.Encoding.ASCII.GetString(receiveBuffer, 0, replyLen); if (strReply != "1001") Console.WriteLine("Warning, STOP command did not return ACK reply."); // Close the socket _socket.Close(); _socket = null; } ////// Sends the specified AMP command, and reads the response. The command is specified as a string of hexadecimal bytes, /// and the reply is returned as an array of byte values. If the isExtendedReply argument is set to true, /// when reading the reply data we will use the number of bytes specified by the "actual byte count" value. /// public byte[] SendCommand(string command, bool isExtendedReply) { byte[] sendBytes; byte[] replyBytes; if (_socket == null || !_socket.Connected) throw new Exception("Socket is not yet connected."); // AMP protocol only allows the complete command to take up to a maximum of 9999 bytes // due to the format of the length (4 decimal characters) after the CMDS string. if (command.Length > 9999) { throw new Exception(string.Format("Error sending AMP command. Data size ({0} bytes) exceeds maximum allowed of 9999 bytes.", command.Length)); } // Format the socket command by adding the CMDSxxxx header. string sendString = string.Format("CMDS{0:0000}{1}\n", command.Length, command); sendBytes = System.Text.Encoding.ASCII.GetBytes(sendString); // Send the command to the server. _socket.Send(sendBytes); // Read the first byte of the response (CMD1) byte cmd1 = ReadByte(); // If we are expecting a response in the extended format, use the "actual byte count" value // to determine how much reply data is to be expected. Otherwise, read the amount of data // based on the data length specified in the lower nibble of the CMD1 byte. if (isExtendedReply) { // Read the second byte of the response (CMD2) byte cmd2 = ReadByte(); // Read the extended byte count. byte[] extBc = new byte[2]; extBc[0] = ReadByte(); extBc[1] = ReadByte(); byte[] tempExtBc = new byte[] { extBc[1], extBc[0] }; UInt16 dataLen = BitConverter.ToUInt16(tempExtBc, 0); // Read the expected amount of data. Socket commands do not include an extra checksum byte as RS422 does. byte[] data = ReadBytes(dataLen); // Total length of data for a socket reply in extended format: CMD1 + CMD2 + BC1 + BC2 + DATALEN int totalLen = dataLen + 4; // Assemble the complete reply into the 'replyBytes' array. replyBytes = new byte[totalLen]; replyBytes[0] = cmd1; replyBytes[1] = cmd2; replyBytes[2] = extBc[0]; replyBytes[3] = extBc[1]; Array.Copy(data, 0, replyBytes, 4, data.Length); } else { // Extract the response data length from the lower nibble of the CMD1 byte. int dataLen = (cmd1 & 0x0F); // Bytes remaining to read: the CMD2 byte, plus the length of data expected. // For socket connections, there is no checksum byte added like there is for RS422. int bytesToRead = dataLen + 1; // Read the remaining bytes of the response. byte[] data = ReadBytes(bytesToRead); // Total length also includes the cmd1 byte that was read previously. int totalLen = bytesToRead + 1; // Assemble the complete reply into the 'replyBytes' array. replyBytes = new byte[totalLen]; replyBytes[0] = cmd1; Array.Copy(data, 0, replyBytes, 1, data.Length); } return replyBytes; } ////// Reads and returns one byte of data from an AMP command reply. /// This actually involves reading two bytes of data from the network, since data is returned as a hexadecimal string. /// private byte ReadByte() { byte[] b = new byte[2]; if (_socket.Receive(b) != 2) throw new Exception("Failed to read data from socket"); string str = ASCIIEncoding.ASCII.GetString(b); return Convert.ToByte(str, 16); } ////// Reads and returns the specified number of bytes of data from an AMP command reply. /// This actually involves reading two bytes of data from the network for each byte, since data is returned as a hexadecimal string. /// private byte[] ReadBytes(int count) { int totalToRead = count * 2; byte[] b = new byte[totalToRead]; int totalRead = 0; while (totalRead < totalToRead) { int numRead = _socket.Receive(b, totalRead, (totalToRead - totalRead), SocketFlags.None); totalRead += numRead; } string str = ASCIIEncoding.ASCII.GetString(b); return HexStringToByteArray(str); } ////// Converts a string representing hex numbers into their equivalent byte values (2 characters per byte). /// Example: "A005" --> { 0xA0, 0x05 } /// public byte[] HexStringToByteArray(string hexString) { if (hexString == null) return new byte[] { }; if (hexString.Length % 2 != 0) throw new Exception("HexStringToByteArray: Input string must be an even number of characters."); byte[] byteArray = new byte[hexString.Length / 2]; for (int i = 0; i < (hexString.Length / 2); i++) { byteArray[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16); } return byteArray; } ////// Sends the A0.0F "Get Working Folder Request" command and returns the response. /// public string GetWorkingFolderRequest() { // Send the command and receive the response. byte[] reply = SendCommand("A00F", true); // Format of the expected reply data: [0x82] [0x0F] [ExtBC1] [ExtBC2] [Len1] [Len2] [FolderName] // Verify that the expected response was received, and extract the folder name string from the reply data. if (reply[0] == 0x82 && reply[1] == 0x0F) { // Extract the folder name length byte[] lengthBytes = new byte[] { reply[5], reply[4] }; Int16 folderNameLen = BitConverter.ToInt16(lengthBytes, 0); // Extract and return the folder name string. return Encoding.UTF8.GetString(reply, 6, folderNameLen); } else { throw new Exception("GetWorkingFolderRequest returned an invalid reply."); } } ////// Sends the 20.61 "EE On" command (enables E-to-E mode on the channel). /// public void EEOn() { // Send the command and receive the response. byte[] reply = SendCommand("2061", false); // Verify that the expected ACK reply was received. if (! (reply[0] == 0x10 && reply[1] == 0x01)) { throw new Exception("EEOn command did not return ACK reply."); } } } }