I having a little issue reading data from Nave32 Flight Controller, I'm able to send data to the card using the SerialDevice class but when the LoadAsync Method is call and there is no more data to read the app hung and does not move forward, there is not exception during the execution of the process. I look at the app state and there is a bunch a task block and I'm not sure why. See image below
UPDATE
Firmware source code is here
here is my example code
namespace Apps.Receiver
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
this.dataRecived.Text = DateTime.Now.ToString();
}
private async Task readAsync(SerialDevice serialPort)
{
string data = string.Empty;
using (var dataReaderObject = new DataReader(serialPort.InputStream))
{
try
{
dataReaderObject.InputStreamOptions = InputStreamOptions.None;
var hasData = true;
while (hasData)
{
var bytesRead = await dataReaderObject.LoadAsync(serialPort.DataBits);
if (bytesRead > 0)
data += dataReaderObject.ReadString(bytesRead);
else
hasData = false;
}
}
catch (Exception ex)
{
status.Text = "reading data fail: " + ex.Message;
closeDevice(serialPort);
}
}
dataRecived.Text = data;
}
private async void sendData_Click(object sender, RoutedEventArgs e)
{
var serialPort = await getSerialDevice("COM3");
if (dataToSend.Text.Length != 0)
{
using (var dataWriteObject = new DataWriter(serialPort.OutputStream))
{
try
{
if (dataToSend.Text.Equals("#", StringComparison.CurrentCultureIgnoreCase))
dataWriteObject.WriteString(dataToSend.Text);
else
dataWriteObject.WriteString(dataToSend.Text + "\n");
var bytesWritten = await dataWriteObject.StoreAsync();
if (bytesWritten > 0)
{
status.Text = dataToSend.Text + ", ";
status.Text += "bytes written successfully!";
}
await readAsync(serialPort);
}
catch (Exception ex)
{
status.Text = "writing data fail: " + ex.Message;
}
}
closeDevice(serialPort);
}
else
{
status.Text = "Enter the text you want to write and then click on 'WRITE'";
}
}
private void closeDevice(SerialDevice device)
{
device.Dispose();
device = null;
}
private async Task<SerialDevice> getSerialDevice(string portName)
{
var aqs = SerialDevice.GetDeviceSelector("COM3");
var devices = await DeviceInformation.FindAllAsync(aqs);
if (devices.Count == 0)
throw new Exception("Unablet to connect to device");
var serialPort = await SerialDevice.FromIdAsync(devices[0].Id);
if (serialPort == null)
throw new Exception("Unablet to Open to port " + portName);
serialPort.BaudRate = 115200;
serialPort.DataBits = 8;
return serialPort;
}
}
}
See this example, try if it helps doing it like this:
await reader.LoadAsync(8192);
while (reader.UnconsumedBufferLength > 0)
{
strBuilder.Append(reader.ReadString(reader.UnconsumedBufferLength));
await reader.LoadAsync(8192);
}
Related
I am able to pair a Bluetooth skincare device to my PC using a C# app that I wrote.
Definition:
//Device Data
BluetoothLEDevice bluetoothLeDevice;
DeviceData deviceData=new DeviceData();
GattDeviceServicesResult result;
static DeviceInformation device = null;
The handlers that reads devices, implemented in MainForm():
// Query for extra properties you want returned
string[] requestedProperties = { "System.Devices.Aep.DeviceAddress", "System.Devices.Aep.IsConnected" };
DeviceWatcher deviceWatcher =
DeviceInformation.CreateWatcher(
BluetoothLEDevice.GetDeviceSelectorFromPairingState(false),
requestedProperties,
DeviceInformationKind.AssociationEndpoint);
// Register event handlers before starting the watcher.
// Added, Updated and Removed are required to get all nearby devices
deviceWatcher.Added += DeviceWatcher_Added;
deviceWatcher.Updated += DeviceWatcher_Updated;
deviceWatcher.Removed += DeviceWatcher_Removed;
// EnumerationCompleted and Stopped are optional to implement.
deviceWatcher.EnumerationCompleted += DeviceWatcher_EnumerationCompleted;
deviceWatcher.Stopped += DeviceWatcher_Stopped;
// Start the watcher.
deviceWatcher.Start();
The code that pairs the device with a click of a button:
private async void btnPair_Click(object sender, EventArgs e)
{
feedbackTB.AppendText("Waiting for pairing..." + Environment.NewLine);
if (device == null)
{
statusBox.Text = "Please, make sure the device is on, and try again. Uncover the device and it will automatically turn on.";
feedbackTB.AppendText("Timed out." + Environment.NewLine);
Thread.Sleep(200);
}
else
{
try
{
hydrationFqList = new List<int>();
psList = new List<int>();
bluetoothLeDevice = await BluetoothLEDevice.FromIdAsync(device.Id);
statusBox.Text = "Attempting to pair with device";
result = await bluetoothLeDevice.GetGattServicesAsync();
if (result.Status == GattCommunicationStatus.Success)
{
statusBox.Text = "Pairing succeeded";
feedbackTB.AppendText("Device paired." + Environment.NewLine);
var services = result.Services;
foreach (var service in services)
{
if (service.Uuid.ToString() == deviceData.devInfoSvc)
{
GattCharacteristicsResult charactiristicResult = await service.GetCharacteristicsAsync();
feedbackTB.AppendText("Skimming device info service... " + Environment.NewLine);
if (charactiristicResult.Status == GattCommunicationStatus.Success)
{
feedbackTB.AppendText("Reading device info... " + Environment.NewLine);
var characteristics = charactiristicResult.Characteristics;
foreach (var characteristic in characteristics)
{
GattCharacteristicProperties properties = characteristic.CharacteristicProperties;
if (properties.HasFlag(GattCharacteristicProperties.Read))
{
GattReadResult rResult = await characteristic.ReadValueAsync();
var reader = DataReader.FromBuffer(rResult.Value);
byte[] input = new byte[reader.UnconsumedBufferLength];
reader.ReadBytes(input);
string data = Encoding.ASCII.GetString(input);
if (characteristic.Uuid.ToString() == deviceData.mfgNameCt)
{
mfgNameTB.Text = data;
}
if (characteristic.Uuid.ToString() == deviceData.modelNumCt)
{
modelNumTB.Text = data;
}
if (characteristic.Uuid.ToString() == deviceData.serialNumCt)
{
serialNumber = data;
snTB.Text = serialNumber;
}
if (characteristic.Uuid.ToString() == deviceData.macAddrCt)
{
macAddress = data;
macAddrTB.Text = macAddress;
}
if (characteristic.Uuid.ToString() == deviceData.hwRevCt)
{
hwRevisionTB.Text = data;
}
if (characteristic.Uuid.ToString() == deviceData.fwRevCt)
{
fwRevisionTB.Text = data;
}
}
statusBox.Text = "Press and hold device against your skin to start scan.";
}
}
}
}
}
else
{
statusBox.Text = "Please, make sure the device is connected, and try again.";
feedbackTB.AppendText("Timed out." + Environment.NewLine);
}
feedbackTB.AppendText("Number of run(s):" + increment + Environment.NewLine);
}
catch (Exception err)
{
ErrorWindow eWindow = new ErrorWindow();
eWindow.label4.Text = err.GetType().ToString();
eWindow.errorDetails.Text = err.Message;
eWindow.ShowDialog();
}
}
}
And this is my newly implemented disconnect button:
private void btnDisconnect _Click(object sender, EventArgs e)
{
bluetoothLeDevice.Dispose();
}
Now we get to the issue.
When the device is on, a blue light is suppose to flash.
When the device is paired to a PC or app, the blue light will stay on.
I expect the device to disconnect when the button is clicked. Upon the click, the light will start flashing again when it's just turned on.
But upon clicking on the button, nothing happened. The device is still stuck connected.
How can I fix this?
I have a function called getMessages that can be called by a Button click (using the RelayCommand trigger) or that is called in a timer every 15s.
The desired behavior is:
webservice > deserialize answer > system notification > updatelistview > insert localDB
But when the function is called by the timer the updatelistview is not done. Why does this happen if the function is the same and works perfectly in the button command?
CODE:
// Get messages for the logged in user
public async void getMessages()
{
try
{
List<FriendGetMessage> msg = new List<FriendGetMessage>();
var response = await CommunicationWebServices.GetCHAT("users/" + au.idUser + "/get", au.token);
if (response.StatusCode == HttpStatusCode.OK) // If there are messages for me.
{
var aux = await response.Content.ReadAsStringAsync();
IEnumerable<FriendGetMessage> result = JsonConvert.DeserializeObject<IEnumerable<FriendGetMessage>>(aux);
if (result != null)
{
foreach (var m in result)
{
msg.Add(m);
}
//MsgList=msg;
foreach (var f in Friends)
{
if (f.msg == null || f.msg.Count() == 0)
{
f.msg = new ObservableCollection<Messages>();
}
foreach (var mess in msg)
{
if (mess.idUser == f.idUser)
{
Messages mm = new Messages();
mm.received = mess.message;
mm.timestamp = "Received " + mess.serverTimestamp;
mm.align = "Right";
// Add to the friend list.
f.msg.Add(mm);
// Add to Local DB
InsertMessage(null, au.idUser.ToString(), f.idUser, mess.message, mess.serverTimestamp);
var notification = new System.Windows.Forms.NotifyIcon()
{
Visible = true,
Icon = System.Drawing.SystemIcons.Information,
BalloonTipIcon = System.Windows.Forms.ToolTipIcon.Info,
BalloonTipTitle = "New Message from " + f.name,
BalloonTipText = "Message: " + mess.message,
};
// Display for 5 seconds.
notification.ShowBalloonTip(5);
// The notification should be disposed when you don't need it anymore,
// but doing so will immediately close the balloon if it's visible.
notification.Dispose();
}
}
}
counterChat = 1; // resets the counter
}
}
else {
counterChat = counterChat * 2;
}
//var sql = "select * from chat";
//var respo = GetFromDatabase(sql);
OnPropertyChanged("Friends");
}
catch (Exception e)
{
MessageBox.Show("GetMessages: " + e);
Debug.WriteLine("{0} Exception caught.", e);
}
}
CODE TIMER:
public void chatUpdate()
{
_timerChat = new DispatcherTimer(DispatcherPriority.Render);
_timerChat.Interval = TimeSpan.FromSeconds(15);
_timerChat.Tick += new EventHandler(timerchat_Tick);
_timerChat.Start();
}
public void timerchat_Tick(object sender, EventArgs e)
{
if (counterChat != incChat)
{
incChat++;
}
else
{
getMessages();
OnPropertyChanged("Friends");
incChat = 0;
}
}
ADDED - I've also tried this and didn't worked (it seems that is some kind of concurrency problem to the ObservableCollection called Friends (is a friendslist) each friend has an ObservableCollection of messages (is a chat))
public void chatUpdate()
{
_timerChat = new DispatcherTimer(DispatcherPriority.Render);
_timerChat.Interval = TimeSpan.FromSeconds(15);
_timerChat.Tick += new EventHandler(timerchat_Tick);
_timerChat.Start();
}
public async void timerchat_Tick(object sender, EventArgs e)
{
if (counterChat != incChat)
{
incChat++;
}
else
{
Application.Current.Dispatcher.Invoke((Action)async delegate { await getMessages(); });
incChat = 0;
}
}
Best regards,
I think you need to make the timer handler be an async method as follows:
public async void timerchat_Tick(object sender, EventArgs e)
{
if (counterChat != incChat)
{
incChat++;
}
else
{
await getMessages();
OnPropertyChanged("Friends");
incChat = 0;
}
}
This way OnPropertyChanged("Friends") is guaranteed to fire after the work in getMessages is done.
The methods need to change to:
DispatcherTimer _timerChat = new DispatcherTimer(DispatcherPriority.Render);
_timerChat.Interval = TimeSpan.FromSeconds(15);
_timerChat.Tick += new EventHandler(timerchat_Tick);
_timerChat.Start();
public async void timerchat_Tick(object sender, EventArgs e)
{
//...
await getMessages();
//...
}
public async Task getMessages()
{
try
{
// ... your code here
string result = await response.Content.ReadAsStringAsync();
// .... rest of your code
}
catch (Exception e)
{
MessageBox.Show("GetMessages: " + e);
}
}
It is solved. The problem was in my ViewModels I was opening multiple threads and sometimes the right one would update the UI and sometimes no.
Thanks for all the answers.
It is my first program on wifi direct so I made a simple program that just discover all nearby devices and then try to connect to one of them the discovery works fine and gets all the devices the problem is in the connection that always fails after using this function WiFiDirectDevice.FromIdAsync() it keeps paring for too long and then it fails always.
I was following the code on this microsoft tutorial: https://channel9.msdn.com/Events/Build/2015/3-98
Here is my code:
// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace App7
{
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
DeviceInformationCollection DevicesFound;
WiFiDirectAdvertisementPublisher pub;
WiFiDirectConnectionListener listener;
CancellationTokenSource m_CancellationTokenSource;
private async void button_Click(object sender, RoutedEventArgs e)
{
String DeviceSelector = WiFiDirectDevice.GetDeviceSelector(WiFiDirectDeviceSelectorType.AssociationEndpoint);
DevicesFound = await DeviceInformation.FindAllAsync(DeviceSelector);
if(DevicesFound.Count>0)
{
connectedDeviceslist.ItemsSource = DevicesFound;
var dialog = new MessageDialog(DevicesFound[0].Name);
await dialog.ShowAsync();
}
}
private async void button1_Click(object sender, RoutedEventArgs e)
{
int selectedIndex = connectedDeviceslist.SelectedIndex;
String deviceID = DevicesFound[selectedIndex].Id;
var selecetedDevice = DevicesFound[selectedIndex];
try
{
WiFiDirectConnectionParameters connectionParams = new WiFiDirectConnectionParameters();
connectionParams.GroupOwnerIntent = Convert.ToInt16("1");
connectionParams.PreferredPairingProcedure = WiFiDirectPairingProcedure.GroupOwnerNegotiation;
m_CancellationTokenSource = new CancellationTokenSource();
var wfdDevice = await WiFiDirectDevice.FromIdAsync(selecetedDevice.Id, connectionParams).AsTask(m_CancellationTokenSource.Token);
var EndpointPairs = wfdDevice.GetConnectionEndpointPairs();
}
catch(Exception ex)
{
await Dispatcher.RunAsync(CoreDispatcherPriority.High, async() =>
{
var dialog = new MessageDialog(ex.Message);
await dialog.ShowAsync();
});
}
}
private void button2_Click(object sender, RoutedEventArgs e)
{
pub = new WiFiDirectAdvertisementPublisher();
pub.Advertisement.ListenStateDiscoverability = WiFiDirectAdvertisementListenStateDiscoverability.Normal;
listener = new WiFiDirectConnectionListener();
listener.ConnectionRequested += OnConnectionRequested;
pub.Start();
}
private async void OnConnectionRequested(WiFiDirectConnectionListener sender, WiFiDirectConnectionRequestedEventArgs args)
{
try
{
var ConnectionRequest = args.GetConnectionRequest();
var tcs = new TaskCompletionSource<bool>();
var dialogTask = tcs.Task;
var messageDialog = new MessageDialog("Connection request received from " + ConnectionRequest.DeviceInformation.Name, "Connection Request");
// Add commands and set their callbacks; both buttons use the same callback function instead of inline event handlers
messageDialog.Commands.Add(new UICommand("Accept", null, 0));
messageDialog.Commands.Add(new UICommand("Decline", null, 1));
// Set the command that will be invoked by default
messageDialog.DefaultCommandIndex = 1;
// Set the command to be invoked when escape is pressed
messageDialog.CancelCommandIndex = 1;
await Dispatcher.RunAsync(CoreDispatcherPriority.High, async () =>
{
// Show the message dialog
var commandChosen = await messageDialog.ShowAsync();
tcs.SetResult((commandChosen.Label == "Accept") ? true : false);
});
var fProceed = await dialogTask;
if (fProceed == true)
{
var tcsWiFiDirectDevice = new TaskCompletionSource<WiFiDirectDevice>();
var wfdDeviceTask = tcsWiFiDirectDevice.Task;
// WriteToOutput("Connecting to " + ConnectionRequest.DeviceInformation.Name + "...");
WiFiDirectConnectionParameters ConnectionParams = new WiFiDirectConnectionParameters();
await Dispatcher.RunAsync(CoreDispatcherPriority.High, async () =>
{
try
{
ConnectionParams.GroupOwnerIntent = 14;
tcsWiFiDirectDevice.SetResult(await WiFiDirectDevice.FromIdAsync(ConnectionRequest.DeviceInformation.Id, ConnectionParams));
}
catch (Exception ex)
{
// WriteToOutput("FromIdAsync task threw an exception: " + ex.ToString());
}
});
WiFiDirectDevice wfdDevice = await wfdDeviceTask;
var EndpointPairs = wfdDevice.GetConnectionEndpointPairs();
/* m_ConnectedDevices.Add("dummy", wfdDevice);
WriteToOutput("Devices connected on L2, listening on IP Address: " + EndpointPairs[0].LocalHostName.ToString() + " Port: " + Globals.strServerPort);
StreamSocketListener listenerSocket = new StreamSocketListener();
listenerSocket.ConnectionReceived += OnSocketConnectionReceived;
await listenerSocket.BindEndpointAsync(EndpointPairs[0].LocalHostName, Globals.strServerPort);*/
}
else
{
/* // Decline the connection request
WriteToOutput("Connection request from " + ConnectionRequest.DeviceInformation.Name + " was declined");
ConnectionRequest.Dispose();*/
}
}
catch (Exception ex)
{
//WriteToOutput("FromIdAsync threw an exception: " + ex.ToString());
}
}
}
}
can someone please help me out with this... I've been struggling all day.
So I'm trying to learn Async sockets which is something that's been giving me trouble.
The issue is basically the way I'm updating the ListBox with people who have joined the chat room's names:
Basically what I'm doing is having each client send "!!addlist [nickname]" when they join the server.
It's not ideal as it doesn't check for duplicates etc. but now I just want to know why it won't work.
Whenever somebody adds a name they haven't seen before, they will also send "!!addlist [nick]"
In this way, every time someone joins, the lists should be updated for everyone.
The issue seems to be that all the clients start communicating at the same time and it interferes with the buffer.
I've tried using a separate buffer for every client so that's not the issue.
I've tried using lock() but that doesn't seem to be working either.
Essentially what happens is the buffers seem to truncate; where there is data from two different people in the same buffer.
Please just tell me what I'm doing wrong with the buffers or on the client side:
Note that the async socket is using Send instead of BeginSend.
I've tried both methods and they run into the same issue... so it's probably client side?
public partial class Login : Form
{
private ChatWindow cw;
private Socket serverSocket;
private List<Socket> socketList;
private byte[] buffer;
private bool isHost;
private bool isClosing;
public void startListening()
{
try
{
this.isHost = true; //We're hosting this server
cw.callingForm = this; //Give ChatForm the login form (this) [that acts as the server]
cw.Show(); //Show ChatForm
cw.isHost = true; //Tell ChatForm it is the host (for display purposes)
this.Hide(); //And hide the login form
serverSocket.Bind(new IPEndPoint(IPAddress.Any, int.Parse(portBox.Text))); //Bind to our local address
serverSocket.Listen(1); //And start listening
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null); //When someone connects, begin the async callback
cw.connectTo("127.0.0.1", int.Parse(portBox.Text), nicknameBox.Text); //And have ChatForm connect to the server
}
catch (Exception) { /*MessageBox.Show("Error:\n\n" + e.ToString());*/ } //Let us know if we ran into any errors
}
public void AcceptCallback(IAsyncResult AR)
{
try
{
Socket s = serverSocket.EndAccept(AR); //When someone connects, accept the new socket
socketList.Add(s); //Add it to our list of clients
s.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), s); //Begin the async receive method using our buffer
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null); //And start accepting new connections
}
catch (Exception) {}
}
public void ReceiveCallback(IAsyncResult AR) //When a message from a client is received
{
try
{
if (isClosing)
return;
Socket s = (Socket)AR.AsyncState; //Get the socket from our IAsyncResult
int received = s.EndReceive(AR); //Read the number of bytes received (*need to add locking code here*)
byte[] dbuf = new byte[received]; //Create a temporary buffer to store just what was received so we don't have extra data
Array.Copy(buffer, dbuf, received); //Copy the received data from our buffer to our temporary buffer
foreach (Socket client in socketList) //For each client that is connected
{
try
{
if (client != (Socket)AR.AsyncState) //If this isn't the same client that just sent a message (*client handles displaying these*)
client.Send(dbuf); //Send the message to the client
}
catch (Exception) { }
} //Start receiving new data again
s.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), s);
}
catch (Exception) { /*cw.output("\n\nError:\n\n" + e.ToString());*/ }
}
public void SendCallback(IAsyncResult AR)
{
try
{
Socket s = (Socket)AR.AsyncState;
s.EndSend(AR);
}
catch (Exception) { /*cw.output("\n\nError:\n\n" + e.ToString());*/ }
}
Here is the client side:
public void getData()
{
try
{
byte[] buf = new byte[1024];
string message = "";
while(isConnected)
{
Array.Clear(buf, 0, buf.Length);
message = "";
clientSocket.Receive(buf, buf.Length, SocketFlags.None);
message = Encoding.ASCII.GetString(buf);
if (message.StartsWith("!!addlist"))
{
message = message.Replace("!!addlist", "");
string userNick = message.Trim();
if (!namesBox.Items.Contains(userNick))
{
addNick(userNick.Trim());
}
continue;
}
else if (message.StartsWith("!!removelist"))
{
message = message.Replace("!!removelist", "");
string userNick = message.Trim();
removeNick(userNick);
output("Someone left the room: " + userNick);
continue;
}
else if (!namesBox.Items.Contains(message.Substring(0, message.IndexOf(":"))))
{
addNick(message.Substring(0, message.IndexOf(":")).Trim()); //So they at least get added when they send a message
}
output(message);
}
}
catch (Exception)
{
output("\n\nConnection to the server lost.");
isConnected = false;
}
}
Here is my addNick function that seems to fix some things?
public void addNick(string n)
{
if (n.Contains(" ")) //No Spaces... such a headache
return;
if (n.Contains(":"))
return;
bool shouldAdd = true;
n = n.Trim();
for (int x = namesBox.Items.Count - 1; x >= 0; --x)
if (namesBox.Items[x].ToString().Contains(n))
shouldAdd = false;
if (shouldAdd)
{
namesBox.Items.Add(n);
output("Someone new joined the room: " + n);
sendRaw("!!addlist " + nickName);
}
}
I think the issue is that some of the packets are being skipped?
Maybe there's too much code in the client after Receive before it gets called again?
Should I create a separate thread for each message so that receive runs constantly? (Dumb)
Should I have my client use Async receives and sends as well?
I have a feeling that is the answer ^
With all of the checks I do, I managed to clean up the duplicate name issue... but i regularly receive messages with spaces and partial messages from other clients it seems.
Okay so, after messing with this for a long time, I have it relatively stable.
For starters, I added the following state object:
public class StateObject
{
public Socket workSocket = null;
public const int BufferSize = 1024;
public byte[] buffer = new byte[BufferSize];
public StringBuilder sb = new StringBuilder();
public bool newConnection = true;
}
This makes it easy to keep track of each connection and gives each connection its own buffer.
The second thing I did was look for a new line in each message.
I wasn't looking for this in the original code and I believe this was the root of most issues.
I also gave the responsibility of dealing with username management to the server; something that I should have done from the start obviously.
Here is the current server code:
This code is in no way perfect and I'm continuously finding new errors the more I try to break it. I'm going to keep messing with it for awhile but at the moment, it seems to work decently.
public partial class Login : Form
{
private ChatWindow cw;
private Socket serverSocket;
private List<Socket> socketList;
private byte[] buffer;
private bool isHost;
private bool isClosing;
private ListBox usernames;
public Login()
{
InitializeComponent();
}
private void Login_Load(object sender, EventArgs e)
{
ipLabel.Text = getLocalIP();
cw = new ChatWindow();
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socketList = new List<Socket>();
buffer = new byte[1024];
isClosing = false;
usernames = new ListBox();
}
public string getLocalIP()
{
return Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork).ToString();
}
private void joinButton_Click(object sender, EventArgs e)
{
try
{
int tryPort = 0;
this.isHost = false;
cw.callingForm = this;
if (ipBox.Text == "" || portBox.Text == "" || nicknameBox.Text == "" || !int.TryParse(portBox.Text.ToString(), out tryPort))
{
MessageBox.Show("You must enter an IP Address, Port, and Nickname to connect to a server.", "Missing Info");
return;
}
this.Hide();
cw.Show();
cw.connectTo(ipBox.Text, int.Parse(portBox.Text), nicknameBox.Text);
}
catch(Exception otheree) {
MessageBox.Show("Error:\n\n" + otheree.ToString(),"Error connecting...");
cw.Hide();
this.Show();
}
}
private void hostButton_Click(object sender, EventArgs e)
{
int tryPort = 0;
if (portBox.Text == "" || nicknameBox.Text == "" || !int.TryParse(portBox.Text.ToString(), out tryPort)) {
MessageBox.Show("You must enter a Port and Nickname to host a server.", "Missing Info");
return;
}
startListening();
}
public void startListening()
{
try
{
this.isHost = true; //We're hosting this server
cw.callingForm = this; //Give ChatForm the login form (this) [that acts as the server]
cw.Show(); //Show ChatForm
cw.isHost = true; //Tell ChatForm it is the host (for display purposes)
this.Hide(); //And hide the login form
serverSocket.Bind(new IPEndPoint(IPAddress.Any, int.Parse(portBox.Text))); //Bind to our local address
serverSocket.Listen(1); //And start listening
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null); //When someone connects, begin the async callback
cw.connectTo("127.0.0.1", int.Parse(portBox.Text), nicknameBox.Text); //And have ChatForm connect to the server
}
catch (Exception) {}
}
public void AcceptCallback(IAsyncResult AR)
{
try
{
StateObject state = new StateObject();
state.workSocket = serverSocket.EndAccept(AR);
socketList.Add(state.workSocket);
state.workSocket.BeginReceive(state.buffer, 0, StateObject.BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), state);
serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), null);
}
catch (Exception) {}
}
public void ReceiveCallback(IAsyncResult AR)
{
try
{
if (isClosing)
return;
StateObject state = (StateObject)AR.AsyncState;
Socket s = state.workSocket;
String content = "";
int received = s.EndReceive(AR);
if(received > 0)
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, received));
content = state.sb.ToString();
if (content.IndexOf(Environment.NewLine) > -1) //If we've received the end of the message
{
if (content.StartsWith("!!addlist") && state.newConnection)
{
state.newConnection = false;
content = content.Replace("!!addlist", "");
string userNick = content.Trim();
if (isHost && userNick.StartsWith("!"))
userNick = userNick.Replace("!", "");
userNick = userNick.Trim();
if (userNick.StartsWith("!") || userNick == string.Empty || usernames.Items.IndexOf(userNick) > -1)
{
//Invalid Username :c get dropped
s.Send(Encoding.ASCII.GetBytes("Invalid Username/In Use - Sorry :("));
s.Shutdown(SocketShutdown.Both);
s.Disconnect(false);
s.Close();
socketList.Remove(s);
return;
}
usernames.Items.Add(userNick);
foreach (string name in usernames.Items)
{
if (name.IndexOf(userNick) < 0)
{
s.Send(Encoding.ASCII.GetBytes("!!addlist " + name + "\r\n"));
Thread.Sleep(10); //such a hack... ugh it annoys me that this works
}
}
foreach (Socket client in socketList)
{
try
{
if (client != s)
client.Send(Encoding.ASCII.GetBytes("!!addlist " + userNick + "\r\n"));
}
catch (Exception) { }
}
}
else if (content.StartsWith("!!removelist") && !state.newConnection)
{
content = content.Replace("!!removelist", "");
string userNick = content.Trim();
usernames.Items.Remove(userNick);
foreach (Socket client in socketList)
{
try
{
if (client != s)
client.Send(Encoding.ASCII.GetBytes("!!removelist " + userNick + "\r\n"));
}
catch (Exception) { }
}
}
else if (state.newConnection) //if they don't give their name and try to send data, just drop.
{
s.Shutdown(SocketShutdown.Both);
s.Disconnect(false);
s.Close();
socketList.Remove(s);
return;
}
else
{
foreach (Socket client in socketList)
{
try
{
if (client != s)
client.Send(System.Text.Encoding.ASCII.GetBytes(content));
}
catch (Exception) { }
}
}
}
Array.Clear(state.buffer, 0, StateObject.BufferSize);
state.sb.Clear();
s.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
}
catch (Exception) {
socketList.Remove(((StateObject)AR.AsyncState).workSocket);
}
}
public void SendCallback(IAsyncResult AR)
{
try
{
StateObject state = (StateObject)AR.AsyncState;
state.workSocket.EndSend(AR);
}
catch (Exception) {}
}
private void Login_FormClosed(object sender, FormClosedEventArgs e)
{
try
{
this.isClosing = true;
if (this.isHost)
{
foreach (Socket c in socketList)
{
if (c.Connected)
{
c.Close();
}
}
serverSocket.Shutdown(SocketShutdown.Both);
serverSocket.Close();
serverSocket = null;
serverSocket.Dispose();
}
socketList.Clear();
}
catch (Exception) { }
finally
{
Application.Exit();
}
}
}
public class StateObject
{
public Socket workSocket = null;
public const int BufferSize = 1024;
public byte[] buffer = new byte[BufferSize];
public StringBuilder sb = new StringBuilder();
public bool newConnection = true;
}
The client code (work in progress):
public partial class ChatWindow : Form
{
private Socket clientSocket;
private Thread chatThread;
private string ipAddress;
private int port;
private bool isConnected;
private string nickName;
public bool isHost;
public Login callingForm;
private static object conLock = new object();
public ChatWindow()
{
InitializeComponent();
isConnected = false;
isHost = false;
}
public string getIP() {
return Dns.GetHostEntry(Dns.GetHostName()).AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork).ToString();
}
public void displayError(string err)
{
output(Environment.NewLine + Environment.NewLine + err + Environment.NewLine);
}
public void op(string s)
{
try
{
lock (conLock)
{
chatBox.Text += s;
}
}
catch (Exception) { }
}
public void connectTo(string ip, int p, string n) {
try
{
this.Text = "Trying to connect to " + ip + ":" + p + "...";
this.ipAddress = ip;
this.port = p;
this.nickName = n;
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
if (!isHost)
{
op("Connecting to " + ipAddress + ":" + port + "...");
}
else
{
output("Listening on " + getIP() + ":" + port + "...");
}
clientSocket.Connect(ipAddress, port);
isConnected = true;
if (!isHost)
{
this.Text = "Connected to " + ipAddress + ":" + port + " - Nickname: " + nickName;
output("Connected!");
}
else
{
this.Text = "Hosting on " + getIP() + ":" + port + " - Nickname: " + nickName;
}
chatThread = new Thread(new ThreadStart(getData));
chatThread.Start();
nickName = nickName.Replace(" ", "");
nickName = nickName.Replace(":", "");
if(nickName.StartsWith("!"))
nickName = nickName.Replace("!", "");
namesBox.Items.Add(nickName);
sendRaw("!!addlist " + nickName);
}
catch (ThreadAbortException)
{
//do nothing; probably closing chat window
}
catch (Exception e)
{
if (!isConnected)
{
this.Hide();
callingForm.Show();
clearText();
MessageBox.Show("Error:\n\n" + e.ToString(), "Error connecting to remote host");
}
}
}
public void removeNick(string n)
{
if (namesBox.Items.Count <= 0)
return;
for (int x = namesBox.Items.Count - 1; x >= 0; --x)
if (namesBox.Items[x].ToString().Contains(n))
namesBox.Items.RemoveAt(x);
}
public void clearText()
{
try
{
lock (conLock)
{
chatBox.Text = "";
}
}
catch (Exception) { }
}
public void addNick(string n)
{
if (n.Contains(" ")) //No Spaces... such a headache
return;
if (n.Contains(":"))
return;
bool shouldAdd = true;
n = n.Trim();
for (int x = namesBox.Items.Count - 1; x >= 0; --x)
if (namesBox.Items[x].ToString().Contains(n))
shouldAdd = false;
if (shouldAdd)
{
namesBox.Items.Add(n);
output("Someone new joined the room: " + n);
//sendRaw("!!addlist " + nickName);
}
}
public void addNickNoMessage(string n)
{
if (n.Contains(" ")) //No Spaces... such a headache
return;
if (n.Contains(":"))
return;
bool shouldAdd = true;
n = n.Trim();
for (int x = namesBox.Items.Count - 1; x >= 0; --x)
if (namesBox.Items[x].ToString().Contains(n))
shouldAdd = false;
if (shouldAdd)
{
namesBox.Items.Add(n);
//sendRaw("!!addlist " + nickName);
}
}
public void getData()
{
try
{
byte[] buf = new byte[1024];
string message = "";
while(isConnected)
{
Array.Clear(buf, 0, buf.Length);
message = "";
int gotData = clientSocket.Receive(buf, buf.Length, SocketFlags.None);
if (gotData == 0)
throw new Exception("I swear, this was working before but isn't anymore...");
message = Encoding.ASCII.GetString(buf);
if (message.StartsWith("!!addlist"))
{
message = message.Replace("!!addlist", "");
string userNick = message.Trim();
if(!namesBox.Items.Contains(userNick))
{
addNick(userNick);
}
continue;
}
else if (message.StartsWith("!!removelist"))
{
message = message.Replace("!!removelist", "");
string userNick = message.Trim();
removeNick(userNick);
output("Someone left the room: " + userNick);
continue;
}
output(message);
}
}
catch (Exception)
{
isConnected = false;
output(Environment.NewLine + "Connection to the server lost.");
}
}
public void output(string s)
{
try
{
lock (conLock)
{
chatBox.Text += s + Environment.NewLine;
}
}
catch (Exception) { }
}
private void ChatWindow_FormClosed(object sender, FormClosedEventArgs e)
{
try
{
if(isConnected)
sendRaw("!!removelist " + nickName);
isConnected = false;
clientSocket.Shutdown(SocketShutdown.Receive);
if (chatThread.IsAlive)
chatThread.Abort();
callingForm.Close();
}
catch (Exception) { }
}
private void sendButton_Click(object sender, EventArgs e)
{
if(isConnected)
send(sendBox.Text);
}
private void sendBox_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
if (isConnected)
{
if (sendBox.Text != "")
{
send(sendBox.Text);
sendBox.SelectAll();
e.SuppressKeyPress = true;
e.Handled = true;
}
}
}
}
private void send(string t) {
try
{
byte[] data = System.Text.Encoding.ASCII.GetBytes(nickName + ": " + t + "\r\n");
clientSocket.Send(data);
output(nickName + ": " + t);
}
catch (Exception e)
{
displayError(e.ToString());
}
}
private void sendRaw(string t)
{
try
{
byte[] data = System.Text.Encoding.ASCII.GetBytes(t + "\r\n");
clientSocket.Send(data);
}
catch (Exception e)
{
displayError(e.ToString());
}
}
private void chatBox_TextChanged(object sender, EventArgs e)
{
chatBox.SelectionStart = chatBox.Text.Length;
chatBox.ScrollToCaret();
}
private void sendBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
e.SuppressKeyPress = true;
}
}
To do:
Add invokes, more delegates, do some more QA and find out what breaks it.
Also, I believe there's still the possibility of packet loss due to the client addlist functions being in the read loop. I believe this is why the "crappy hack" using Thread.Sleep(10) in the server callback for name population is an issue.
I think it might be better to either pass the command off to another thread while continuing to read or have the client tell the server it's ready for another name.
Otherwise, there might be some data loss during name updates.
The other thing is that, as was said in the comments above, delegates should be used when updating the UI objects (chatbox and listbox). I wrote the code for these but ultimately removed it because there was no noticeable change and I wanted to keep it simple.
I do still use an object lock when outputting text to the chatbox, but there's no noticeable difference there.
The code should be added as not using delegates is potentially problematic, but I literally caught the chat box in an infinite loop of updates without issue.
I tried breaking it with telnet and was successful so I added a newConnection property to the StateObject to ensure that each client can only send "!!addlist" once.
There are, of course, other ways to abuse the server by creating a client that joins and leaves repeatedly, so ultimately I will probably end up passing the !!removelist handling to the server instead of leaving it up to the client.
I wrote a service to send sensor data over bluetooth on android. Although I got no errors my C# client stops getting data after I sent exactly 561K data. At this moment it seems like my android continues to send data but my client doesn't get any. After a while, android also stops sending data. I tried different configurations. My program always stops sending data after "Service->Server". I don't get any errors but it stops sending. Here is android program.
#Override
public synchronized int onStartCommand(Intent intent, int flags, int startId) {
Log.i(EXTRA_MESSAGE,"onStartCommand");
if(isRunning)
Log.e(EXTRA_MESSAGE, "I am already running");
else
{
isRunning = true;
BluetoothDevice selectedDevice = (BluetoothDevice) intent.getParcelableExtra(EXTRA_MESSAGE);
if(selectedDevice == null)
{Log.i(EXTRA_MESSAGE,"null it is "); return -1;}
connect = new ConnectThread(selectedDevice);
connect.start();
mHandler =new Handler(){
#Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch(msg.what){
case SUCCESS_CONNECT:
connected = new ConnectedThread((BluetoothSocket) msg.obj);
Toast.makeText(getApplicationContext(), "Connected", 0).show();
//connected.write(("Connected").getBytes());
Log.i(EXTRA_MESSAGE, "we are connected");
isConnected = true;
break;
case MESSAGE_READ:
Toast.makeText(getApplicationContext(), ((byte[]) msg.obj).toString(), 0).show();
break;
}
}
};
mSensor = (SensorManager) getSystemService(SENSOR_SERVICE);
sSensor = mSensor.getDefaultSensor(Sensor.TYPE_ORIENTATION);
mSensor.registerListener(this, sSensor,SensorManager.SENSOR_DELAY_NORMAL);
// sSensor = mSensor.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
// mSensor.registerListener(this, sSensor,SensorManager.SENSOR_DELAY_NORMAL);
sSensor = mSensor.getDefaultSensor(Sensor.TYPE_GYROSCOPE);
mSensor.registerListener(this, sSensor,SensorManager.SENSOR_DELAY_NORMAL);
}
return super.onStartCommand(intent, flags, startId);
}
#Override
#Override
public void onSensorChanged(SensorEvent event) {
Log.i(EXTRA_MESSAGE, "Sensor data arrived");
if(isConnected )
{
String toSend = Integer.toString(event.sensor.getType())+ ":" + Long.toString(event.timestamp)+ ":";
for(float f : event.values){
toSend = toSend + Float.toString(f)+":";
}
//
connected.write(toSend.getBytes());
}
}
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public ConnectThread(BluetoothDevice device) {
// Use a temporary object that is later assigned to mmSocket,
// because mmSocket is final
BluetoothSocket tmp = null;
mmDevice = device;
// Get a BluetoothSocket to connect with the given BluetoothDevice
try {
// MY_UUID is the app's UUID string, also used by the server code
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
Log.i(EXTRA_MESSAGE,"connectThread started successfully");
} catch (IOException e) { }
mmSocket = tmp;
}
public void run() {
// Cancel discovery because it will slow down the connection
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
mmSocket.connect();
Log.i(EXTRA_MESSAGE,"connectThread connect successfully");
} catch (IOException connectException) {
// Unable to connect; close the socket and get out
Log.i(EXTRA_MESSAGE,"connectThread connect exception");
try {
mmSocket.close();
} catch (IOException closeException) { }
}
// Do work to manage the connection (in a separate thread)
mHandler.obtainMessage(SUCCESS_CONNECT, mmSocket).sendToTarget();
}
/** Will cancel an in-progress connection, and close the socket */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public boolean shouldContinue = true;
int nBytes =0;
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// Get the input and output streams, using temp objects because
// member streams are final
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
Log.i(EXTRA_MESSAGE,"connectedThread sockets");
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer; // buffer store for the stream
int bytes; // bytes returned from read()
// Keep listening to the InputStream until an exception occurs
while(shouldContinue) {
try {
// Read from the InputStream
buffer = new byte[1024];
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI activity
Log.e(EXTRA_MESSAGE, " We read");
mHandler.obtainMessage(MESSAGE_READ, bytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
break;
}
}
}
/* Call this from the main activity to send data to the remote device */
public void write(byte[] bytes) {
try {
Log.i(EXTRA_MESSAGE,"Service->Server");
mmOutStream.write(bytes);
nBytes += bytes.length;
Log.i(EXTRA_MESSAGE,"ok" + String.valueOf(nBytes ));
} catch (IOException e) {
Log.i(EXTRA_MESSAGE,"exception");
}
}
/* Call this from the main activity to shutdown the connection */
public void cancel() {
try {
shouldContinue = false;
mmSocket.close();
} catch (IOException e) { }
}
}
Also my c# thread is as follows
public void ServerConnectThread()
{
serverStarted = true;
int counter = 0;
updateUI("Server started, waiting for clients");
BluetoothListener blueListener = new BluetoothListener(mUUID);
blueListener.Start();
BluetoothClient conn = blueListener.AcceptBluetoothClient();
updateUI("Client has connected");
Stream mStream = conn.GetStream();
while (true)
{
try
{
//handle server connection
byte[] received = new byte[1024];
mStream.Read(received, 0, received.Length);
counter += Encoding.ASCII.GetString(received).Length;
String[] fields = Encoding.ASCII.GetString(received).Split(':');
double[] data = new double[3];
for (int i = 2; i < 5; i++) data[i-2] = double.Parse(fields[i]);
//mSource.notifyObserver(Int16.Parse(fields[0]), data);
updateUI(counter.ToString() + " "+ fields[2]+ ":" + fields[3] + ":" + fields[4]);
byte[] sent = Encoding.ASCII.GetBytes("Hello World");
mStream.Write(sent, 0, sent.Length);
}
catch (IOException exception)`enter code here`
{
updateUI("Client has disconnected!!!!");
}
}
}
One final thing is I've found thousands of 561K android program which sounded a little interesting.
Ok I found my own problem. Basically I m sending a reply from my client when I got the sensor data and It is not handled by android app.