For quite a lot of time I struggled with finding a working solution to connect an android device and an UWP application (on PC) with either an IP connection or bluetooth. The main issue is to find a set of code or example which is simple enough to get into but is guaranteed to work (so that my efforts are not futile, which was the case for more than a week now).
What is clear is that there is no possibility for a "code pair" (as of client-server), as the libraries used and the way the code structures are built must be vastly different. A further problem is that bluetooth does not seem to allow loopback connections, which causes even more issues with testing. Another issue is possibly outdated example projects. Also it's hard to find xamarin/c# solutions, and I didn't want to get into Android Studio and Java (my project is the UWP one, the android part is just for testing). These are simply too many layers of difficulties to get into for me.
The goal for now (and thus also my question asking for help) is a basic operation:
Send a plain message or datastream from Xamarin-Android (as client) to UWP (as server) and have a response for receiving it - via bluetooth.
Let's ignore device searching now (if possible), let's just use IP/MAC address directly. From there on everything should fall into place. All necessary capabilities/declarations are set and devices are paired.
I would be really grateful for any help.
I found out the solution myself, so here is how it went:
First of all, remember to define all necessary declarations and capabilities for bluetooth. This will explicitly focus on the code part.
For the Xamarin/Android client part. The website which was really helpful is this one. Also try out the quite well known chat sample for Xamarin. CreateMessage is a method to create debug messages on the local device which can be displayed. I kept it very simple, because my project is primarily about the UWP part. All of this was enclosed in a try { } catch { } clause, but I leave it out now due to having even more indentations and brackets.
using Java.Util;
using System.Text;
using System.IO;
using Android.Runtime;
using System.Threading.Tasks;
TestClass
{
// The UUIDs will be displayed down below if not known.
const string TARGET_UUID = "00001105-0000-1000-8000-00805f9b34fb";
BluetoothSocket socket = null;
OutputStreamInvoker outStream = null;
InputStreamInvoker inStream = null;
void Connect ()
{
BluetoothAdapter adapter = BluetoothAdapter.DefaultAdapter;
if (adapter == null) CreateMessage ("No Bluetooth adapter found.");
else if (!adapter.IsEnabled) CreateMessage ("Bluetooth adapter is not enabled.");
List<BluetoothDevice> L = new List<BluetoothDevice> ();
foreach (BluetoothDevice d in adapter.BondedDevices)
{
CreateMessage ("D: " + d.Name + " " + d.Address + " " + d.BondState.ToString ());
L.Add (d);
}
BluetoothDevice device = null;
device = L.Find (j => j.Name == "PC-NAME");
if (device == null) CreateMessage ("Named device not found.");
else
{
CreateMessage ("Device has been found: " + device.Name + " " + device.Address + " " + device.BondState.ToString ());
}
socket = device.CreateRfcommSocketToServiceRecord (UUID.FromString (TARGET_UUID));
await socket.ConnectAsync ();
if (socket != null && socket.IsConnected) CreateMessage ("Connection successful!");
else CreateMessage ("Connection failed!");
inStream = (InputStreamInvoker) socket.InputStream;
outStream = (OutputStreamInvoker) socket.OutputStream;
if (socket != null && socket.IsConnected)
{
Task t = new Task (() => Listen (inStream));
t.Start ();
}
else throw new Exception ("Socket not existing or not connected.");
}
}
Now we enter the part with the bytes and pain. I used this format to transmit messages: [4 bytes of uint for message length] [1 byte per character]. What is important is that you use the same byte to uint conversion, because the order of bytes or how it went in general had differences in the UWP specific methods. If your word length is not what it is supposed to be (instead of ~23 something like 3000000+), that is a problem. Reading bytes which do not exist (yet) can mean exceptions or even merciless crashes despite using try { } catch { } clauses.
The following method sends the message in the format mentioned above. As said, it is among the most simple ways to do this, so I won't mention how things can be done better.
private async void SendMessage (string message)
{
uint messageLength = (uint) message.Length;
byte[] countBuffer = BitConverter.GetBytes (messageLength);
byte[] buffer = Encoding.UTF8.GetBytes (message);
await outStream.WriteAsync (countBuffer, 0, countBuffer.Length);
await outStream.WriteAsync (buffer, 0, buffer.Length);
}
Usage: Run method 1, and then method 2. You can also do a SendMessage within method 1 at the end (when it is already connected).
Now to the part about listening for messages/responses. In the first method you will see this one was run via a Task, so that it does not block the method it is started it. Maybe there are Xamarin/Android specific ways to solve that, but it does not matter to me, so I simply circumvented that.
private async void Listen (Stream inStream)
{
bool Listening = true;
CreateMessage ("Listening has been started.");
byte[] uintBuffer = new byte[sizeof (uint)]; // This reads the first 4 bytes which form an uint that indicates the length of the string message.
byte[] textBuffer; // This will contain the string message.
// Keep listening to the InputStream while connected.
while (Listening)
{
try
{
// This one blocks until it gets 4 bytes.
await inStream.ReadAsync (uintBuffer, 0, uintBuffer.Length);
uint readLength = BitConverter.ToUInt32 (uintBuffer, 0);
textBuffer = new byte[readLength];
// Here we know for how many bytes we are looking for.
await inStream.ReadAsync (textBuffer, 0, (int) readLength);
string s = Encoding.UTF8.GetString (textBuffer);
CreateMessage ("Received: " + s);
}
catch (Java.IO.IOException e)
{
CreateMessage ("Error: " + e.Message);
Listening = false;
break;
}
}
CreateMessage ("Listening has ended.");
}
This was only half the work. For the UWP server part, I will simply post my current code, which is way more clean and requires no editing for this.
using System;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Bluetooth.Rfcomm;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
namespace BT
{
public sealed class BluetoothConnectionHandler
{
RfcommServiceProvider provider;
bool isAdvertising = false;
StreamSocket socket;
StreamSocketListener socketListener;
DataWriter writer;
DataReader reader;
Task listeningTask;
public bool Listening { get; private set; }
// I use Actions for transmitting the output and debug output. These are custom classes I created to pack them more conveniently and to be able to just "Trigger" them without checking anything. Replace this with regular Actions and use their invoke methods.
public ActionSingle<string> MessageOutput { get; private set; } = new ActionSingle<string> ();
public ActionSingle<string> LogOutput { get; private set; } = new ActionSingle<string> ();
// These were in the samples.
const uint SERVICE_VERSION_ATTRIBUTE_ID = 0x0300;
const byte SERVICE_VERSION_ATTRIBUTE_TYPE = 0x0a; // UINT32
const uint SERVICE_VERSION = 200;
const bool DO_RESPONSE = true;
public async void StartServer ()
{
// Initialize the provider for the hosted RFCOMM service.
provider = await RfcommServiceProvider.CreateAsync (RfcommServiceId.ObexObjectPush);
// Create a listener for this service and start listening.
socketListener = new StreamSocketListener ();
socketListener.ConnectionReceived += OnConnectionReceived;
await socketListener.BindServiceNameAsync (provider.ServiceId.AsString (), SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);
// Set the SDP attributes and start advertising.
InitializeServiceSdpAttributes (provider);
provider.StartAdvertising (socketListener);
isAdvertising = true;
}
public void Disconnect ()
{
Listening = false;
if (provider != null) { if (isAdvertising) provider.StopAdvertising (); provider = null; } // StopAdvertising relentlessly causes a crash if not advertising.
if (socketListener != null) { socketListener.Dispose (); socketListener = null; }
if (writer != null) { writer.DetachStream (); writer.Dispose (); writer = null; }
if (reader != null) { reader.DetachStream (); reader.Dispose (); reader = null; }
if (socket != null) { socket.Dispose (); socket = null; }
if (listeningTask != null) { listeningTask = null; }
}
public async void SendMessage (string message)
{
// There's no need to send a zero length message.
if (string.IsNullOrEmpty (message)) return;
// Make sure that the connection is still up and there is a message to send.
if (socket == null || writer == null) { LogOutput.Trigger ("Cannot send message: No clients connected."); return; } // "No clients connected, please wait for a client to connect before attempting to send a message."
uint messageLength = (uint) message.Length;
byte[] countBuffer = BitConverter.GetBytes (messageLength);
byte[] buffer = Encoding.UTF8.GetBytes (message);
LogOutput.Trigger ("Sending: " + message);
writer.WriteBytes (countBuffer);
writer.WriteBytes (buffer);
await writer.StoreAsync ();
}
private void InitializeServiceSdpAttributes (RfcommServiceProvider provider)
{
DataWriter w = new DataWriter ();
// First write the attribute type.
w.WriteByte (SERVICE_VERSION_ATTRIBUTE_TYPE);
// Then write the data.
w.WriteUInt32 (SERVICE_VERSION);
IBuffer data = w.DetachBuffer ();
provider.SdpRawAttributes.Add (SERVICE_VERSION_ATTRIBUTE_ID, data);
}
private void OnConnectionReceived (StreamSocketListener listener, StreamSocketListenerConnectionReceivedEventArgs args)
{
provider.StopAdvertising ();
isAdvertising = false;
provider = null;
listener.Dispose ();
socket = args.Socket;
writer = new DataWriter (socket.OutputStream);
reader = new DataReader (socket.InputStream);
writer.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
reader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8;
//StartListening ();
LogOutput.Trigger ("Connection established.");
listeningTask = new Task (() => StartListening ());
listeningTask.Start ();
// Notify connection received.
}
private async void StartListening ()
{
LogOutput.Trigger ("Starting to listen for input.");
Listening = true;
while (Listening)
{
try
{
// Based on the protocol we've defined, the first uint is the size of the message. [UInt (4)] + [Message (1*n)] - The UInt describes the length of the message.
uint readLength = await reader.LoadAsync (sizeof (uint));
// Check if the size of the data is expected (otherwise the remote has already terminated the connection).
if (!Listening) break;
if (readLength < sizeof (uint))
{
Listening = false;
Disconnect ();
LogOutput.Trigger ("The connection has been terminated.");
break;
}
uint messageLength = reader.RReadUint (); //
LogOutput.Trigger ("messageLength: " + messageLength.ToString ());
// Load the rest of the message since you already know the length of the data expected.
readLength = await reader.LoadAsync (messageLength);
// Check if the size of the data is expected (otherwise the remote has already terminated the connection).
if (!Listening) break;
if (readLength < messageLength)
{
Listening = false;
Disconnect ();
LogOutput.Trigger ("The connection has been terminated.");
break;
}
string message = reader.ReadString (messageLength);
MessageOutput.Trigger ("Received message: " + message);
if (DO_RESPONSE) SendMessage ("abcdefghij");
}
catch (Exception e)
{
// If this is an unknown status it means that the error is fatal and retry will likely fail.
if (SocketError.GetStatus (e.HResult) == SocketErrorStatus.Unknown)
{
Listening = false;
Disconnect ();
LogOutput.Trigger ("Fatal unknown error occurred.");
break;
}
}
}
LogOutput.Trigger ("Stopped to listen for input.");
}
}
}
Usage is the following:
Create an instance of BluetoothConnectionHandler.
Set up the MessageOutput and/or LogOutput (read the comment in the code regarding this).
Run its StartServer method.
To send a message, use its SendMessage method.
The extension method for the RReadUint:
public static uint RReadUint (this DataReader reader)
{
uint a = 0;
byte[] x = new byte[sizeof (uint)];
reader.ReadBytes (x);
a = BitConverter.ToUInt32 (x, 0);
return a;
}
This should contain everything needed to do what I asked for... in hintsight I see there was no simple answer possible. From here on everything can get improved, as it is meant as the possibly most basic way to have bluetooth communication between UWP and Xamarin/Android.
In case you have questions about this, feel free to ask in the comment sections.
Related
This is my code, can you help me to change the code so that I can recive multiple messages? At the moment I can just recive one question then the client must reconnect.
I hope you can help me.
public string PortNumber = "1337";
public MainPage()
{
this.InitializeComponent();
StartServer();
}
private async void StartServer()
{
try
{
var streamSocketListener = new Windows.Networking.Sockets.StreamSocketListener();
// The ConnectionReceived event is raised when connections are received.
streamSocketListener.ConnectionReceived += this.StreamSocketListener_ConnectionReceived;
// Start listening for incoming TCP connections on the specified port. You can specify any port that's not currently in use.
await streamSocketListener.BindServiceNameAsync(this.PortNumber);
this.serverListBox.Items.Add("server is listening...");
}
catch (Exception ex)
{
Windows.Networking.Sockets.SocketErrorStatus webErrorStatus = Windows.Networking.Sockets.SocketError.GetStatus(ex.GetBaseException().HResult);
this.serverListBox.Items.Add(webErrorStatus.ToString() != "Unknown" ? webErrorStatus.ToString() : ex.Message);
}
}
private async void StreamSocketListener_ConnectionReceived(Windows.Networking.Sockets.StreamSocketListener sender, Windows.Networking.Sockets.StreamSocketListenerConnectionReceivedEventArgs args)
{
string request;
using (var streamReader = new StreamReader(args.Socket.InputStream.AsStreamForRead()))
{
request = await streamReader.ReadLineAsync();
}
await this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => this.TB1.Text=(string.Format("server received the request: \"{0}\"", request)));
// Echo the request back as the response.
using (Stream outputStream = args.Socket.OutputStream.AsStreamForWrite())
{
using (var streamWriter = new StreamWriter(outputStream))
{
await streamWriter.WriteLineAsync(request);
await streamWriter.FlushAsync();
}
}
}
You should always listen the upcoming message in the StreamSocketListener.ConnectionReceived Event, this event will only trigger when a connection was received on the StreamSocketListener object. If you want to always receive the sent data, you should always read it in the StreamSocketListener.ConnectionReceived Event.
Here is the sample code in the official StreamSocket:
In the Scenario1_Start.xaml.cs, the OnConnection is same as your StreamSocketListener_ConnectionReceived method. You can also learn more from the official sample.
private async void OnConnection(
StreamSocketListener sender,
StreamSocketListenerConnectionReceivedEventArgs args)
{
DataReader reader = new DataReader(args.Socket.InputStream);
try
{
while (true)
{
// Read first 4 bytes (length of the subsequent string).
uint sizeFieldCount = await reader.LoadAsync(sizeof(uint));
if (sizeFieldCount != sizeof(uint))
{
// The underlying socket was closed before we were able to read the whole data.
return;
}
// Read the string.
uint stringLength = reader.ReadUInt32();
uint actualStringLength = await reader.LoadAsync(stringLength);
if (stringLength != actualStringLength)
{
// The underlying socket was closed before we were able to read the whole data.
return;
}
// Display the string on the screen. The event is invoked on a non-UI thread, so we need to marshal
// the text back to the UI thread.
NotifyUserFromAsyncThread(
String.Format("Received data: \"{0}\"", reader.ReadString(actualStringLength)),
NotifyType.StatusMessage);
}
}
catch (Exception exception)
{
// If this is an unknown status it means that the error is fatal and retry will likely fail.
if (SocketError.GetStatus(exception.HResult) == SocketErrorStatus.Unknown)
{
throw;
}
NotifyUserFromAsyncThread(
"Read stream failed with error: " + exception.Message,
NotifyType.ErrorMessage);
}
}
I am currently trying to write an app for android with Xamarin where I want to create and destroy sockets to the same device, and then redo that process over again. I have written both client and server code. I am having a problem doing that, since the app always crashes on the server side when it tries to read data from the client for a second time.
What I mean is that it is always successful the first time around, but the second time around, it always crashes. We figured out that the problem was on the client though cause once we started keeping the sockets open and reusing them instead of closing them and recreating a new one when needed, it worked as intended and did not crash. Here is the code we ended up using:
[SERVER]
public class BluetoothSocketListener {
private BluetoothScanner _scanner;
private BluetoothServerSocket serverSocket;
private string TAG = "Socket Listener: ";
private Thread listenThread;
public BluetoothSocketListener(BluetoothScanner scanner, UUID uuid) {
_scanner = scanner;
BluetoothServerSocket tmp = null;
try {
tmp = scanner.Adapter.ListenUsingInsecureRfcommWithServiceRecord("AGHApp", uuid);
} catch(Exception e) {
Console.WriteLine(TAG + "Listen failed, exception: " + e);
}
serverSocket = tmp;
listenThread = new Thread(new ThreadStart(StartListening));
listenThread.Start();
}
private void StartListening() {
Console.WriteLine(TAG + "Listening...");
BluetoothSocket socket = null;
while(_scanner.Running){
try {
socket = serverSocket.Accept();
}catch(Exception e) {
Console.WriteLine(TAG + "Accept failed: " + e);
break;
}
if (socket != null) {
lock (this) {
ReadData(socket.InputStream);
socket.Close();
}
}
}
serverSocket.Close();
}
private void ReadData(Stream stream) {
// Check to see if this NetworkStream is readable.
if(stream.CanRead){
byte[] streamData = new byte[1024];
StringBuilder completeMsg = new StringBuilder();
int bytesRead = 0;
// Incoming message may be larger or smaller than the buffer size.
do{
bytesRead = stream.Read(streamData, 0, 1);
completeMsg.AppendFormat("{0}", Encoding.ASCII.GetString(streamData, 0, bytesRead));
}
while(stream.IsDataAvailable());
// Print out the received message to the console.
Console.WriteLine("Message : " + completeMsg);
}
else{
Console.WriteLine("Cannot read from stream");
}
}
}
[CLIENT]
private void SendData(BluetoothDevice device, string msg){
Console.WriteLine(TAG + "Finding socket");
BluetoothSocket socket = null;
if(sockets.ContainsKey(device.Address)) {
socket = sockets[device.Address];
}
else {
socket = device.CreateInsecureRfcommSocketToServiceRecord(_uuid);
socket.Connect();
sockets.Add(socket);
}
Console.WriteLine(TAG + "Socket connected, writing to socket");
byte[] bMsg = Encoding.ASCII.GetBytes(msg);
socket.OutputStream.Write(bMsg, 0, bMsg.Length);
socket.OutputStream.Close();
}
As can be seen, I never actually close the sockets on the client side after I send the message. This is not the problem though since if this is necessary, I can easily do this in some other function.
What I would like is to create and close the socket every time I want to send a message, since I only want to send something every 15 minutes, and the device might have moved and is no longer available. It is also not necessary to keep track of the devices. Fire and forget. This is what we started with and would like to have something similar to this as well:
private void SendData(BluetoothDevice device, string msg){
BluetoothSocket socket = device.CreateInsecureRfcommSocketToServiceRecord(_uuid);
socket.Connect();
Console.WriteLine(TAG + "Socket connected, writing to socket");
byte[] bMsg = Encoding.ASCII.GetBytes(msg);
socket.OutputStream.Write(bMsg, 0, bMsg.Length);
socket.OutputStream.Close();
socket.Close();
Console.WriteLine(TAG + "Socket closed");
}
Something to notice is that the server actually closes the socket after it receives the message, why is that even working? And why can't I close the socket on the client side? Am I missing something integral here?
The exception is a Java.IO exception, where the message reads: bt socket closed, read -1
Would really appreciate some help!
First of all, I'm absolutely not a network programmer. What I try to do, is a very simple TCP/IP communication between a Java server and a C# client.
Java server:
public void run(){
try {
// Open server socket
_server = new ServerSocket(SERVER_PORT);
_client = _server.accept();
System.out.println("ComInterface: client connected.");
// Wait for a client data output stream
while(true){
// Receive message from client
BufferedReader is =
new BufferedReader(new InputStreamReader(_client.getInputStream()));
msg = is.readLine();
// Process message
if(msg!=null){
System.out.println("ComInterface: Message Received : <" + msg + ">.");
processMessage(msg); // Independant method
}
else{
System.out.println("ComInterface: client closed connection.");
_client.close();
_client = _server.accept();
System.out.println("ComInterface: client connected.");
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public void sendMessage(String msg){
try {
// Out stream
DataOutputStream os = new DataOutputStream(_client.getOutputStream());
os.writeBytes((String)(msg+"\n"+(char)13));
os.flush();
System.out.println("ComInterface: Message <" + msg + "> sent");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
And here's the C# client:
public class ComInterface : MonoBehaviour
{
public const String SERVER_IP = "127.0.0.1"; // Localhost
public const int PORT = 1100; // Default port
public const int READ_BUFFER_SIZE = 5000; // 4.8828125 kilobytes
private TcpClient _client;
private ASCIIEncoding _asen;
private byte[] _readBuffer;
private String _msg;
public Boolean connected { get; internal set; } // setter is for internal use only
/**
* Initialize internal variables (buffer, socket...)
*/
public ComInterface()
{
connected = false;
_client = new TcpClient();
_asen = new ASCIIEncoding();
_readBuffer = new Byte[READ_BUFFER_SIZE];
_msg = String.Empty;
}
/**
* Connect to server at SERVER_IP:PORT
* Return true if connection was a success, or false if failure.
*/
public Boolean Connect()
{
try
{
_client.Connect(SERVER_IP, PORT);
connected = true;
Array.Clear(_readBuffer, 0, _readBuffer.Length);
Debug.Log("TCPClient: <Connect> Connected to the server");
// Start an asynchronous read invoking ReceiveComMessage
_client.GetStream().BeginRead(_readBuffer, 0, READ_BUFFER_SIZE, new AsyncCallback(ReceiveComMessage), _client.GetStream());
}
catch (Exception ex)
{
Debug.Log("TCPClient: <Connect> Cannot connect to the server - " + ex.Message);
connected = false;
}
// Return connection state
return connected;
}
/**
* Received a message from Communicator
*/
private void ReceiveComMessage(IAsyncResult ar)
{
int BytesRead;
String msg;
try
{
BytesRead = _client.GetStream().EndRead(ar);
if (BytesRead < 1)
{
// if no bytes were read server has close.
Debug.Log("TCPClient: <ReceiveComMessage> The server has closed (BytesRead<1)");
this.Disconnect();
return;
}
// Convert the byte array the message was saved into,
msg = Encoding.ASCII.GetString(_readBuffer);
Debug.Log("C# Message: \"" + msg + "\""); // Output example in log below
BytesRead = 0;
Array.Clear(_readBuffer, 0, _readBuffer.Length);
// Start a new asynchronous read into readBuffer.
_client.GetStream().BeginRead(_readBuffer, 0, READ_BUFFER_SIZE, new AsyncCallback(ReceiveComMessage), _readBuffer);
}
catch (Exception ex)
{
Debug.Log("TCPClient: <ReceiveComMessage> The server has closed (Exception):" + ex.Message + " see " + ex.StackTrace);
this.Disconnect();
}
The main problem is that all the message are arriving incomplete. Here's the log trace:
C#: Message "{
C#: Message ""sender":"Bob"",
C#: Message ""recipient":",
etc...
Instead of for instance
C#: Message "{"sender":"Bob","recipient":[1,2,3]}"
I'm a bit confused and I'd need some help to resolve this. Thank you very much!
TCP is a stream-oriented connection, not message-oriented. It has no concept of a message. When you write out your serialized string, it only sees a meaningless sequence of bytes. TCP is free to break up that stream up into multiple fragments and they will be received at the client in those fragment-sized chunks. It is up to you to reconstruct the entire message on the other end.
In your scenario, one would typically send a message length prefix. This way, the client first reads the length prefix so it can then know how large the incoming message is supposed to be.
I would seriously consider using something like Google's Protocol Buffers as a good way of declaring your messages and then streaming them with the size prefix option. The nice thing is that you define your set of messages once and then use the available tools to automatically generate C++, Java, C#, etc code from the message definitions. This will help in having a consistent messaging set that works between languages.
A message (any data, I mean), when sent through a socket, is divided into several packets.
When printing each received packet, you don't see your whole message.
You should define an end of message string (something like ".#."). Until you receive this sequence, you keep concatenating the messages you receive.
This is what session protocols (that is, protocols that run on the top of TCP) do.
Hope this helps.
Regards, Calil
Take a look at this example...
Java TCP Server...
import java.net.*;
import java.io.*;
public class TcpServer
{
public static void main(String h[])
{
try
{
ServerSocket serverSocket = new ServerSocket(1100);
Socket socket = serverSocket.accept();
System.out.println("Client Accepted");
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
System.out.println("Received: " + bufferedReader.readLine());
PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);
printWriter.println("Hello Theo. Welcome to socket programming.");
} catch (Exception e)
{
System.out.println(e);
}
}
}
C# TCP Client...
using System;
using System.IO;
using System.Net.Sockets;
class Program
{
static void Main(string[] args)
{
try
{
var client = new TcpClient("localhost", 1100);
var stream = client.GetStream();
var streamWriter = new StreamWriter(stream);
streamWriter.WriteLine("My name is Theo");
streamWriter.Flush();
var streamReader = new StreamReader(stream);
Console.WriteLine("Received: " + streamReader.ReadLine());
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
Console.WriteLine("Press a key to continue.");
Console.ReadKey();
}
}
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've been fighting with one problem for a whole 3 days I can't find any solution, please help :)
I work with Visual Studio 2010 and C# language.
I have a device working like a server, that sends some data in a very irregular periods of time (not possible to define any read timeout).
I wrote a TCP client to connect to that server and read data. It works OK, however when something is wrong with the network and server becomes unavailable (e.g. when I plug out the network cable from my computer), it takes about 10 seconds for application to "notice" there is no connection to the server and throw an exception. (I don't know why exactly 10 seconds? Where it's defined? Can I change it?)
I want to react faster - let say after one second after connection broken.
Googling for answer however doesn't provide me any working solution.
The test code is below, I try to make it on 2 threads: one is reading data, the second one is looking for connection status and should alarm me when it's broken. It's not working neither for TCPClient nor Socket class. I've tried to read / write some data with tcpClient.SendTimeout = 100; and stream.WriteTimeout = 100; but it doesn't seem to work.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
namespace TCPClient
{
class Program
{
static volatile bool _continue = true;
static TcpClient tcpClient;
static NetworkStream stream;
static void Main(string[] args)
{
try
{
//client thread - listen from server
Thread tcpListenThread = new Thread(TcpListenThread);
tcpListenThread.Start();
//connection checking thread
Thread keepAliveThread = new Thread(KeepAliveThread);
keepAliveThread.Start();
while (_continue)
{
if (Console.ReadLine() == "q")
{
_continue = false;
}
}
keepAliveThread.Join(2000);
if (keepAliveThread.IsAlive)
{
Console.WriteLine("Thread keepAlive has been aborted...");
keepAliveThread.Abort();
}
tcpListenThread.Join(2000);
if (tcpListenThread.IsAlive)
{
Console.WriteLine("Listen thread has been aborted...");
tcpListenThread.Abort();
}
}
catch (Exception ex)
{
Console.WriteLine("\n" + ex.Message);
}
Console.WriteLine("\nHit any key to quit...");
Console.Read();
}
private static void TcpListenThread()
{
string server = "172.20.30.40";
int port = 3000;
try
{
using (tcpClient = new TcpClient())
{
tcpClient.Connect(server, port);
if (tcpClient.Connected)
Console.WriteLine("Successfully connected to server");
using (stream = tcpClient.GetStream())
{
while (_continue)
{
Byte[] data = new Byte[1024];
Int32 bytes = stream.Read(data, 0, data.Length);
string responseData = System.Text.Encoding.ASCII.GetString(data, 0, bytes);
Console.WriteLine("Received: {0}, responseData);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("Listen thread exception! " + ex.Message);
}
}
private static void KeepAliveThread()
{
while (_continue)
{
if (tcpClient != null)
{
try
{
//...what to put here to check or throw exception when server is not available??...
}
catch (Exception ex)
{
Console.WriteLine("Disconnected...");
}
}
Thread.Sleep(1000); //test for connection every 1000ms
}
}
}
}
Edit:
#carsten's answer: although it looks promising, this solution do not work...
I made the simplest test application for that:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
namespace TCPClientTest
{
class Program
{
static void Main(string[] args)
{
try
{
string server = "172.20.30.40";
int port = 3000;
using (TcpClient tcpClient = new TcpClient())
{
tcpClient.Connect(server, port);
int i = 0;
while (true)
{
// This is how you can determine whether a socket is still connected.
bool blockingState = tcpClient.Client.Blocking;
try
{
byte[] tmp = new byte[1];
tcpClient.Client.Blocking = false;
tcpClient.Client.Send(tmp, 0, 0);
Console.WriteLine("Connected!");
}
catch (SocketException e)
{
// 10035 == WSAEWOULDBLOCK
if (e.NativeErrorCode.Equals(10035))
Console.WriteLine("Still Connected, but the Send would block");
else
{
Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode);
}
}
finally
{
tcpClient.Client.Blocking = blockingState;
}
Console.WriteLine("Connected: {0} ({1})", tcpClient.Client.Connected, i++);
Thread.Sleep(1000);
}
}
}
catch (Exception ex)
{
Console.WriteLine("Global exception: {0}", ex.Message);
}
}
}
}
The results are following, it displays:
Connected!
Connected: True
plus my order number every one second. When I disconnect network cable, it takes 8 seconds to start printing:
Disonnected: error code 10054!
Connected: False
so by 8 seconds I'm not aware that the connection is lost. It looks like pinging is the best option here, yet I'll test another solutions.
I think this is a question that often comes around. This might be why MSDN docs really give a good answer to this - see Socket.Connected
Quote from there:
The Connected property gets the connection state of the Socket as of
the last I/O operation. When it returns false, the Socket was either
never connected, or is no longer connected.
The value of the Connected property reflects the state of the
connection as of the most recent operation. If you need to determine
the current state of the connection, make a nonblocking, zero-byte
Send call. If the call returns successfully or throws a WAEWOULDBLOCK
error code (10035), then the socket is still connected; otherwise, the
socket is no longer connected.
with this sample code:
// .Connect throws an exception if unsuccessful
client.Connect(anEndPoint);
// This is how you can determine whether a socket is still connected.
bool blockingState = client.Blocking;
try
{
byte [] tmp = new byte[1];
client.Blocking = false;
client.Send(tmp, 0, 0);
Console.WriteLine("Connected!");
}
catch (SocketException e)
{
// 10035 == WSAEWOULDBLOCK
if (e.NativeErrorCode.Equals(10035))
Console.WriteLine("Still Connected, but the Send would block");
else
{
Console.WriteLine("Disconnected: error code {0}!", e.NativeErrorCode);
}
}
finally
{
client.Blocking = blockingState;
}
Console.WriteLine("Connected: {0}", client.Connected);
and the straight forward motification for an extension-method:
public static bool IsConnected(this Socket client)
{
// This is how you can determine whether a socket is still connected.
bool blockingState = client.Blocking;
try
{
byte [] tmp = new byte[1];
client.Blocking = false;
client.Send(tmp, 0, 0);
return true;
}
catch (SocketException e)
{
// 10035 == WSAEWOULDBLOCK
if (e.NativeErrorCode.Equals(10035))
return true;
else
{
return false;
}
}
finally
{
client.Blocking = blockingState;
}
}
This is a very old thread, but it's the first SO post that came up when I searched for this question and I found a more useful solution somewhere else, so I thought I'd post a link to it to help others in the future:
https://social.msdn.microsoft.com/Forums/en-US/c857cad5-2eb6-4b6c-b0b5-7f4ce320c5cd/c-how-to-determine-if-a-tcpclient-has-been-disconnected?forum=netfxnetcom&prof=required
ElFenix posted this answer that worked for me:
// Detect if client disconnected
if (tcp.Client.Poll(0, SelectMode.SelectRead))
{
byte[] buff = new byte[1];
if (tcp.Client.Receive(buff, SocketFlags.Peek) == 0)
{
// Client disconnected
bClosed = true;
}
}
Simple answer. You can't. Tcp is made in a way which doesn't allow this. However, the normal way to achieve this is to send ping's with shorter interval than messages. So, say, that whenever you get a message from the server, you start a clock that count down 1 min, then you send a "ping" command (if you haven't received a new message in between). If you don't receive a response to your "ping" within 30 seconds, you conclude that the connection is broken.
Theoretically, you should be able to do this on a package-level (tcp sends ACK which you should be able to check for), but I don't know if that's possible in C#, or any programming language for that matter, or if you need to do that in firmware/hardware.
it will be correct and working if you type the following:
byte[] tmp = new byte[] {0};
...
client.Send(tmp, 1, 0);
...
Just in case anyone else needs something simple and effective.
This is the code I came up with
while (true)
{
string s = null;
DateTime readLag = DateTime.Now;
try
{
s = streamIn.ReadLine();
}
catch (Exception ex)
{
SocketException sockEx = ex.InnerException as SocketException;
if (sockEx != null)
{
OnDebug("(" + sockEx.NativeErrorCode + ") Exception = " + ex);
}
else
{
OnDebug("Not SockEx :" + ex);
}
}
if (enableLog) OnDebug(s);
if (s == null || s == "")
{
if (readLag.AddSeconds(.9) > DateTime.Now)
{
break;
}
}
else
{
CommandParser(s);
}
}
What I got was native error 10035 every time a read failed but would block.
When the connection was trashed, I instantly got a return with no data.
Setting the readLag.AddSeconds() to just below my read timeout would give me a pretty good idea that the time never elapsed, and I got no data. Which should not happen.
Soon as this criteria popped up, I just kick out of the loop and the thread ends.
Hope this helps anyone else out there.
There is a socket option called SO_KEEPALIVE from the oginal unix implemetnation that has the TCP protocol send occasional probes to make sure the other end of the pipe is still listening. It can be set along with how often the probes are set via Socket.IOControl