Here is my code:
public class ServerClient
{
public TcpClient tcp;
public StreamReader streamReader;
public StreamWriter streamWriter;
public int accountId;
public int connectionId;
public ServerClient(TcpClient clientSocket)
{
tcp = clientSocket;
streamReader = new StreamReader(tcp.GetStream(), false);
streamWriter = new StreamWriter(tcp.GetStream());
clientSocket.NoDelay = true;
}
}
Here is how i am listening for data, this is called in a while loop:
// Check for message from Client.
NetworkStream s = c.tcp.GetStream();
if (s.DataAvailable)
{
string data = c.streamReader.ReadLine();
if (data != null)
{
if (ValidateJSON(data))
{
OnIncomingData(c, data);
}
}
}
//continue;
Here is how i check if the client is still connected:
private bool IsConnected(TcpClient c)
{
try
{
if (c != null && c.Client != null && c.Client.Connected)
{
if (c.Client.Poll(0, SelectMode.SelectRead))
{
return !(c.Client.Receive(new byte[1], SocketFlags.Peek) == 0);
}
return true;
}
else
{
return false;
}
}
catch
{
return false;
}
}
Here is how i send data into the stream:
string JSonData = JsonConvert.SerializeObject(SendData);
c.streamWriter.WriteLine(JSonData);
c.streamWriter.Flush();
Here is my client how it listens for message:
public void ConnectToWorldServer()
{
if (socketReady)
{
return;
}
//Default host and port values;
string host = "127.0.0.1";
int port = 8080;
//ClientLoginServer ClientLoginServer = new ClientLoginServer();
try
{
socket = new TcpClient(host, port);
stream = socket.GetStream();
socket.NoDelay = true;
writer = new StreamWriter(stream);
reader = new StreamReader(stream);
socketReady = true;
//Preserve the connection to worldserver thrue scenes
UnityThread.executeInUpdate(() =>
{
DontDestroyOnLoad(worldserverConnection);
});
// Start listening for connections.
while (true)
{
keepReading = true;
while (keepReading)
{
keepReading = true;
if (socketReady)
{
if (stream.DataAvailable)
{
string data = reader.ReadLine();
if (data != null)
{
UnityThread.executeInUpdate(() =>
{
OnIncomingData(data);
});
}
}
}
System.Threading.Thread.Sleep(1);
}
System.Threading.Thread.Sleep(1);
}
}
catch (Exception e)
{
Debug.Log("Socket error : " + e.Message);
}
}
I have red about SSL Stream. Here is what i have found: https://msdn.microsoft.com/en-us/library/system.net.security.sslstream(v=vs.110).aspx
I have several questions:
Do i need to buy an SSL certificate like a certificate for a website ?
Can i generate a self-signed SSL certificate? If so and it's needed can you show me any reference of how to achieve that ?
Is it possible to attach this SSL Stream to my StreamReader/StreamWriter ? Is it even needed or i have to remake the whole connection/communication thing?
When SSLStream is created the whole communication between the server and the client will be encrypted so anybody can't stay in between and listen/understand what they are communicating ?
If i have to attach a certificate file to the client so it may establish a successful SSL connection to the server and then send that client to all the people which have to use that client, doesn't that compromise the security of the SSL connection as i am sending them the certificate as well ?
This is all i wanted to ask. The best thing you can do to help me with your answer is if possible please put the code changes i have to apply in order to make that StreamReader/StreamWriter work over encrypted SSL.
you need a certificate; makecert would work, but it won't be trusted by default, so you'd either need to supply a custom RemoteCertificateValidationCallback to the consumer, or use a trusted cert; Let's Encrypt might be useful there
makecert or New-SelfSignedCertificate
the SslStream would need to go between the NetworkStream and the StreamReader; frankly, it isn't clear to make that StreamReader give you much here - you could do everything here with just the Streaam API; so instead of WriteLine, you'd encode manually and then Write the encoded buffer; but - there's no reason that you can't just whack SslStream in the middle of the two
yes
only the server needs the certificate (and in particular, only the server needs the private key of the certificate); there is something related called "client certificates" used for authenticating the client, but that is a related but different topic
As an example ripped from SE.Redis and mangled beyond legibility; SE.Redis optionally encrypts, and is basically:
Stream stream = // connect (get a NetworkStream)
if (config.Ssl) {
var ssl = new SslStream(stream, ... cert check callbacks, etc ...);
ssl.AuthenticateAsClient(expectedHost, allowedSslProtocols);
stream = ssl;
}
so it you had something like that, you'd just make sure you attached any StreamReader/StreamWriter after you've wrapped the NetworkStream in SslStream. But again: I really think you could remove StreamReader/StreamWriter here without much inconvenience.
Related
I'm attempting to write a .Net Framework 4.7.2 client that can communicate with a proto buffer payload eventually, over SSL. I think the best approach would be a TAP model, but I've had less luck finding a working example to repeatedly do the send (if data to send) and process server messages as and when the server replies, and therefore I've gone with the APM approach here below.
My problem is I get a "The BeginWrite method cannot be called when another write is pending" error in SendWorker(). My server does receive the connection and the message. I can work on the SSL cert later.
My output is:
Client connected.
Client is selecting a local certificate.
To quit, press the enter key.
Client is selecting a local certificate.
Validating the server certificate.
Certificate error: RemoteCertificateNameMismatch
Writing data to the server.
Writing data to the server.
The test code is:
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading;
namespace SslClient
{
// This is a client side of a
// client-server application that communicates using the
// SslStream and TcpClient classes.
// After connecting to the server and authenticating,
// the client should repeatedly send items from a q and listen for any replies
public class SslTcpClient
{
// complete is used to terminate the application when all
// asynchronous calls have completed or any call has thrown an exception.
// complete might be used by any of the callback methods.
private bool complete = false;
// e stores any exception thrown by an asynchronous method so that
// the mail application thread can display the exception and terminate gracefully.
// e might be used by any of the callback methods.
private Exception e = null;
// readData and buffer holds the data read from the server.
// They is used by the ReadCallback method.
private StringBuilder readData = new StringBuilder();
private byte[] buffer = new byte[2048];
EventWaitHandle _trigger = new AutoResetEvent(false);
private TcpClient _tcpClient;
private SslStream _sslStream;
private Thread _senderThread;
private readonly object _locker = new object();
public ConcurrentQueue<byte[]> WriteQueue = new ConcurrentQueue<byte[]>();
// The following method is invoked by the CertificateValidationDelegate.
public bool ValidateServerCertificate(
object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
Console.WriteLine("Validating the server certificate.");
if (sslPolicyErrors == SslPolicyErrors.None)
return true;
Console.WriteLine("Certificate error: {0}", sslPolicyErrors);
// Do not allow this client to communicate with unauthenticated servers.
return true; // ToDo
}
public X509Certificate SelectLocalCertificate(
object sender,
string targetHost,
X509CertificateCollection localCertificates,
X509Certificate remoteCertificate,
string[] acceptableIssuers)
{
Console.WriteLine("Client is selecting a local certificate.");
if (acceptableIssuers != null &&
acceptableIssuers.Length > 0 &&
localCertificates != null &&
localCertificates.Count > 0)
{
// Use the first certificate that is from an acceptable issuer.
foreach (X509Certificate certificate in localCertificates)
{
string issuer = certificate.Issuer;
if (Array.IndexOf(acceptableIssuers, issuer) != -1)
return certificate;
}
}
if (localCertificates != null &&
localCertificates.Count > 0)
return localCertificates[0];
return null;
}
private void AuthenticateCallback(IAsyncResult ar)
{
SslStream stream = (SslStream)ar.AsyncState;
try
{
stream.EndAuthenticateAsClient(ar);
}
catch (Exception authenticationException)
{
e = authenticationException;
//complete = true;
return;
}
}
public void ProduceConsumerQueue(TcpClient tcpClient, SslStream sslStream)
{
_tcpClient = tcpClient;
_sslStream = sslStream;
_senderThread = new Thread(SenderWorker);
_senderThread.Start();
}
public void Send(byte[] payload)
{
WriteQueue.Enqueue(payload);
_trigger.Set();
}
//our workhorse. This will just sit and wait until there is something to send
//it will continue to send until there is nothing left
void SenderWorker()
{
while (!complete)
{
string msg = null;
if (WriteQueue.Count > 0)
{
byte[] message;
WriteQueue.TryDequeue(out message);
if (_tcpClient.Connected && message.Length > 0)
{
if (_sslStream.CanWrite)
{
_sslStream.BeginWrite(message, 0, message.Length,
new AsyncCallback(WriteCallback),
_sslStream);
}
}
}
else
{
_trigger.WaitOne(); // Zero msgs left.. just wait
}
}
}
private void WriteCallback(IAsyncResult ar)
{
SslStream stream = (SslStream)ar.AsyncState;
try
{
Console.WriteLine("Writing data to the server.");
stream.EndWrite(ar);
// Asynchronously read a message from the server.
//stream.BeginRead(buffer, 0, buffer.Length,
// new AsyncCallback(ReadCallback),
// stream);
}
catch (Exception writeException)
{
e = writeException;
complete = true;
return;
}
}
private void ReadCallback(IAsyncResult ar)
{
// Read the message sent by the server.
// The end of the message is signaled using the
// "<EOF>" marker.
SslStream stream = (SslStream)ar.AsyncState;
int byteCount = -1;
try
{
Console.WriteLine("Reading data from the server.");
byteCount = stream.EndRead(ar);
// Use Decoder class to convert from bytes to UTF8
// in case a character spans two buffers.
Decoder decoder = Encoding.UTF8.GetDecoder();
char[] chars = new char[decoder.GetCharCount(buffer, 0, byteCount)];
decoder.GetChars(buffer, 0, byteCount, chars, 0);
readData.Append(chars);
// Check for EOF or an empty message.
if (byteCount != 0)
{
// We are not finished reading.
// Asynchronously read more message data from the server.
stream.BeginRead(buffer, 0, buffer.Length,
new AsyncCallback(ReadCallback),
stream);
}
else
{
Console.WriteLine("Message from the server: {0}", readData.ToString());
}
if (WriteQueue.Count>0)
{
//byte[] message;
//WriteQueue.TryDequeue(out message);
//if (message.Length > 0)
//{
// stream.BeginWrite(message, 0, message.Length,
// new AsyncCallback(WriteCallback),
// ar);
//}
}
}
catch (Exception readException)
{
e = readException;
complete = true;
return;
}
complete = true;
}
public int Main(string serverName, int port)
{
// Create a TCP/IP client socket.
TcpClient client = new TcpClient(serverName, port);
Console.WriteLine("Client connected.");
// Create an SSL stream that will close the client's stream.
SslStream sslStream = new SslStream(
client.GetStream(),
false,
new RemoteCertificateValidationCallback(ValidateServerCertificate),
new LocalCertificateSelectionCallback(SelectLocalCertificate)
);
var clientCertificate = FindClientCertificate(X509FindType.FindBySubjectName, "IIS Express Development Certificate"); // IIS Express Development Certificate
var clientCertificates = new X509CertificateCollection(new X509Certificate[] { clientCertificate });
// Begin authentication.
// The server name must match the name on the server certificate.
sslStream.BeginAuthenticateAsClient(
"UIClient",
clientCertificates,
SslProtocols.Tls12,
false,/* cert revocation */
new AsyncCallback(AuthenticateCallback),
sslStream);
// sslStream.AuthenticateAsClient("UIClient", clientCertificates, SslProtocols.Tls12, false);
ProduceConsumerQueue(client, sslStream);
// User can press a key to exit application, or let the
// asynchronous calls continue until they complete.
Console.WriteLine("To quit, press the enter key.");
do
{
// Real world applications would do work here
// while waiting for the asynchronous calls to complete.
Send(System.Text.Encoding.UTF8.GetBytes("hello".ToArray()));
System.Threading.Thread.Sleep(100);
} while (complete != true && Console.KeyAvailable == false);
if (Console.KeyAvailable)
{
Console.ReadLine();
Console.WriteLine("Quitting.");
client.Close();
sslStream.Close();
return 1;
}
if (e != null)
{
Console.WriteLine("An exception was thrown: {0}", e.ToString());
}
sslStream.Close();
client.Close();
Console.WriteLine("Good bye.");
return 0;
}
private static X509Certificate FindClientCertificate(X509FindType type, string item)
{
return Find(type, item, StoreLocation.CurrentUser) ?? Find(type, item, StoreLocation.LocalMachine);
}
private static X509Certificate Find(X509FindType t, string item, StoreLocation location)
{
using (var store = new X509Store(location))
{
store.Open(OpenFlags.OpenExistingOnly);
var certs = store.Certificates.Find(t, item, true);
if (t == X509FindType.FindBySubjectName)
{
return store.Certificates.OfType<X509Certificate2>().FirstOrDefault(x => x.FriendlyName == item);
}
return certs.OfType<X509Certificate>().FirstOrDefault();
}
}
}
}
How can it be fixed? Is there a better complete solution that you can share?
Thanks in advance,
Steve
I'm implementing an application in .Net. I have to create a connection by SSH which is works, but the HL7 data receiving fails. The destination is a raspberry pi. So when I'm debugging the ssh client is connected, the port is forwarded, the tcp client also connected, but there is no answer for my queries. Plese suggest me some examples!
In this project I have already implemented it on Android - it works fine.
So in .Net I tried the NHapiTools library and I also tried the direct TcpClient way too. localPort = remotePort. I used localIP = "localhost"
static void Main(string[] args)
{
try
{
PrivateKeyFile file = new PrivateKeyFile(#"./key/private.key");
using (var client = new SshClient(remoteIP, sshPort, username, file))
{
client.Connect();
var ci = client.ConnectionInfo;
var port = new ForwardedPortLocal(localIP, localPort, client.ConnectionInfo.Host, remotePort);
client.AddForwardedPort(port);
port.Start();
var req = "MSH|^~\\&|TestAppName||AVR||20181107201939.357+0000||QRY^R02^QRY_R02|923456|P|2.5";
////TCP
var tcpClient = new TcpClient();
tcpClient.Connect(localIP, (int)localPort);
Byte[] data = System.Text.Encoding.ASCII.GetBytes(req);
using (var stream = tcpClient.GetStream())
{
stream.Write(data, 0, data.Length);
using (var buffer = new MemoryStream())
{
byte[] chunk = new byte[4096];
int bytesRead;
while ((bytesRead = stream.Read(chunk, 0, chunk.Length)) > 0)
{
buffer.Write(chunk, 0, bytesRead);
}
data = buffer.ToArray();
}
}
//I used this also with same result -> no respond
//SimpleMLLP
/*
var connection = new SimpleMLLPClient(localIP, localPort,
Encoding.UTF8);
var response = connection.SendHL7Message(req);
*/
}
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.ReadLine();
}
}
So I experinced that the buffer size is 0 in TCP (due to time out). In the SimpleMLLP test SendHK7Message method never returns
You are not implementing MLLP (also called LLP) protocol while sending message.
Description HEX ASCII Symbol
Message starting character 0B 11 <VT>
Message ending characters 1C,0D 28,13 <FS>,<CR>
This way, when you send a message to Listener (TCP/MLLP server), it looks for Start Block in your incoming data. It never finds it. It just discards your entire message considering garbage. Hence, you get nothing back from Listener.
With MLLP implemented, your message (the stuff you are writing on socket) should look something like below:
<VT>MSH|^~\\&|TestAppName||AVR||20181107201939.357+0000||QRY^R02^QRY_R02|923456|P|2.5<FS><CR>
Note the <VT>, <CR> and <FS> are place holders in above message.
You may refer to this article for detailed information (Read step 4 and onward):
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
namespace SimpleMllpHl7ClientAdvanced
{
public class Program
{
private static char END_OF_BLOCK = '\u001c';
private static char START_OF_BLOCK = '\u000b';
private static char CARRIAGE_RETURN = (char)13;
static void Main(string[] args)
{
TcpClient ourTcpClient = null;
NetworkStream networkStream = null;
var testHl7MessageToTransmit = new StringBuilder();
//a HL7 test message that is enveloped with MLLP as described in my article
testHl7MessageToTransmit.Append(START_OF_BLOCK)
.Append("MSH|^~\\&|AcmeHIS|StJohn|CATH|StJohn|20061019172719||ORM^O01|MSGID12349876|P|2.3")
.Append(CARRIAGE_RETURN)
.Append("PID|||20301||Durden^Tyler^^^Mr.||19700312|M|||88 Punchward Dr.^^Los Angeles^CA^11221^USA|||||||")
.Append(CARRIAGE_RETURN)
.Append("PV1||O|OP^^||||4652^Paulson^Robert|||OP|||||||||9|||||||||||||||||||||||||20061019172717|20061019172718")
.Append(CARRIAGE_RETURN)
.Append("ORC|NW|20061019172719")
.Append(CARRIAGE_RETURN)
.Append("OBR|1|20061019172719||76770^Ultrasound: retroperitoneal^C4|||12349876")
.Append(CARRIAGE_RETURN)
.Append(END_OF_BLOCK)
.Append(CARRIAGE_RETURN);
try
{
//initiate a TCP client connection to local loopback address at port 1080
ourTcpClient = new TcpClient();
ourTcpClient.Connect(new IPEndPoint(IPAddress.Loopback, 1080));
Console.WriteLine("Connected to server....");
//get the IO stream on this connection to write to
networkStream = ourTcpClient.GetStream();
//use UTF-8 and either 8-bit encoding due to MLLP-related recommendations
var sendMessageByteBuffer = Encoding.UTF8.GetBytes(testHl7MessageToTransmit.ToString());
if (networkStream.CanWrite)
{
//send a message through this connection using the IO stream
networkStream.Write(sendMessageByteBuffer, 0, sendMessageByteBuffer.Length);
Console.WriteLine("Data was sent data to server successfully....");
var receiveMessageByteBuffer = Encoding.UTF8.GetBytes(testHl7MessageToTransmit.ToString());
var bytesReceivedFromServer = networkStream.Read(receiveMessageByteBuffer, 0, receiveMessageByteBuffer.Length);
// Our server for this example has been designed to echo back the message
// keep reading from this stream until the message is echoed back
while (bytesReceivedFromServer > 0)
{
if (networkStream.CanRead)
{
bytesReceivedFromServer = networkStream.Read(receiveMessageByteBuffer, 0, receiveMessageByteBuffer.Length);
if (bytesReceivedFromServer == 0)
{
break;
}
}
}
var receivedMessage = Encoding.UTF8.GetString(receiveMessageByteBuffer);
Console.WriteLine("Received message from server: {0}", receivedMessage);
}
Console.WriteLine("Press any key to exit...");
Console.ReadLine();
}
catch (Exception ex)
{
//display any exceptions that occur to console
Console.WriteLine(ex.Message);
}
finally
{
//close the IO strem and the TCP connection
networkStream?.Close();
ourTcpClient?.Close();
}
}
}
}
You should modify your following line of code as below:
var req = START_OF_BLOCK + "MSH|^~\\&|TestAppName||AVR||20181107201939.357+0000||QRY^R02^QRY_R02|923456|P|2.5" + END_OF_BLOCK + CARRIAGE_RETURN;
For more open source code, you may refer to this github project.
After days of struggling I have solved the problem. The main error was with the port forwarding.
I would reccomend to use SSH.Net by Renci (There was algorithm error with Tamir ssh).
After ssh connection created I used this to port forward:
var port = new ForwardedPortLocal(localIP, localPort, "localhost", remotePort);
Check your localIP with ipconfig /all in cmd. Or use 127.0.0.1 as a loopback IP.
SimpleMLLPClient did not worked for me so I used the direct tcp client query way. Like this:
TcpClient ourTcpClient = new TcpClient();
ourTcpClient.Connect(localIP, (int)localPort);
NetworkStream networkStream = ourTcpClient.GetStream();
var sendMessageByteBuffer = Encoding.UTF8.GetBytes(testHl7MessageToTransmit.ToString());
if (networkStream.CanWrite)
{
networkStream.Write(sendMessageByteBuffer, 0, sendMessageByteBuffer.Length);
Console.WriteLine("Data was sent to server successfully....");
byte[] receiveMessageByteBuffer = new byte[ourTcpClient.ReceiveBufferSize];
var bytesReceivedFromServer = networkStream.Read(receiveMessageByteBuffer, 0, receiveMessageByteBuffer.Length);
if (bytesReceivedFromServer > 0 && networkStream.CanRead)
{
receivedMessage.Append(Encoding.UTF8.GetString(receiveMessageByteBuffer));
}
var message = receivedMessage.Replace("\0", string.Empty);
Console.WriteLine("Received message from server: {0}", message);
}
So it gave me instant answer with 0 bytes (not due timeout). And here comes Amit Joshi help. I used a query what he suggested with START_OF_BLOCK, CARRIAGE_RETURN and END_OF_BLOCK and finally started to work. Thank you Amit Joshi!
Additional info:
In Android (java/Kotlin) jsch session setPortForwardingL works fine with three params:
val session = jsch.getSession("user", sshIP, sshPort)
session.setPassword("")
jsch.addIdentity(privatekey.getAbsolutePath())
// Avoid asking for key confirmation
val prop = Properties()
prop.setProperty("StrictHostKeyChecking", "no")
session.setConfig(prop)
session.connect(5000)
session.setPortForwardingL(localForwardPort, "localhost", remotePort)
val useTls = false
val context = DefaultHapiContext()
connection = context.newClient("localhost", localForwardPort, useTls)
I have written this code to check for a particular string from a file. Right now it checks for the string. But how can I send the reply back saying "it is present" to the client? The server side program should have all the codes. It also accepts multiple clients.
The Procedure of this program is as follows
Basically if a client wants to check if there's a particular string(word) in a file, he connects this code through a port on telnet. He types in the strings he wants to search(on telnet) and send it to the server side. And this server side program checks it for him from the file. And if it is present, it sends a message back to the client saying "The string is present in the file" And if it isn't, It should send a message saying "It is not".
The search string("hello") is in this program. How can I enable the client to search for it from client side(telnet)?
This is where I've come till with a lot of help and tutorials. Can someone please help me?
EDITED - I have changed the code such that it sends a reply back to the client. All I need to know now is, how can I enable the client to search (type the word he wants to search for) through the client side(telnet)? Any help will be really appreciated.
I have updated my code too.
Thank you.
class Program
{
static void Main(string[] args)
{
IPAddress ipad = IPAddress.Parse("127.0.0.1");
TcpListener serversocket = new TcpListener(ipad, 8888);
TcpClient clientsocket = default(TcpClient);
Byte[] bytes = new Byte[256];
serversocket.Start();
Console.WriteLine(">> Server Started");
while(true)
{
clientsocket = serversocket.AcceptTcpClient();
Console.WriteLine("Accepted Connection From Client");
LineMatcher lm = new LineMatcher(clientsocket);
Thread thread = new Thread(new ThreadStart(lm.Run));
thread.Start();
Console.WriteLine("Client connected");
}
Console.WriteLine(" >> exit");
Console.ReadLine();
clientsocket.Close();
serversocket.Stop();
}
}
public class LineMatcher
{
public string fileName = "c:/myfile2.txt";
private TcpClient _client;
public LineMatcher(TcpClient client)
{
_client = client;
}
public void Run()
{
byte[] data = new byte[256];
NetworkStream strm = _client.GetStream();
try
{
using (var r = new StreamReader("c:/myfile2.txt"))
{
string line = "";
bool done = false;
int lineNumber = 0;
String s = r.ReadToEnd();
ASCIIEncoding encoder = new ASCIIEncoding();
while (String.IsNullOrEmpty(s))
{
data = encoder.GetBytes("There is no data in the file.");
Console.WriteLine("There is no data in the file.");
}
if (s.IndexOf("hello", StringComparison.CurrentCultureIgnoreCase) >= 0)
{
data = encoder.GetBytes("It is Present.");
}
else
{
data = encoder.GetBytes("It is not Present");
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.ToString());
}
strm.Write(data, 0, data.Length);
strm.Flush();
Console.WriteLine("Closing client");
_client.Close();
}
}
Instead of if (s==null), you should check if the string contains the word. Being very creative, we can check for the word "word" like so: if (s.IndexOf("word") >= 0) which searches for the location of "word" within s and returns the index. In C#, indexes always start at 0. If the string "word" is not contained within your file string, it will return -1. Therefore that if statement will return true if the word is contained, or false if it is not.
Think of if as a statement which takes only one parameter. And that parameter is either true or false. The (s==null) is an expression which returns the value true or false which is then used by the if statement.
However, this will not work, if for instance, the file reads: THIS IS A WORD, because "word" does not equal "WORD". You can get around this by using a case insensitive compare like so:
if(s.IndexOf("word", StringComparison.CurrentCultureIgnoreCase) >= 0) {
// contains "word"
} else {
// does not contain "word"
}
Have a look at the following for reference
http://msdn.microsoft.com/en-us/library/ms228362(v=vs.80).aspx
http://msdn.microsoft.com/en-us/library/system.string.aspx
Your client applications will only be able to search once. This is because after you perform the search, you close the connection.
Console.WriteLine("Closing client");
_client.Close();
If you want the connection to stay open you will need to include a loop to ensure you return to the beginning of the LineMatcher class to re-search.
Rather than checking the IndexOf this string, I'd instead simply use the Contains method. While IndexOf is designed to find where a substring is located within a string, Contains is built for the specific purpose of simply checking whether or not a substring exists. Note that this is not case insensitive.
else if (s.Contains("HTTP"))
{
I would strongly recommend you get the searching application working first, as a stand-alone application, and then write a telnet server which launches your original application. These are two separate functions, and you'll find it a lot easier to test them individually.
I solved it. :) This is how I did it. Any suggestions on improving it?
namespace ServerSideApplication
{
class Program
{
static void Main(string[] args)
{
TcpListener socketListener = new TcpListener(8888);
TcpClient netClient = default(TcpClient);
StreamReader sr;
StringBuilder sb = new StringBuilder();
socketListener.Start();
sr = new StreamReader("c:\\test.txt");
sb.Append(sr.ReadToEnd());
while (true)
{
netClient = socketListener.AcceptTcpClient();
Console.WriteLine("Accepted Connection From Client" + Environment.NewLine + "Client connected");
ServerSide ss = new ServerSide();
ss.startServerSide(netClient, sb);
}
socketListener.Stop();
}
}
class ServerSide
{
TcpClient netClient;
StringBuilder sb;
public void startServerSide(TcpClient netClient, StringBuilder sb)
{
this.netClient = netClient;
this.sb = sb;
Thread thread = new Thread(processRequest);
thread.Start();
}
private void processRequest()
{
byte[] data = new byte[4096];
int bytesRead;
NetworkStream strm = netClient.GetStream();
bytesRead = 0;
try
{
NetworkStream ns = netClient.GetStream();
string clientChar = "", s = "";
do
{
bytesRead = ns.Read(data, 0, (int)data.Length);
clientChar = Encoding.ASCII.GetString(data).Replace("\0", "");
s += clientChar;
} while (clientChar != Environment.NewLine);
s = s.Trim();
ASCIIEncoding encoder = new ASCIIEncoding();
if (String.IsNullOrEmpty(s))
{
data = encoder.GetBytes("There is no data in the file.");
Console.WriteLine("There is no data in the file.");
}
if (sb.ToString().Contains(s))
{
data = encoder.GetBytes("It Is Present In The File.");
}
else
{
data = encoder.GetBytes("It Is Not Present In The File.");
}
strm.Write(data, 0, data.Length);
strm.Flush();
Console.WriteLine("Closing client");
netClient.Close();
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.ToString());
}
}
}
}
I need to develop a service that will connect to a TCP server. Main tasks are reading incoming messages and also sending commands to the server in ten minutes, like a synchronize command. For example, I used the TcpClient object as shown below:
...
TcpClient tcpClient = new TcpClient();
tcpClient.Connect("x.x.x.x", 9999);
networkStream = tcpClient.GetStream();
clientStreamReader = new StreamReader(networkStream);
clientStreamWriter = new StreamWriter(networkStream);
while(true)
{
clientStreamReader.Read()
}
Also, when I need to write out something in any method, I use:
clientStreamWriter.write("xxx");
Is this usage correct? Or is there a better way?
First, I recommend that you use WCF, .NET Remoting, or some other higher-level communication abstraction. The learning curve for "simple" sockets is nearly as high as WCF, because there are so many non-obvious pitfalls when using TCP/IP directly.
If you decide to continue down the TCP/IP path, then review my .NET TCP/IP FAQ, particularly the sections on message framing and application protocol specifications.
Also, use asynchronous socket APIs. The synchronous APIs do not scale and in some error situations may cause deadlocks. The synchronous APIs make for pretty little example code, but real-world production-quality code uses the asynchronous APIs.
Be warned - this is a very old and cumbersome "solution".
By the way, you can use serialization technology to send strings, numbers or any objects which are support serialization (most of .NET data-storing classes & structs are [Serializable]).
There, you should at first send Int32-length in four bytes to the stream and then send binary-serialized (System.Runtime.Serialization.Formatters.Binary.BinaryFormatter) data into it.
On the other side or the connection (on both sides actually) you definetly should have a byte[] buffer which u will append and trim-left at runtime when data is coming.
Something like that I am using:
namespace System.Net.Sockets
{
public class TcpConnection : IDisposable
{
public event EvHandler<TcpConnection, DataArrivedEventArgs> DataArrive = delegate { };
public event EvHandler<TcpConnection> Drop = delegate { };
private const int IntSize = 4;
private const int BufferSize = 8 * 1024;
private static readonly SynchronizationContext _syncContext = SynchronizationContext.Current;
private readonly TcpClient _tcpClient;
private readonly object _droppedRoot = new object();
private bool _dropped;
private byte[] _incomingData = new byte[0];
private Nullable<int> _objectDataLength;
public TcpClient TcpClient { get { return _tcpClient; } }
public bool Dropped { get { return _dropped; } }
private void DropConnection()
{
lock (_droppedRoot)
{
if (Dropped)
return;
_dropped = true;
}
_tcpClient.Close();
_syncContext.Post(delegate { Drop(this); }, null);
}
public void SendData(PCmds pCmd) { SendDataInternal(new object[] { pCmd }); }
public void SendData(PCmds pCmd, object[] datas)
{
datas.ThrowIfNull();
SendDataInternal(new object[] { pCmd }.Append(datas));
}
private void SendDataInternal(object data)
{
if (Dropped)
return;
byte[] bytedata;
using (MemoryStream ms = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
try { bf.Serialize(ms, data); }
catch { return; }
bytedata = ms.ToArray();
}
try
{
lock (_tcpClient)
{
TcpClient.Client.BeginSend(BitConverter.GetBytes(bytedata.Length), 0, IntSize, SocketFlags.None, EndSend, null);
TcpClient.Client.BeginSend(bytedata, 0, bytedata.Length, SocketFlags.None, EndSend, null);
}
}
catch { DropConnection(); }
}
private void EndSend(IAsyncResult ar)
{
try { TcpClient.Client.EndSend(ar); }
catch { }
}
public TcpConnection(TcpClient tcpClient)
{
_tcpClient = tcpClient;
StartReceive();
}
private void StartReceive()
{
byte[] buffer = new byte[BufferSize];
try
{
_tcpClient.Client.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, DataReceived, buffer);
}
catch { DropConnection(); }
}
private void DataReceived(IAsyncResult ar)
{
if (Dropped)
return;
int dataRead;
try { dataRead = TcpClient.Client.EndReceive(ar); }
catch
{
DropConnection();
return;
}
if (dataRead == 0)
{
DropConnection();
return;
}
byte[] byteData = ar.AsyncState as byte[];
_incomingData = _incomingData.Append(byteData.Take(dataRead).ToArray());
bool exitWhile = false;
while (exitWhile)
{
exitWhile = true;
if (_objectDataLength.HasValue)
{
if (_incomingData.Length >= _objectDataLength.Value)
{
object data;
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream(_incomingData, 0, _objectDataLength.Value))
try { data = bf.Deserialize(ms); }
catch
{
SendData(PCmds.Disconnect);
DropConnection();
return;
}
_syncContext.Post(delegate(object T)
{
try { DataArrive(this, new DataArrivedEventArgs(T)); }
catch { DropConnection(); }
}, data);
_incomingData = _incomingData.TrimLeft(_objectDataLength.Value);
_objectDataLength = null;
exitWhile = false;
}
}
else
if (_incomingData.Length >= IntSize)
{
_objectDataLength = BitConverter.ToInt32(_incomingData.TakeLeft(IntSize), 0);
_incomingData = _incomingData.TrimLeft(IntSize);
exitWhile = false;
}
}
StartReceive();
}
public void Dispose() { DropConnection(); }
}
}
That is just an example, you should edit it for your use.
I have had luck using the socket object directly (rather than the TCP client). I create a Server object that looks something like this (I've edited some stuff such as exception handling out for brevity, but I hope that the idea comes across.)...
public class Server()
{
private Socket sock;
// You'll probably want to initialize the port and address in the
// constructor, or via accessors, but to start your server listening
// on port 8080 and on any IP address available on the machine...
private int port = 8080;
private IPAddress addr = IPAddress.Any;
// This is the method that starts the server listening.
public void Start()
{
// Create the new socket on which we'll be listening.
this.sock = new Socket(
addr.AddressFamily,
SocketType.Stream,
ProtocolType.Tcp);
// Bind the socket to the address and port.
sock.Bind(new IPEndPoint(this.addr, this.port));
// Start listening.
this.sock.Listen(this.backlog);
// Set up the callback to be notified when somebody requests
// a new connection.
this.sock.BeginAccept(this.OnConnectRequest, sock);
}
// This is the method that is called when the socket recives a request
// for a new connection.
private void OnConnectRequest(IAsyncResult result)
{
// Get the socket (which should be this listener's socket) from
// the argument.
Socket sock = (Socket)result.AsyncState;
// Create a new client connection, using the primary socket to
// spawn a new socket.
Connection newConn = new Connection(sock.EndAccept(result));
// Tell the listener socket to start listening again.
sock.BeginAccept(this.OnConnectRequest, sock);
}
}
Then, I use a separate Connection class to manage the individual connection with the remote host. That looks something like this...
public class Connection()
{
private Socket sock;
// Pick whatever encoding works best for you. Just make sure the remote
// host is using the same encoding.
private Encoding encoding = Encoding.UTF8;
public Connection(Socket s)
{
this.sock = s;
// Start listening for incoming data. (If you want a multi-
// threaded service, you can start this method up in a separate
// thread.)
this.BeginReceive();
}
// Call this method to set this connection's socket up to receive data.
private void BeginReceive()
{
this.sock.BeginReceive(
this.dataRcvBuf, 0,
this.dataRcvBuf.Length,
SocketFlags.None,
new AsyncCallback(this.OnBytesReceived),
this);
}
// This is the method that is called whenever the socket receives
// incoming bytes.
protected void OnBytesReceived(IAsyncResult result)
{
// End the data receiving that the socket has done and get
// the number of bytes read.
int nBytesRec = this.sock.EndReceive(result);
// If no bytes were received, the connection is closed (at
// least as far as we're concerned).
if (nBytesRec <= 0)
{
this.sock.Close();
return;
}
// Convert the data we have to a string.
string strReceived = this.encoding.GetString(
this.dataRcvBuf, 0, nBytesRec);
// ...Now, do whatever works best with the string data.
// You could, for example, look at each character in the string
// one-at-a-time and check for characters like the "end of text"
// character ('\u0003') from a client indicating that they've finished
// sending the current message. It's totally up to you how you want
// the protocol to work.
// Whenever you decide the connection should be closed, call
// sock.Close() and don't call sock.BeginReceive() again. But as long
// as you want to keep processing incoming data...
// Set up again to get the next chunk of data.
this.sock.BeginReceive(
this.dataRcvBuf, 0,
this.dataRcvBuf.Length,
SocketFlags.None,
new AsyncCallback(this.OnBytesReceived),
this);
}
}
You can use your Connection object to send data by calling its Socket directly, like so...
this.sock.Send(this.encoding.GetBytes("Hello to you, remote host."));
As I said, I've tried to edit the code here for posting, so I apologize if there are any errors in it.
First of all, TCP does not guarantee that everything that you send will be received with the same read at the other end. It only guarantees that all bytes that you send will arrive and in the correct order.
Therefore, you will need to keep building up a buffer when reading from the stream. You will also have to know how large each message is.
The simplest ever is to use a non-typeable ASCII character to mark the end of the packet and look for it in the received data.
I've developed a dotnet library that might come in useful. I have fixed the problem of never getting all of the data if it exceeds the buffer, which many posts have discounted. Still some problems with the solution but works descently well https://github.com/Apollo013/DotNet-TCP-Communication
I have a simple message:
package test;
message sendName {
optional string username = 1;
}
The awesome VS plugin generates the .cs file:
namespace test {
[global::System.Serializable global::ProtoBuf.ProtoContract(Name=#"sendName")]
public partial class sendName : global::ProtoBuf.IExtensible {
public sendName() {}
private string _username = "";
[global::ProtoBuf.ProtoMember(1, IsRequired = false, Name=#"username", DataFormat = > global::ProtoBuf.DataFormat.Default)]
[global::System.ComponentModel.DefaultValue("")]
public string username
{
get { return _username; }
set { _username = value; }
}
private global::ProtoBuf.IExtension extensionObject;
global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
{
return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing);
}
}
}
I am sending a message from the java side by using
objName.build().writeTo((FileOutputStream)socket.getOutputStream());
In my C# application, which acts like the Socket Listener, I have a method called Listen which listens for the message sent by the java client:
public void Listen()
{
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
TcpListener listener = new TcpListener(ipAddress, 4055);
TcpClient client = null;
listener.Start();
while (true)
{
Debug.WriteLine("Waiting for a Connection");
client = listener.AcceptTcpClient();
Stream stream = client.GetStream();
// sendName sendNameObj = Serializer.Deserialize<sendName>(stream);
}
}
I am obviously missing some basics here.
What method should I use to get the sendName object?
When I debug my code in C#, the debugger quits when I call DeserializeWithLengthPrefix method.
This is my C# code:
private void Listen()
{
IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
TcpListener listener = new TcpListener(ipAddress,4055);
listener.Start();
listener.BeginAcceptSocket(ClientConnected, listener);
}
private void ClientConnected(IAsyncResult result)
{
TcpListener server = (TcpListener)result.AsyncState;
using (TcpClient client = server.EndAcceptTcpClient(result))
using (NetworkStream stream = client.GetStream())
{
try
{
//SendNameMessage sendNameObj = Serializer.Deserialize<SendNameMessage>(stream);
SendNameMessage sendNameObj = Serializer.DeserializeWithLengthPrefix<SendNameMessage>(stream, PrefixStyle.Fixed32);
string name = sendNameObj.sendName;
if (name != null && name.Length > 0)
{
nameTextBox.Text = name;
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
This is my java code:
SendNameMessage.Builder sendNameMessageObj = null;
sendNameMessageObj = SendNameMessage.newBuilder();
sendNameMessageObj.setSendName("Protobuf-net");
SocketRequest.INSTANCE.openSocket();
sendNameMessageObj.build().writTo(SocketRequest.INSTANCE.getWriter());
The Serializer.Deserialize<sendName>(stream); should do it, but I'm guessing that it is hanging forever? The reason here is that protobuf messages don't have an inbuilt terminator, so the default is that it reads until the end of the stream - which won't happen until you close the socket.
To get around this, a common trick is to prefix the message with the length, and limit yourself to that. There may be an inbuilt way to handle that in the Java implementation, or you can handle it manually. On the C# side, protobuf-net has DeserializeWithLengthPrefix, which accepts an enum to specify how you've encoded it. For simplicity I would suggest a basic 32-bit little-endian encoding of the length (which maps to PrefixStyle.Fixed32).
Re the edit / comment: the two must match. At the moment you are serializing without a length prefix, but deserializing expecting a length prefix. Does the Java API expose a mechanism to obtain the data-length before you write it? If not, perhaps write first to a temp buffer, see how much you wrote, then write the length and finally the data.
Or; if you are only sending a single message - just close the stream and use Deserialize (no length prefix). I have a C#-to-C# multi-message sockets example available, but I can't comment much on the java side...