Two way C++ to C# communication using named pipes - c#

I am trying to have a 2-way communication between a VC++ 6 app and a C# app. I am using named pipes. In my C++ code I can read the message from the C# client but then the server "dies" and I have to restart it again.
What I want to do is have the C# app connect to the C++ app, request a status, and the C++ app goes off and checks the status, and then returns either "busy" or "idle".
I can't write anything back to the C# client as it says the connection has been closed. Some things I have commented out are things I have tried already.
C++ code (started as a thread)
UINT CNamedPipe::StartNamedPipeServer()
{
LPTSTR lpszPipename = "\\\\.\\pipe\\SAPipe";
HANDLE hPipe;
BOOL flg;
DWORD dwWrite,dwRead;
char szServerUpdate[200];
char szClientUpdate[200];
hPipe = CreateNamedPipe ( lpszPipename,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE |
PIPE_READMODE_MESSAGE |
PIPE_NOWAIT, //changed from nowait
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
PIPE_TIMEOUT, // client time-out
NULL); // no security attribute
if (hPipe == INVALID_HANDLE_VALUE)
return 0;
ConnectNamedPipe(hPipe, NULL);
while(m_bServerActive) //This seems to work well ....
{
//Read from client
flg = ReadFile(hPipe,szClientUpdate,strlen(szClientUpdate),&dwRead, NULL);
if(flg) //Read something from the client!!!!
{
CString csMsg,csTmp;
for(int i=0;i<dwRead;i++){
csTmp.Format("%c",szClientUpdate[i]);
csMsg += csTmp;
}
AfxMessageBox("Client message: " + csMsg);
strcpy( szServerUpdate,"busy");
//Write status to Client
flg = WriteFile(hPipe, szServerUpdate, strlen(szServerUpdate), &dwWrite, NULL);
EndServer();
StartServer();
}
}
return 0;
}
C# Code:
public void ThreadStartClient(object obj)
{
// Ensure that we only start the client after the server has created the pipe
ManualResetEvent SyncClientServer = (ManualResetEvent)obj;
// Only continue after the server was created -- otherwise we just fail badly
// SyncClientServer.WaitOne();
using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(".", "SAPipe"))
{
// The connect function will indefinately wait for the pipe to become available
// If that is not acceptable specify a maximum waiting time (in ms)
pipeStream.Connect();
//Write from client to server
using (StreamWriter sw = new StreamWriter(pipeStream))
{
sw.WriteLine("What's your status?");
}
//Read server reply
/*using (StreamReader sr = new StreamReader(pipeStream))
{
string temp = "";
temp = sr.ReadLine(); //Pipe is already closed here ... why?
MessageBox.Show(temp);
}*/
//pipeStream.Close();
}
}
}

Disposing of a StreamWriter or StreamReader will close the underlying stream.
Your using statements therefore will be causing the stream to close.
public void ThreadStartClient(object obj)
{
// Ensure that we only start the client after the server has created the pipe
ManualResetEvent SyncClientServer = (ManualResetEvent)obj;
// Only continue after the server was created -- otherwise we just fail badly
// SyncClientServer.WaitOne();
using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(".", "SAPipe"))
{
// The connect function will indefinately wait for the pipe to become available
// If that is not acceptable specify a maximum waiting time (in ms)
pipeStream.Connect();
//Write from client to server
StreamWriter sw = new StreamWriter(pipeStream))
sw.WriteLine("What's your status?");
//Read server reply
StreamReader sr = new StreamReader(pipeStream)
string temp = "";
temp = sr.ReadLine(); //Pipe is already closed here ... why?
MessageBox.Show(temp);
}
}
It should also be noted that because you wrap your stream in a using statement, the commented out pipeStream.Close() function isn't needed.

Ok, got it working for my application .... thanks Blam !
Here's the C++ server (run this inside a thread):
UINT CNamedPipe::StartNamedPipeServer()
{
if(!m_bServerActive)
return 0;
LPTSTR lpszPipename = "\\\\.\\pipe\\MyPipe";
HANDLE hPipe;
BOOL flg;
DWORD dwWrite,dwRead;
char szServerUpdate[200];
char szClientUpdate[200];
hPipe = CreateNamedPipe ( lpszPipename,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE |
PIPE_READMODE_MESSAGE |
PIPE_WAIT, //HAS TO BE THIS
PIPE_UNLIMITED_INSTANCES, // max. instances
BUFSIZE, // output buffer size
BUFSIZE, // input buffer size
PIPE_TIMEOUT, // client time-out
NULL); // no security attribute
if (hPipe == INVALID_HANDLE_VALUE)
return 0;
ConnectNamedPipe(hPipe, NULL);
strcpy( szServerUpdate,"busy");
//Write status to Client
flg = WriteFile(hPipe, szServerUpdate, strlen(szServerUpdate), &dwWrite, NULL);
EndServer();
StartServer();
return 0;
}
And here's the C# client:
public void ThreadStartClient(object obj)
{
// Ensure that we only start the client after the server has created the pipe
ManualResetEvent SyncClientServer = (ManualResetEvent)obj;
using (NamedPipeClientStream pipeStream = new NamedPipeClientStream(".", "MyPipe", PipeDirection.InOut))
{
// The connect function will indefinately wait for the pipe to become available
// If that is not acceptable specify a maximum waiting time (in ms)
pipeStream.Connect();
if (!pipeStream.IsConnected) //It thinks it's connected but can't read anything ....
{
MessageBox.Show("Failed to connect ....");
return;
}
//Read server reply
StreamReader sr = new StreamReader(pipeStream);
char[] c = new char[200];
while (sr.Peek() >= 0)
{
sr.Read(c, 0, c.Length);
}
string s = new string(c);
MessageBox.Show(s);
}
}
I'm not actually sending anything from the client to the server because I don't need to ... the key thing in this was the PIPE_WAIT parameter in the CreateNamedPipe() function. This makes the server wait for a client connection.

Related

C# tcpclient not writing to socket...or maybe I'm not reading all of the response?

I'm trying to write a C# console app to interface with a PC Miler telnet server. I managed to get this to work in powershell, but my C# code is not working.
static void Main(string[] args)
{
byte[] ReadBuffer = new byte[1024];
string[] stringSeparators = new string[]{"READY"};
try
{
TcpClient Socket = new TcpClient("myServer", 8320);
if (Socket.Connected) {
NetworkStream netStream = Socket.GetStream();
string PCMSResponse = "";
// Check for the READY prompt
if (netStream.CanRead) {
int byteRead = 0;
while (netStream.DataAvailable) {
byteRead = netStream.Read(ReadBuffer, 0, ReadBuffer.Length);
PCMSResponse += Encoding.ASCII.GetString(ReadBuffer, 0 , byteRead).Trim();
}
} else {
Console.WriteLine("Cannot read from myServer");
}
Thread.Sleep(1000);
// Get New Trip
if (netStream.CanWrite) {
Byte[] PCMSCommandBuffer = Encoding.ASCII.GetBytes("PCMSNewTrip");
netStream.Write(PCMSCommandBuffer, 0 , PCMSCommandBuffer.Length);
Thread.Sleep(1000);
} else {
Console.WriteLine("Cannot write to myServer.");
}
if (netStream.CanRead) {
int byteRead = 0;
while (netStream.DataAvailable) {
byteRead = netStream.Read(ReadBuffer, 0, ReadBuffer.Length);
PCMSResponse += Encoding.ASCII.GetString(ReadBuffer, 0, byteRead).Trim();
}
} else {
Console.WriteLine("Cannot read from myServer");
}
string[] ResponseArray = PCMSResponse.Split(stringSeparators, StringSplitOptions.None);
int c = ResponseArray.Length - 2;
string TripID = ResponseArray[c].Replace(System.Environment.NewLine, "").Replace("\0", "");
Console.WriteLine(PCMSResponse);
}
}
catch (SocketException)
{
Console.WriteLine("Unable to connect to server")
}
}
Expected output
ALK PCMILER SERVER READY
pcmsnewtrip
53
READY
Actual output
ALK PCMILER SERVER READY
It seems like I am not actually writing to the server because if I were to write an invalid command to the server I would see an error like this:
ALK PCMILER SERVER READY
pmctripnew
NO SUCH FUNCTION
READY
I know I can write to the socket because I'm not getting my error message Cannot write to myServer.
Not sure what I'm doing wrong. :(
Let me know if you'd also like to see the powershell code.
EDIT - If you're going to edit this post, at least keep the expected output AS EXPECTED! Changing the expected output changes the intent of the post. (EG, if I want apples but you edit my post to ask for oranges, I'm not going to get the correct answer.)
So the issue turns out to be that the NetworkStream.Write method does not end a string with a line terminator. As a result, when I wrote my PCMSCommandBuffer bytes to the stream, the command was never processed by the server (because its waiting for more of the command or the enter key to let it know you're done). My solution was to assign the stream to a StreamWriter. Then I can use the WriteLine method whichdoes send a line terminator.
streamWriter = new StreamWriter(netStream);
// Get New Trip
if (netStream.CanWrite) {
Byte[] PCMSCommandBuffer = Encoding.ASCII.GetBytes("PCMSNewTrip");
//netStream.Write(PCMSCommandBuffer, 0 , PCMSCommandBuffer.Length);
streamWriter.WriteLine("PCMSNewTrip");
//netStream.Flush();
streamWriter.Flush();
Console.WriteLine(PCMSCommandBuffer.Length + " PCMSNewTrip");
Thread.Sleep(1000);
} else {
Console.WriteLine("Cannot write to EW-APP1.");
}

TCP IP Client in WPF for Ubuntu 14 Server

I am trying to create TCP IP Client in WPF GUI / C#.NET for a Ubunu Server.
Problem: I am able to connect to the server machine , the connection work correctly, send the message correctly and the ubuntu console show as well , Client Connected and sent this command like Start Video Feed on the server but When it comes to Read the Response nothing happens - It does not read the byte array that should be returned by the Ubuntu Server. Actually on message - 102 it should start the video feed on the server and return back the video feed byte array which should be read further and display the video. No code written yet to display the video feed as I am unable to read the feed from the server however, the client sends the commands(messages) to the server correctly as mentioned can view it on a console of the Ubuntu server machine. Please suggest thanks !!
Below is the code please have a look and suggest me what I am doing wrong :
namespace POC_TCP_Listener
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private int WhichEventFired = 0;
private void Button_Click_1(object sender, RoutedEventArgs e)
{
try
{
// string message = "{Site: 1}";
WhichEventFired = 1; //Start Video Feed
Thread ClientThread = new Thread(new ThreadStart(ConnectToServerAndRetrieveBytes));
ClientThread.Start();
}
catch (Exception ex)
{
string st = ex.Message;
}
}
private void ConnectToServerAndRetrieveBytes()
{
TcpClient TCP = new TcpClient();
TCP.Connect("IPAddress", 5001);
byte[] packet;
var size = 9;
var header = 102;
var siteId = 1;
var state = 1;
if (WhichEventFired == 1)
{
header = 102; // Start Video Feed
}
else if (WhichEventFired == 2)
{
header = 114; // Stop Video Feed
}
else
{
header = 115; // query Temperature
}
// <8> <115> <1>
packet = BitConverter.GetBytes(size).Reverse().Concat(BitConverter.GetBytes(header).Reverse()).Concat(BitConverter.GetBytes(siteId).Reverse()).Concat(BitConverter.GetBytes(state).Reverse()).ToArray();
// Translate the passed message into ASCII and store it as a Byte array.
Byte[] data = packet;
// Get a client stream for reading and writing.
NetworkStream stream = TCP.GetStream();
// Send the message to the connected TcpServer.
stream.Write(data, 0, data.Length);
byte[] buffer = new byte[64 * 1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
// In the below line - it stops and nothing happens after it - Please Suggest
while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
// return ms.ToArray();
}
stream.Close();
TCP.Close();
}
}
}
strong text
Please suggest why it stops working or let me know if I am doing anything wrong.
I think the problem is that if there are no more packages left, it escapes the while-loop. If you want to check it endlessly you should try using a for. But i am not sure.
Yeah that should be it. If there are no Packages in the moment the while-loop is starting, it terminates itself because there is nothing to read. So you could check if the Ubuntu Server returns you something.

Sending message from C# client to C server

I'm fairly new to socket programming, but, using an online tutorial, I've successfully sent a short string from one machine to another using C.
The problem I'm having is with trying to send a string from a client written in C#. The server (written in C) prints out a blank/empty string.
Here is the C code that runs on the "server" machine (in this case a router running OpenWRT):
int main(int argc, char *argv[])
{
int listenfd = 0, connfd = 0;
struct sockaddr_in serv_addr;
char recvBuff[1025];
int bytesRead;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, '0', sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(1234);
bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
printf("Listening for string on port 1234...\n");
listen(listenfd, 10);
while(1)
{
connfd = accept(listenfd, (struct sockaddr*)NULL, NULL);
bytesRead = recv(connfd, recvBuff, 1024, 0); // Receive string
if (bytesRead < 0)
{
printf("Error reading from stream\n");
}
recvBuff[bytesRead] = 0; // null-terminate the string
printf("%d:%s\n", bytesRead, recvBuff);
close(connfd);
sleep(1);
}
}
When sending this little server a string from another C program it works exactly as expected (prints the string out then waits for another one). [Note: I don't think the C client's code is relevant, but I can post it if need be]
However, when trying to send it a string from a C# program (copied below), the server prints out 0: (i.e. 0 bytes read, followed by an empty string) and I can't for the life of me figure out what the issue is. Both apps are pretty straightforward, so I'm assuming I should be using something other than WriteLine (I've also tried Write but to no avail).
C# Client:
namespace SocketTest
{
class Program
{
static void Main(string[] args)
{
TcpClient client = new TcpClient("10.45.13.220", 1234);
Stream stream = client.GetStream();
StreamWriter writer = new StreamWriter(stream);
writer.WriteLine("Testing...");
client.Close();
}
}
}
To null terminate a string use
recvBuff[bytesRead] = '\0';
And call close on writer (which causes the writer to flush any pending bytes)
writer.WriteLine("Testing...");
writer.Close();
client.Close();

Named pipes async

Im trying to set up a named pipe server and client to send data between two programs.
My issue is that when i data is recived eg. BeginRead command om server triggers after i have serialized an object from the client it triggers the callback like 20 times for the same message. The goal is that the client program will send commands to the server program. And when the server processes tasks it will send status updates back to the client when there is one connected.
Here is my current test program.
class Program
{
static void Main(string[] args)
{
var server = new PipeServer();
server.Init();
var client = new PipeClient();
if (client.Connect())
{
Console.WriteLine("Connected to server.");
}
else
{
Console.WriteLine("Connection failed.");
return;
}
while (true)
{
Console.Write(" \\> ");
string input = Console.ReadLine();
if (string.IsNullOrEmpty(input)) break;
var arr = input.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
int value = 0;
if (arr.Length != 2) break;
if (!int.TryParse(arr[1], out value)) break;
var obj = new PipeObject { Name = arr[0], Value = value };
client.Send(obj);
//string result = f.Deserialize(client) as string;
//Console.WriteLine(result);
}
}
}
internal class PipeServer
{
IFormatter Formatter = new BinaryFormatter();
public NamedPipeServerStream Instance { get; internal set; }
public bool IsConnected { get; internal set; }
byte[] buffer = new byte[65535];
public object Message { get; set; }
StreamReader sr;
StreamWriter sw;
internal PipeServer()
{
IsConnected = false;
}
public void Init()
{
var ps = new PipeSecurity();
ps.AddAccessRule(new PipeAccessRule(WindowsIdentity.GetCurrent().User, PipeAccessRights.FullControl, AccessControlType.Allow));
ps.AddAccessRule(new PipeAccessRule(new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null), PipeAccessRights.ReadWrite, AccessControlType.Allow));
Instance = new NamedPipeServerStream("Levscan4Pipe", PipeDirection.InOut, 1, PipeTransmissionMode.Message, PipeOptions.Asynchronous | PipeOptions.WriteThrough, 65535, 65535, ps);
sr = new StreamReader(Instance);
sw = new StreamWriter(Instance);
Instance.BeginWaitForConnection(OnClientConnected, Instance);
Thread t = new Thread(Run);
t.Start();
}
void Run()
{
int index = 0;
if (IsConnected)
{
try
{
Instance.BeginRead(buffer, 0, buffer.Length, OnRead_Completed, Instance);
//index += Instance.Read(buffer, 0, buffer.Length);
//try
//{
// using (var ms = new MemoryStream(buffer))
// {
// Message = Formatter.Deserialize(ms);
// index = 0;
// }
//}
//catch (Exception e)
//{
// Debug.WriteLine(e.Message);
// Debug.WriteLine(e.StackTrace);
//}
}
catch (IOException)
{
IsConnected = false;
Instance.Disconnect();
}
}
Thread.Sleep(Timeout.Infinite);
//Instance.WaitForConnection();
//Thread t = new Thread(Run);
//t.Start();
}
void OnClientConnected(IAsyncResult ar)
{
Instance.EndWaitForConnection(ar);
IsConnected = true;
}
void OnRead_Completed(IAsyncResult ar)
{
var bytes = Instance.EndRead(ar);
Debug.WriteLine("{1} > Read completed - bytes read: {0}".FormatWith(bytes, DateTime.Now.ToString()));
//try
//{
// using (var ms = new MemoryStream(buffer))
// {
// Message = Formatter.Deserialize(ms);
// }
//}
//catch (Exception e)
//{
// Debug.WriteLine(e.Message);
// Debug.WriteLine(e.StackTrace);
//}
}
}
internal class PipeClient
{
IFormatter f = new BinaryFormatter();
public NamedPipeClientStream Instance { get; internal set; }
StreamWriter sw;
StreamReader sr;
public PipeClient()
{
Instance = new NamedPipeClientStream(".", "Levscan4Pipe", PipeDirection.InOut, PipeOptions.Asynchronous | PipeOptions.WriteThrough);
sr = new StreamReader(Instance);
sw = new StreamWriter(Instance);
}
public bool Connect()
{
try
{
Instance.Connect(5000);
Instance.ReadMode = PipeTransmissionMode.Message;
Instance.WaitForPipeDrain();
return true;
}
catch
{
return false;
}
}
public void Send(object obj)
{
f.Serialize(Instance, obj);
Instance.Flush();
Instance.WaitForPipeDrain();
}
}
Edit
Changed the while loop to a if, to start the BeginRead. This solves the multiple callbacks but i stil dont get a complete message.
If the server is writing to the stream like:
write field 1
write field 2
write field 3
etc.
There is some time between the writes, and the receiver (your program) can be reading the first three fields while the server is still writing the others. The pipe stream doesn't know when the server is finished writing, so it can't buffer everything and send it to you all in one big chunk.
When the server writes everything to a memory stream first and then copies the memory stream to the pipe stream, your program can get it all at once. Maybe. If the server is sending a very large packet, you might read just part of it.
The pipe stream is just a stream of bytes. It doesn't impose any format on the data. It doesn't have any concept of records or anything like that. So you have to treat it like a stream of bytes and do your own composing of records, etc.
If you need to know the size of the record sent from the server, the server has to put that information in the stream for you. Typically, the server will write the length and then the data. The receiver can then read the length, convert it to an integer, and then read that many bytes from the stream. And, yes, it might take multiple reads in order to get all of the bytes. That's just the nature of a byte stream.
The other way to handle this is to have an end-of-record marker. So the server sends its data and your program reads until it finds the byte sequence that signifies the end of the record. You have to be careful, though, because the server could be sending multiple records and your read could grab the end of one record as well as the beginning of the next.
Working with byte streams can be a lot of work because you have to reconstruct records after reading the bytes. It's much easier to use an existing framework (like WCF, as mentioned in one of the comments) if you can.

Duplex NamePipe between C# and C++

I'am having some trouble to make a Duplex communication between a C# server and a C++ work:
The objective is to create the pipe, read something from the client and write something back.
Everything is working fine if i'm just reading from the client or just writing to the client but I can't do both one after the other!
Here's my C# server:
// Create a name pipe
using (NamedPipeServerStream pipeStream = new NamedPipeServerStream("TestPipe"))
{
Console.WriteLine("[Server] Pipe created {0}", pipeStream.GetHashCode());
// Wait for a connection
pipeStream.WaitForConnection();
Console.WriteLine("[Server] Pipe connection established");
//Reading Part
using (StreamReader sr = new StreamReader(pipeStream))
{
string temp;
while ((temp = sr.ReadLine()) != null)
{
Console.WriteLine("{0}: {1}", DateTime.Now, temp);
}
}
//Writing Part
using (StreamWriter sw = new StreamWriter(pipeStream))
{
sw.AutoFlush = true;
String st = "send Back\0";
sw.WriteLine(st);
}
}
And Here is the C++ client:
HANDLE hFile;
BOOL flg;
DWORD dwWrite;
char szPipeUpdate[200];
hFile = CreateFile(L"\\\\.\\pipe\\TestPipe", GENERIC_WRITE|GENERIC_READ,
0, NULL, OPEN_EXISTING,
0, NULL);
strcpy(szPipeUpdate,"Sending some data from client to server!");
if(hFile == INVALID_HANDLE_VALUE)
{
DWORD dw = GetLastError();
printf("CreateFile failed for Named Pipe client\n:" );
}
else
{
flg = WriteFile(hFile, szPipeUpdate, strlen(szPipeUpdate), &dwWrite, NULL);
if (FALSE == flg)
{
printf("WriteFile failed for Named Pipe client\n");
}
else
{
printf("WriteFile succeeded for Named Pipe client\n");
}
}
printf("Let's read!\n");
//Read the datas sent by the server
BOOL fFinishRead = FALSE;
do
{
char chResponse[200];
DWORD cbResponse, cbRead;
cbResponse = sizeof(chResponse);
fFinishRead = ReadFile(
hFile, // Handle of the pipe
chResponse, // Buffer to receive the reply
cbResponse, // Size of buffer in bytes
&cbRead, // Number of bytes read
NULL // Not overlapped
);
if (!fFinishRead && ERROR_MORE_DATA != GetLastError())
{
DWORD dwError = GetLastError();
wprintf(L"ReadFile from pipe failed w/err 0x%08lx\n", dwError);
break;
}
std::cout << chResponse;
} while (!fFinishRead); // Repeat loop if ERROR_MORE_DATA
CloseHandle(hFile);
With the Server & Client NamedPipeStreams you need to make sure at least one end of the connection is attempting to read at all times, otherwise writes will fail. I'd guess it's a timing issue between your client & server around whichever is listening/writing. Here's a quick blog post explaining this behavior as well.
I'd recomend using two streams on the client & server, both reading/writing from the same pipe.

Categories