Create a TCP Channel Communicator Wrapper class

TcP channel communicator wrapper.

Note: There are some stuff that changes with requirement, and suitable comments are provided. This might require some more.

And usual steps are marked to distinguish the various steps involved. The implementation stuff is in the bottom.
This article uses a logger component whose implementation primarily is Enterprise Library. Please make sure you have that or you replace those statements with your own.

STEP 1: Create a TCP Channel Communicator Wrapper class as below. Note Properties of the Project file are used to pass in the necessary values for TCP like the Username, domain, serverip, alternateip. You might have to change the implementation if you are providing these values otherwise.


using System;
using System.Net;
using System.Xml;
using System.Text;
using System.Threading;
using System.Net.Sockets;
using System.Collections;

namespace Utilities.Communicators
{
/// <summary>
/// Wrapper class to interact with a TCP channel.
/// </summary>
public class TcpChannelCommunicator
{
#region Private Variables

private Socket _socket;
private Object lockSocketObject;
private short _protocolId;
private Int32 SequenceNumber;
private Thread _receiverThread;
private bool _continueReceiving = true;
private string _userName;
private string _password;
private string _domain;
private Hashtable _requestResponseTable;
private Object lockTableObject;

private static TcpChannel _singletonTcpChannel = null;
private static Object lockSingletonSocketObject = new object();
private static Hashtable _currentUsers = new Hashtable();
private static Object lockCurrentUsers = new object();
private static Object _lockExecuteCommanAsync = new object();

private class ResponseEventData
{
public AutoResetEvent are = null;
public StringBuilder response = null;
public ResponseEventData(AutoResetEvent a, StringBuilder r)
{
are = a;
response = r;
}
}

#endregion Private Variables

#region Public Members

public string UserName { get { return _userName; } }
public string Domain { get { return _domain; } }

#endregion Public Members

#region Constants

// Send and receive buffer sizes. Should be big enough to hold
// largest possible ITF message (0xFFFF bytes) and SYNC byte
const int MAX_ITF_MSG_SIZE = 0xFFFF;
const int SND_BUF_SIZE = MAX_ITF_MSG_SIZE + 1;
const int RCV_BUF_SIZE = MAX_ITF_MSG_SIZE + 1;
const ushort MDTF_HDR_SIZE = 12; // actual size of Mdtf_Hdr
const ushort SEQUENCE_NUMBER_POSITION = 4;
const ushort XML_RESPONSE_TYPE = 40;

const short USERNAME_HEADER_SIZE = 20;
const short COMPANY_HEADER_SIZE = 6;
const short RMDS_PROTOCOL_ID = 0; // Protocol Id
const short RMDS_VER_ID = 1;
const short LOGON_SESSION_ID = 1; // Logon session ID.

const string LOGON_QUERY = "APACHE_LOGON";

const char APACHE_CONN_REQUEST_MSG = 'A';
const char APACHE_QUERY_REQUEST = 'q';
const char LOGON_REQUEST_MSG = 'L'; // 0x4c
const char LOGOFF_REQUEST_MSG = 'C'; // 0x43
const char LOGON_LOGOFF_SUCCESS = 'R'; // 0x52
const char LOGON_LOGOFF_ERROR = 'E'; // 0x45
const char LOGON_RESPONSE_MSG = 'I'; // 0x49
const char LOGOFF_RESPONSE_MSG = 'X'; // 0x58
const char KEEP_ALIVE_REQUEST_MSG = 'K'; // 0x4b
const char KEEP_ALIVE_RESPONSE_MSG = 'k'; // 0x6b
const char ISI_OPEN_FID_RESPONSE = 'Z';

const char SYNC_BYTE = (char)2; // SYNC byte

//Port and server configuration.
private const string tcpServer = "tcpServer";
private const string tcpAlternateServer = "tcpAlternateServer";
private const string tcpPort = "tcpPort";
private const string tcpUser = "tcpUser";
private const string tcpPassword = "tcpPassword";
private const string tcpdomain = "tcpdomain";

#endregion Constants

#region Private Methods

/// <summary>
/// Constructor
/// </summary>
private TcpChannelCommunicator()
{
lockTableObject = new Object();
lockSocketObject = new Object();
_requestResponseTable = new Hashtable();
_continueReceiving = true;

try
{
Open();

_receiverThread = new Thread(new ParameterizedThreadStart(ReceiverThread));
_receiverThread.Start(this);

SendRequest(LOGON_QUERY, APACHE_CONN_REQUEST_MSG, "", "");
}
catch (Exception exception)
{
Destroy(true);
throw exception;
}
}

/// <summary>
/// Opens this instance.
/// </summary>
/// <returns></returns>
private string Open()
{
string response = "";
try
{
_protocolId = IPAddress.HostToNetworkOrder(RMDS_PROTOCOL_ID);

//Use Property Settings to configure the settings
IPHostEntry ipHostInfo = Dns.GetHostEntry(Properties.Settings.Default.tcpServer);

IPAddress ipAddress = ipHostInfo.AddressList[0];

//Use Property Settings to configure the settings
IPEndPoint remoteEP = new IPEndPoint(ipAddress, Convert.ToInt32
(Properties.Settings.Default.tcpPort));

_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream,
ProtocolType.Tcp);
_socket.Connect(remoteEP);

}
catch (SocketException se)
{
//Try the alternate address if the primary fails
IPHostEntry ipHostInfo = Dns.GetHostEntry(Properties.Settings.Default.tcpAlternateServer);

IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress,
Convert.ToInt32(Properties.Settings.Default.tcpPort));

_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.Connect(remoteEP);
}
catch (System.Exception exception)
{
throw exception;
}
return response;
}

/// <summary>
/// Executes the command async.
/// </summary>
/// <param name="Query">The query.</param>
/// <param name="userName">Name of the user.</param>
/// <param name="password">The password.</param>
/// <param name="companyName">Name of the company.</param>
/// <param name="are">The are.</param>
/// <param name="responseData">The response data.</param>
/// <param name="autoRefresh">if set to <c>true</c> [auto refresh].</param>
private void ExecuteCommandAsync(string Query, string userName,
string password, string companyName, ref AutoResetEvent are, ref
StringBuilder responseData, bool autoRefresh)
{
try
{
AutoResetEvent loginARE = new AutoResetEvent(false);
StringBuilder loginResponse = new StringBuilder();

bool loggedIn = false;

if (_currentUsers.Contains(string.Format("{0}.{1}", userName, companyName)) == true)
{
loggedIn = true;
}

if (loggedIn == false)
{
string errCode = "";
string errMessage = "";

Login(userName, password, companyName, ref loginARE, ref loginResponse);
loggedIn = loginARE.WaitOne(TimeSpan.FromSeconds(20), false);

// check if there has been a response from the server
if (loggedIn == true)
{
if ((loginResponse.ToString().IndexOf("LOGON") == -1)
&& (loginResponse.ToString().IndexOf("LOGICAL_ADDR") == -1))
{
errMessage = loginResponse.ToString();

loggedIn = false;
}
}

// Check if the user has been logged in
if (loggedIn == false)
{
// If its a timeout, then construct an xml from the error response and send it to the user.

StringBuilder responseBuilder = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(responseBuilder))
{
writer.WriteStartElement("Response", "Message");

writer.WriteStartElement("Error");
writer.WriteAttributeString("message", "errMessage");
writer.WriteString(errCode);
writer.WriteEndElement();

writer.WriteEndElement();
}

responseData.Append(responseBuilder.ToString());
are.Set();

return;
}

string[] logonResponseFields = loginResponse.ToString().Split(new char[] { ' ' });
string logicalAddress = "";
foreach (string s in logonResponseFields)
{
if (s.IndexOf("LOGICAL_ADDR") != -1)
{
logicalAddress = s.Split(new char[] { '=' })[1];
logicalAddress.Trim();
logicalAddress = logicalAddress.ToUpper();
break;
}
}
if (logicalAddress.Length > 0)
{
_currentUsers.Add(string.Format("{0}.{1}", userName, companyName), logicalAddress);

}
}

UInt32 sNo = SendRequest(Query, APACHE_QUERY_REQUEST, userName, companyName);

_requestResponseTable.Add(sNo, new ResponseEventData(are, responseData));
}
catch (System.Exception e)
{
Destroy(true);
throw e;
}
finally
{
}
}

/// <summary>
/// Logins the specified user name.
/// </summary>
/// <param name="userName">Name of the user.</param>
/// <param name="password">The password.</param>
/// <param name="company">The company.</param>
/// <param name="are">The are.</param>
/// <param name="responseData">The response data.</param>
/// <returns></returns>
private UInt32 Login(string userName, string password, string company, ref AutoResetEvent are, ref StringBuilder responseData)
{
try
{
//This might change with requirement.
string LogonQuery = String.Format("LOGON USERNAME=\"{0}\" PASSWORD=\"{1}\" COMPANY=\"{2}\"",
userName.ToUpper(), password.ToUpper(), company.ToUpper());

UInt32 sNo = SendRequest(LogonQuery, LOGON_REQUEST_MSG, userName, company);
_requestResponseTable.Add(sNo, new ResponseEventData(are, responseData));
return sNo;
}
catch (System.Exception exception)
{
throw exception;
}
}

/// <summary>
/// Destroys the specified destroy receiver thread.
/// </summary>
/// <param name="destroyReceiverThread">if set to <c>true</c> [destroy receiver thread].</param>
private void Destroy(bool destroyReceiverThread)
{
if (destroyReceiverThread)
{
if (_receiverThread != null)
{
if (_receiverThread.IsAlive)
{
_receiverThread.Abort();
}
}
}

if (_socket == null)
return;
_socket.Shutdown(SocketShutdown.Both);
_socket.Close();
_socket = null;

_singletonTcpChannel = null;
}

/// <summary>
/// Sends the request.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="msgType">Type of the MSG.</param>
/// <param name="userName">Name of the user.</param>
/// <param name="companyName">Name of the company.</param>
/// <returns></returns>
private UInt32 SendRequest(string query, char msgType, string userName, string companyName)
{
if (query.Length == 0)
{
throw (new ArgumentNullException("Query", "Query parameter of ExecuteChannel method is null"));
}

try
{
UInt32 currentSequenceNumber = (UInt32)Interlocked.Increment(ref SequenceNumber);

ushort dataOffset = (ushort)(MDTF_HDR_SIZE +
((userName.Length > 0) ? (USERNAME_HEADER_SIZE + COMPANY_HEADER_SIZE) : 0));

short MsgSize = (short)(dataOffset + query.Length + 1); //Add one for terminator

byte[] sndBuf = new byte[MsgSize + 1]; // for the SYNC_BYTE
byte[] forconversion = new byte[2];

sndBuf[0] = Convert.ToByte(SYNC_BYTE);

ushort MsgSizeUshort = (ushort)(IPAddress.HostToNetworkOrder(MsgSize));
forconversion = BitConverter.GetBytes(MsgSizeUshort);
Array.Copy(forconversion, 0, sndBuf, 1, 2);

sndBuf[3] = Convert.ToByte(dataOffset);
sndBuf[4] = Convert.ToByte(msgType);

UInt32 sequenceNumberNO = (UInt32)IPAddress.HostToNetworkOrder((Int32)currentSequenceNumber);
forconversion = BitConverter.GetBytes(sequenceNumberNO);
Array.Copy(forconversion, 0, sndBuf, 5, 4);

forconversion = BitConverter.GetBytes(_protocolId);
Array.Copy(forconversion, 0, sndBuf, 9, 2);

forconversion = BitConverter.GetBytes(RMDS_VER_ID);
Array.Copy(forconversion, 0, sndBuf, 11, 2);

if (userName.Length > 0)
{
for (int i = 0; i < USERNAME_HEADER_SIZE; i++)
{
// Displace one byte because of SYNC_BYTE
sndBuf[1 + MDTF_HDR_SIZE + i] = (i < userName.Length) ?
Convert.ToByte(char.ToUpper(userName[i]))
: Convert.ToByte(' ');
}

for (int i = 0; i < COMPANY_HEADER_SIZE; i++)
{
// Displace one byte because of SYNC_BYTE
sndBuf[1 + MDTF_HDR_SIZE + USERNAME_HEADER_SIZE + i] =
(i < companyName.Length) ?
Convert.ToByte(char.ToUpper(companyName[i]))
: Convert.ToByte(' ');
}
}

dataOffset++; // Add one for SyncByte
MsgSize++;
System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
Array.Copy(encoding.GetBytes(query), 0, sndBuf, dataOffset, query.Length);

sndBuf[query.Length + dataOffset] = Convert.ToByte('\0');

SendServerRequest(sndBuf, SocketFlags.None);

return currentSequenceNumber;
}
catch (Exception e)
{
throw e;
}
}

/// <summary>
/// Sends the server request.
/// </summary>
/// <param name="sndBuf">The SND buf.</param>
/// <param name="flags">The flags.</param>
/// <returns></returns>

private int SendServerRequest(byte[] sndBuf, SocketFlags flags)
{
int bytessent = 0;
lock (lockSocketObject)
{
bytessent = _socket.Send(sndBuf, sndBuf.Length, flags);
}
return bytessent;
}

/// <summary>
/// Receivers the thread.
/// </summary>
/// <param name="socketObj">The socket obj.</param>
private static void ReceiverThread(Object socketObj)
{
try
{
TcpChannel callingObj = (TcpChannel)socketObj;
Socket sock = callingObj._socket;

ArrayList listenList = new ArrayList();

while (callingObj._continueReceiving)
{
string response = "";
UInt32 responseSequenceNumber = 0;
try
{
byte[] rcvBuf;
byte[] data;
data = new byte[3];
int bytesReceived = 0;

listenList.Clear();
listenList.Add(sock);
Socket.Select(listenList, null, null, 300);

lock (callingObj.lockSocketObject)
{
if (sock.Available > 0)
{
bytesReceived = sock.Receive(data, 3, SocketFlags.None);
}
}
if (bytesReceived == 0)
{
continue;
}

ushort MsgSize_NOrder = BitConverter.ToUInt16(data, 1);
int msgSize = IPAddress.NetworkToHostOrder((short)MsgSize_NOrder) + 1;

rcvBuf = new byte[msgSize];
Array.Copy(data, 0, rcvBuf, 0, 3);

data = new byte[msgSize - 3];
lock (callingObj.lockSocketObject)
{
int bytesToReceieve = msgSize - 3;
int startPos = 3;
// Used a Do while loop because there has to be atleast one iteration anyway.
do
{
int received = sock.Receive(data, bytesToReceieve, SocketFlags.None);
bytesToReceieve -= received;
Array.Copy(data, 0, rcvBuf, startPos, received);
startPos += received;
} while (bytesToReceieve > 0);
}
// We have the complete message(including SYNC_BYTE) in the buffer now.

// Handle Keep alive messages here.
char msgType = Convert.ToChar(rcvBuf[4]);

if (msgType == KEEP_ALIVE_REQUEST_MSG)
{
//T1MobileLogger.Log(Category.Info, "Socket Received KEEP_ALIVE_REQUEST_MSG");
// If the message is a keep alive message,
// We should just send the whole message back, replacing the
// message type with Keep Alive Response type.

rcvBuf[4] = Convert.ToByte(KEEP_ALIVE_RESPONSE_MSG);
callingObj.SendServerRequest(rcvBuf, SocketFlags.None);

continue;
}

//Remove the SYNC BYTE
Array.Copy(rcvBuf, 1, rcvBuf, 0, rcvBuf.Length - 1);
msgSize--;

// Handle LOGON, LOGOFF and query responses here
if ((msgType == LOGON_RESPONSE_MSG) || (msgType == LOGON_LOGOFF_SUCCESS))
{
int dataOffset = Convert.ToInt16(rcvBuf[2]);

byte[] forconversion;

forconversion = new byte[4];
Array.Copy(rcvBuf, SEQUENCE_NUMBER_POSITION, forconversion, 0, 4);
UInt32 intNOrder = BitConverter.ToUInt32(forconversion, 0);
responseSequenceNumber = (UInt32)IPAddress.NetworkToHostOrder((int)intNOrder);

forconversion = new byte[2];
Array.Copy(rcvBuf, dataOffset, forconversion, 0, 2);
ushort datasize = BitConverter.ToUInt16(forconversion, 0);

int LogonTextLength = IPAddress.NetworkToHostOrder((short)datasize);
LogonTextLength -= 6;

forconversion = new byte[LogonTextLength];
Array.Copy(rcvBuf, dataOffset + 6, forconversion, 0, LogonTextLength);

response = Encoding.ASCII.GetString(forconversion);
}
else if ((msgType == LOGOFF_RESPONSE_MSG) || (msgType == LOGON_LOGOFF_ERROR))
{
//ProcessLogoffResponse(rcvBuf);
int dataOffset = Convert.ToInt16(rcvBuf[2]);

byte[] forconversion;

forconversion = new byte[4];
Array.Copy(rcvBuf, SEQUENCE_NUMBER_POSITION, forconversion, 0, 4);
UInt32 intNOrder = BitConverter.ToUInt32(forconversion, 0);
responseSequenceNumber = (UInt32)IPAddress.NetworkToHostOrder((int)intNOrder);

forconversion = new byte[2];
Array.Copy(rcvBuf, dataOffset, forconversion, 0, 2);
ushort datasize = BitConverter.ToUInt16(forconversion, 0);

int LogoffTextLength = IPAddress.NetworkToHostOrder((short)datasize);
LogoffTextLength -= 6;

forconversion = new byte[LogoffTextLength];
Array.Copy(rcvBuf, dataOffset + 6, forconversion, 0, LogoffTextLength);

response = Encoding.ASCII.GetString(forconversion);

string[] logoffResponseFields = response.ToString().Split(new char[] { ' ' });
string logicalAddress = "";
foreach (string s in logoffResponseFields)
{
if (s.IndexOf("LOGICAL_ADDR") != -1)
{
logicalAddress = s.Split(new char[] { '=' })[1];
logicalAddress.Trim();
logicalAddress = logicalAddress.ToUpper();
break;
}
}
if (logicalAddress.Length > 0)
{
lock (lockCurrentUsers)
{
foreach (object key in _currentUsers.Keys)
{
if ((string)_currentUsers[key] == logicalAddress)
{
_currentUsers.Remove(key);
break;
}
}
}
}
}
else if (msgType == ISI_OPEN_FID_RESPONSE)
{
ProcessResponse(rcvBuf, ref response, ref responseSequenceNumber);
}
else
{
response = String.Format("Incorrect Message Type:{0}", msgType);
}

Object obj = null;
lock (callingObj.lockTableObject)
{
if (callingObj._requestResponseTable.Contains(responseSequenceNumber))
{
obj = callingObj._requestResponseTable[responseSequenceNumber];
callingObj._requestResponseTable.Remove(responseSequenceNumber);
}
}

if (obj != null)
{
ResponseEventData re = (ResponseEventData)obj;
re.response.Append(response);
re.are.Set();
}
}

catch (System.Exception e)
{
loggerProvider.Write(string.Format("Error on TCP Channel {0} ", e), LogCategory.Error);
try
{
_singletonTcpChannel.Destroy(false);
}
catch (System.Exception e1)
{
loggerProvider.Write(string.Format("Error on TCP Channel {0} ", e1),
LogCategory.Error);
}
finally
{
callingObj._continueReceiving = false;
}
}
}
}
catch (System.Threading.ThreadAbortException tae)
{
Thread.ResetAbort();
throw tae;
}
}

/// <summary>
/// Processes the response.
/// </summary>
/// <param name="rcvBuf">The RCV buf.</param>
/// <param name="response">The response.</param>
/// <param name="responseSequenceNumber">The response sequence number.</param>
private static void ProcessResponse(byte[] rcvBuf, ref string response, ref UInt32 responseSequenceNumber)
{
try
{
// When this method is called, SYNCBYTE would have been removed
int dataOffset = Convert.ToInt16(rcvBuf[2]);

byte[] forconversion;

forconversion = new byte[4];
Array.Copy(rcvBuf, SEQUENCE_NUMBER_POSITION, forconversion, 0, 4);
UInt32 intNOrder = BitConverter.ToUInt32(forconversion, 0);
responseSequenceNumber = (UInt32)IPAddress.NetworkToHostOrder((int)intNOrder);

forconversion = new byte[2];
Array.Copy(rcvBuf, dataOffset, forconversion, 0, 2);
ushort NOrder = BitConverter.ToUInt16(forconversion, 0);
ushort errorCode = (ushort)IPAddress.NetworkToHostOrder((short)NOrder);

Array.Copy(rcvBuf, dataOffset + 2, forconversion, 0, 2);
NOrder = BitConverter.ToUInt16(forconversion, 0);
ushort responseType = (ushort)IPAddress.NetworkToHostOrder((short)NOrder);

Array.Copy(rcvBuf, 0, forconversion, 0, 2);
NOrder = BitConverter.ToUInt16(forconversion, 0);
int MsgSize = IPAddress.NetworkToHostOrder((short)NOrder);

int data_Pos = dataOffset + 2 + 2 + 2 + 1 + 1;
// dataoffset(12)+errorcode(2)+responsetype(2)+ numberofrecords(2)+responsecontrol(1)+headerextension(1)
response = Encoding.UTF8.GetString(rcvBuf, data_Pos, MsgSize - data_Pos);

string tcpRawResponse = response;

//To remove the prefixed unwanted stuff.
if (response.IndexOf("<?xml") >= 0)
{
response = response.Substring(response.IndexOf("<?xml"));
}

if (errorCode != 0 && responseType != XML_RESPONSE_TYPE)
{
StringBuilder responseBuilder = new StringBuilder();
XmlWriterSettings settings = new XmlWriterSettings();
settings.Encoding = Encoding.GetEncoding("UTF-8");
using (XmlWriter writer = XmlWriter.Create(responseBuilder, settings))
{
writer.WriteStartElement("Response", "ErrorInfo");

writer.WriteStartElement("Error");
writer.WriteAttributeString("errormsg", response);

writer.WriteEndElement();

writer.WriteEndElement();
}
response = responseBuilder.ToString();
}
}
catch (System.Exception e)
{
throw e;
}
}

#endregion

#region Public Methods

/// <summary>
/// Gets the TCP Channel Communicator.
/// </summary>
/// <returns></returns>
public static TcpChannelCommunicator GetTcpChannelCommunicator()
{
lock (lockSingletonSocketObject)
{
if (_singletonTcpChannel == null)
{
_singletonTcpChannel = new TcpChannelCommunicator();
}
}
return _singletonTcpChannel;
}

/// <summary>
/// Executes the command.
/// </summary>
/// <param name="query">The query.</param>
/// <returns></returns>
public string ExecuteCommand(string query)
{
return ExecuteCommand(query, false);
}

/// <summary>
/// Executes the command.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="autoRefresh">if set to <c>true</c> [auto refresh].</param>
/// <returns></returns>
public string ExecuteCommand(string query, bool autoRefresh)
{
if (string.IsNullOrEmpty(query))
{
throw new ArgumentNullException("query", null);
}
try
{
AutoResetEvent are = new AutoResetEvent(false);
StringBuilder responseData = new StringBuilder();
if (string.IsNullOrEmpty(_userName) ||
string.IsNullOrEmpty(_password) ||
string.IsNullOrEmpty(_domain))
{
_userName = Properties.Settings.Default.tcpUser;
_company = Properties.Settings.Default.tcpCompany;
_password = Properties.Settings.Default.tcpPassword;
_domain = Properties.Settings.Default.tcpdomain;
}

lock (_lockExecuteCommanAsync)
{
ExecuteCommandAsync(query, _userName, _password,
_domain, ref are, ref responseData, autoRefresh);
}
are.WaitOne();

if (responseData == null || responseData.Length == 0)
{
throw new Exception();
}

return responseData.ToString();
}
catch (Exception exception)
{
Destroy(true);
throw exception;
}
}

/// <summary>
/// Closes the specified con.
/// </summary>
public void Close()
{
if (_socket == null)
return;

// Send Logoff request
string logoffQuery = "LOGOFF";
SendRequest(logoffQuery, LOGOFF_REQUEST_MSG, _userName, _company);

Thread.Sleep(1000);
_continueReceiving = false;
_receiverThread.Join(1000);

Destroy(true);
}

#endregion
}
}

STEP 2: Set the Properties:

Set as application level,
tcpPort = 10545

similary for other variables.

STEP 3: How to use:

TcpChannelCommunicator executor = TcpChannelCommunicator.GetTcpChannelCommunicator();

string query = "write your query here";

STEP 4: Read the response.

string response = executor.ExecuteCommand(query);

STEP 5: Close the connection.

executor.Close();

Note: The company attribute was used as a specific requirement. It might not be needed in most of the cases.



By [)ia6l0 iii   Popularity  (1598 Views)