Related
There was such a problem: I ran the program many times and now it throws the following error FloodException: Flood prevention. Telegram now requires your program to do requests again only after 73611 seconds have passed (TimeToWait property). If you think the culprit of this problem may lie in TLSharp's implementation, open a Github issue please.
I attach the code below:
using System;
using System.Text;
using System.Windows.Forms;
using TeleSharp.TL;
using TeleSharp.TL.Messages;
using TLSharp.Core;
namespace tgBM
{
public partial class Form1: Form
{
string phone;
string code;
int n = 1;
StringBuilder sb = new StringBuilder ();
TelegramClient client = new TelegramClient (2646156, "08ec188e0bdee432e568120348f5f13a"); // create a client with parameters
public Form1 ()
{
InitializeComponent();
}
string str = "";
public async void authAsync()
{
var dialogs = (TLDialogs) await client.GetUserDialogsAsync();
foreach (var element in dialogs.Chats)
{
TLChat chat = element as TLChat;
if (element is TLChannel)
{
var offset = 0;
TLChannel channel = element as TLChannel;
if (channel.Title == "TOPLES")
{
TLChannel ch = element as TLChannel;
TLInputPeerChannel inputPeer = new TLInputPeerChannel() {ChannelId = ch.Id, AccessHash = (long) ch.AccessHash};
while (n! = 11)
{
try
{
TLChannelMessages res = await client.SendRequestAsync <TLChannelMessages>
(new TLRequestGetHistory() {Peer = inputPeer, Limit = 20, AddOffset = offset, OffsetId = 0});
var msgs = res.Messages;
if (res.Count> offset)
{
offset + = msgs.Count;
foreach (TLAbsMessage msg in msgs)
{
if (msg is TLMessage)
{
TLMessage message = msg as TLMessage;
str + = n.ToString () + "\ t" + message.Id + "\ t" + message.FromId + "\ t" + message.Message + Environment.NewLine;
}
if (msg is TLMessageService)
continue;
n ++;
}
}
else
break;
}
catch (Exception ex)
{
MessageBox.Show (ex.Message);
break;
}
}
}
}
textBox3.Text = str;
}
}
private async void button1_Click (object sender, EventArgs e)
{
phone = textBox1.Text;
await client.ConnectAsync (); // make a connection
var hash = await client.SendCodeRequestAsync(phone);
}
private async void button2_Click (object sender, EventArgs e)
{
code = textBox2.Text;
var user = await client.MakeAuthAsync(phone, await client.SendCodeRequestAsync(phone), code);
authAsync();
}
}
}
In a comment, you said are in a testing phase.
In this case, you should read https://core.telegram.org/api/auth#test-accounts
According to this page, there are 3 ways to perform tests while limiting the risk for FLOOD_WAIT errors:
Connect to the Test servers instead of the Production servers (seems not possible with TLSharp)
Use Test accounts with phone numbers 99966XYYYY (only available on Test servers)
Connect as a user with the phone number you use to create the API ID/Hash
I can do all that with WTelegramClient (TLSharp is no longer maintained and cannot connect to Telegram servers anymore)
First of all I am a complete begineer in C# and literally don't know how to use async/await/cancellation/Tasks, I come from a embedded systems background with basic-moderate C/C++ knowledge. So i just get the idea what those async functions do here. the async task codes are copied from samples from microsoft, they simply keep on reading the serial port for new bytes. I made some animations for the UI through Blend, and that's just it nothing more fancy.
This app runs on a raspberry with IOT core, and its sole job is to provide a UI for user to control a microcontroller with plain text instructions send through serial port. All these buttons simply send some strings to serial port which my microcontroller reads and acts accordingly.
Problem 1: I modified a line in the ReadAsync function uint ReadBufferLength = 1; the value was 128 in sample code, my microcontroller sends a complete instruction or data ending with "\r\n". This meant it will keep on reading bytes in serial that come from the microcontroller until the buffer is full and call the ReadAsync function again. since it was reading until the buffer (128) was full, multiple instructions come and fill the buffer. So i had no way to figure out what is the instruction in the buffer. So my work around here was to change the buffer to 1 and append a string with new bytes from serial port until it finds Environment.NewLine and then build logic based on the complete instruction in the string.
Question 1: How do I modify the code to not change the uint ReadBufferLength = 1; and make the ReadAsync function store my serial incoming data until it finds \r\n ?
MAJOR Problem 2: As soon as I enter Manual/Auto mode by clicking the front panel button, my microcontroller starts sending temperature sensor value through serial port as fast as possible without any delay between instructions. I know the raspberry pi (win10 iot core) is fast enough to not miss any instruction or the physical serial port buffer of the PI getting overfilled. It works fine, there is no problem at this point in the app, no crash or slowdowns. But overtime I can see that the app is consuming more and more RAM. the behavior is same in PC if I build it in x86 mode. after running it for 1 hour it will consume over 120 megabytes whereas it starts at 30-50 megabytes initially.
To recreate the problem, please change string qFilter = SerialDevice.GetDeviceSelector("UART0"); UART0 to appropriate com port in windows. otherwise the app will not run. Probably a Virtual COM port needs to be installed or a usb to ttl module might be required.
Problem 3: Cannot build the app in release mode using the option "Compile with .NET Native tool chain", without this it runs fine in the raspberry.
Internal compiler error: MCG0024:UnresolvableTypeReference Unresolvable type reference 'System.Runtime.InteropServices.WindowsRuntime.IRestrictedErrorInfo' in 'Assembly(Name=System.Private.CoreLib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e)' found. Please check the references in your build system. A reference is either missing or an assembly is missing an expected type. hvac_version_4
github project file
using Windows.UI;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;
using Windows.Devices.SerialCommunication;
using Windows.Storage.Streams;
using System.Threading;
using Windows.Devices.Enumeration;
using System;
using System.Linq;
using System.Threading.Tasks;
using System.Text.RegularExpressions;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media.Animation;
using Microsoft.Toolkit.Uwp.UI.Animations.Behaviors;
// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409
namespace hvac_version_2
{
/// <summary>
/// An empty page that can be used on its own or navigated to within a Frame.
/// </summary>
public sealed partial class MainPage : Page
{
SolidColorBrush greenBrush = new SolidColorBrush(Colors.Green);
SolidColorBrush redBrush = new SolidColorBrush(Colors.Red);
SolidColorBrush greyBrush = new SolidColorBrush(Colors.DarkSlateGray);
//private bool updatingValues;
private CancellationTokenSource ReadCancellationTokenSource;
private SerialDevice serialPort = null;
DataWriter dataWriteObject = null;
DataReader dataReaderObject = null;
public string strFromPort;
private string final_string;
private int compressorAkey=0;
private int compressorBkey=0;
private int pump_entry = 0;
private int compAentry=0;
private int compBentry=0;
private int fan1entry = 0;
private int fan2entry = 0;
private double Blur_amount = 0;
public MainPage()
{
this.InitializeComponent();
stop_button.IsEnabled = false;
set_temperature.IsEnabled = false;
set_switching_time.IsEnabled = false;
auto_mode.IsEnabled = false;
manual_mode.IsEnabled = false;
compressorA_button.IsEnabled = false;
compressorB_button.IsEnabled = false;
fanA_button.IsEnabled = false;
fanB_button.IsEnabled = false;
init.IsEnabled = false;
init_timing.IsEnabled = false;
pump_button.IsEnabled = false;
load_animation.Begin();
}
private async void stop_button_Click(object sender, RoutedEventArgs e)
{
//do something
start_animation.Stop();
if (serialPort == null) return;
await sendToPort("end");
set_temperature.IsEnabled = false;
set_switching_time.IsEnabled = false;
manual_mode.IsEnabled = true;
auto_mode.IsEnabled = true;
stop_button.IsEnabled = false;
compressorA_button.IsEnabled = false;
compressorB_button.IsEnabled = false;
fanA_button.IsEnabled = false;
fanB_button.IsEnabled = false;
init.IsEnabled = false;
init_timing.IsEnabled = false;
pump_button.IsEnabled = false;
start_button.IsEnabled = true;
pump_entry = 0;
compAentry = 0;
compBentry = 0;
fan1entry = 0;
fan2entry = 0;
stop_animation.Begin();
start_button.Width = 162;
GC.Collect();
}
private async void set_temperature_changed(object sender, EventArgs e)
{
//System.Diagnostics.Debug.WriteLine("entered the async void set_temp.............");
}
private async void set_switching_time_value_changed(object sender, EventArgs e)
{
//System.Diagnostics.Debug.WriteLine("entered the async void switching time.............");
}
private async void manual_mode_Click(object sender, RoutedEventArgs e)
{
if (serialPort == null) return;
await sendToPort("manual");
auto_mode.IsEnabled = false;
manual_mode.IsEnabled = false;
stop_button.IsEnabled = true;
set_temperature.IsEnabled = true;
set_switching_time.IsEnabled = true;
pump_button.IsEnabled = true;
fanA_button.IsEnabled = true;
fanB_button.IsEnabled = true;
compressorA_button.IsEnabled = true;
compressorB_button.IsEnabled = true;
init.IsEnabled = true;
init_timing.IsEnabled = true;
}
private async void auto_mode_Click(object sender, RoutedEventArgs e)
{
set_switching_time.IsEnabled = true;
set_temperature.IsEnabled = true;
init.IsEnabled = true;
stop_button.IsEnabled = true;
init_timing.IsEnabled = true;
if (serialPort == null) return;
await sendToPort("auto");
}
private async void start_button_Click(object sender, RoutedEventArgs e)
{
stop_animation.Stop();
await sendToPort("ready");
auto_mode.IsEnabled = true;
manual_mode.IsEnabled = true;
start_button.IsEnabled = false;
start_animation.Begin();
System.Diagnostics.Debug.WriteLine("sending ready command");
}
private async void compressorA_button_Click(object sender, RoutedEventArgs e)
{
if (compAentry == 0)
{
if (serialPort == null) return;
await sendToPort("compressorAON"); //sending key
compAentry = 1;
}
else
{
if (serialPort == null) return;
await sendToPort("compressorAoff"); //sending key
compAentry = 0;
}
}
private async void compressorB_button_Click(object sender, RoutedEventArgs e)
{
if (compBentry == 0)
{
if (serialPort == null) return;
await sendToPort("compressorBON"); //sending key
compBentry = 1;
}
else
{
if (serialPort == null) return;
await sendToPort("compressorBoff"); //sending key
compBentry = 0;
}
}
private async void fanA_button_Click(object sender, RoutedEventArgs e)
{
if (fan1entry == 0)
{
if (serialPort == null) return;
await sendToPort("fan1on");
fan1entry = 1;
}
else
{
if (serialPort == null) return;
await sendToPort("fan1off");
fan1entry = 0;
}
}
private async void fanB_button_Click(object sender, RoutedEventArgs e)
{
if (fan2entry == 0)
{
if (serialPort == null) return;
await sendToPort("fan2on");
fan2entry = 1;
}
else
{
if (serialPort == null) return;
await sendToPort("fan2off");
fan2entry = 0;
}
}
private async void pump_button_Click(object sender, RoutedEventArgs e)
{
if (pump_entry == 0)
{
if (serialPort == null) return;
await sendToPort("pumpon");
pump_entry = 1;
}
else
{
if (serialPort == null) return;
await sendToPort("pumpoff");
pump_entry = 0;
}
}
private async void Page_Loaded(object sender, RoutedEventArgs e)
{
string qFilter = SerialDevice.GetDeviceSelector("UART0");
DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(qFilter);
if (devices.Any())
{
string deviceId = devices.First().Id;
await OpenPort(deviceId);
}
ReadCancellationTokenSource = new CancellationTokenSource();
while (true)
{
//System.Diagnostics.Debug.WriteLine("program came before await listen");
await Listen();
}
}
private async Task OpenPort(string deviceId)
{
serialPort = await SerialDevice.FromIdAsync(deviceId);
if (serialPort != null)
{
serialPort.WriteTimeout = TimeSpan.FromMilliseconds(1000);
serialPort.ReadTimeout = TimeSpan.FromMilliseconds(50);
serialPort.BaudRate = 115200;
serialPort.Parity = SerialParity.None;
serialPort.StopBits = SerialStopBitCount.One;
serialPort.DataBits = 8;
serialPort.Handshake = SerialHandshake.None;
//Console.WriteLine("Serial port configured successfully");
//txtStatus.Text = "Serial port configured successfully";
}
}
private async Task Listen()
{
try
{
if (serialPort != null)
{
dataReaderObject = new DataReader(serialPort.InputStream);
await ReadAsync(ReadCancellationTokenSource.Token);
}
}
catch (Exception ex)
{
txtStatus.Text = ex.Message;
}
finally
{
if (dataReaderObject != null) // Cleanup once complete
{
dataReaderObject.DetachStream();
dataReaderObject = null;
}
}
}
private async Task ReadAsync(CancellationToken cancellationToken)
{
Task<UInt32> loadAsyncTask;
uint ReadBufferLength = 1; // only when this buffer would be full next code would be executed
dataReaderObject.InputStreamOptions = InputStreamOptions.Partial;
loadAsyncTask = dataReaderObject.LoadAsync(ReadBufferLength).AsTask(cancellationToken); // Create a task object
//string debug = dataReaderObject.ReadString(bytesRead2);
//System.Diagnostics.Debug.WriteLine("writing string debug"+ debug);
UInt32 bytesRead = await loadAsyncTask; // Launch the task and wait until buffer would be full
if (bytesRead > 0)
{
strFromPort = dataReaderObject.ReadString(bytesRead);
final_string = final_string + strFromPort;
//txtStatus2.Text = final_string;
if (final_string.Contains(Environment.NewLine))
{
string logicstring = final_string;
final_string = "";
//System.Diagnostics.Debug.WriteLine("writing logic string " + logicstring);
if (logicstring.StartsWith("A"))
{
if(logicstring.Contains("-"))
{
//it is a negative number
logicstring = Regex.Match(logicstring, #"\d+").Value;
int gauge1_pass = int.Parse(logicstring) * -1;
tempgauge1.Value = gauge1_pass;
}
else
{
logicstring = Regex.Match(logicstring, #"\d+").Value;
int gauge1_pass = int.Parse(logicstring);
tempgauge1.Value = gauge1_pass;
}
}
if (logicstring.StartsWith("B"))
{
if (logicstring.Contains("-"))
{
//it is a negative number
logicstring = Regex.Match(logicstring, #"\d+").Value;
int gauge2_pass = int.Parse(logicstring) * -1;
tempgauge1.Value = gauge2_pass;
}
else
{
logicstring = Regex.Match(logicstring, #"\d+").Value;
int gauge2_pass = int.Parse(logicstring);
tempgauge2.Value = gauge2_pass;
}
}
if (logicstring.StartsWith("setting"))
{
if (logicstring.Contains("-"))
{
//it is a negative number
logicstring = Regex.Match(logicstring, #"\d+").Value;
int previous_temp_setting = int.Parse(logicstring) * -1;
set_temperature.Value = previous_temp_setting;
}
else
{
logicstring = Regex.Match(logicstring, #"\d+").Value;
int previous_temp_setting = int.Parse(logicstring);
set_temperature.Value = previous_temp_setting;
}
}
if (logicstring.StartsWith("pumpon"))
{
pump_status_light.Fill = greenBrush;
Storyboard_Pump_Led.RepeatBehavior = RepeatBehavior.Forever;
Storyboard_Pump_Led.Begin();
}
if (logicstring.StartsWith("pumpoff"))
{
pump_status_light.Fill = redBrush;
Storyboard_Pump_Led.Stop();
}
if (logicstring.StartsWith("pumpfail"))
{
if (!FailPopup.IsOpen)
{
FailPopup.IsOpen = true;
}
}
if (logicstring.StartsWith("clear the popup"))
{
if (FailPopup.IsOpen)
{
FailPopup.IsOpen = false;
}
}
if (logicstring.StartsWith("switch_interval"))
{
logicstring = Regex.Match(logicstring, #"\d+").Value;
int previous_interval_setting = int.Parse(logicstring);
set_switching_time.Value = previous_interval_setting;
}
if (logicstring.StartsWith("timer"))
{
logicstring = Regex.Match(logicstring, #"\d+").Value;
int remaining_time = int.Parse(logicstring);
remaining_time = remaining_time / 1000;
timer_box.Text = remaining_time.ToString();
}
if (logicstring.StartsWith("Free RAM = "))
{
logicstring = Regex.Match(logicstring, #"\d+").Value;
int remaining_memory = int.Parse(logicstring);
free_memory.Text = remaining_memory.ToString();
}
if (logicstring.StartsWith("fan1on"))
{
fan1_status_light.Fill = greenBrush;
storyboard_fan1_led.RepeatBehavior = RepeatBehavior.Forever;
storyboard_fan1_led.Begin();
}
if (logicstring.StartsWith("fan1off"))
{
fan1_status_light.Fill = redBrush;
storyboard_fan1_led.Stop();
}
if (logicstring.StartsWith("fan2on"))
{
fan2_status_light.Fill = greenBrush;
storyboard_fan_2.RepeatBehavior = RepeatBehavior.Forever;
storyboard_fan_2.Begin();
}
if (logicstring.StartsWith("fan2off"))
{
fan2_status_light.Fill = redBrush;
storyboard_fan_2.Stop();
}
if (logicstring.StartsWith("compressorAON")) //key
{
compressorA_status_light.Fill = greyBrush;
compressorAkey = 1;
}
if (logicstring.StartsWith("compressorAoff")) //key
{
compressorA_status_light.Fill = redBrush;
compressorAkey = 0;
}
if (logicstring.StartsWith("compressorBON")) //key
{
compressorB_status_light.Fill = greyBrush;
compressorBkey = 1;
}
if (logicstring.StartsWith("compressorBoff")) //key
{
compressorB_status_light.Fill = redBrush;
compressorBkey = 0;
}
if (logicstring.StartsWith("compressorA on")) // actually compressor on
{
compressorA_status_light.Fill = greenBrush;
storyboard_compA_Led.RepeatBehavior = RepeatBehavior.Forever;
storyboard_compA_Led.Begin();
}
if (logicstring.StartsWith("compressorB on")) // actually compressor on
{
compressorB_status_light.Fill = greenBrush;
storyboard_compB_led.RepeatBehavior = RepeatBehavior.Forever;
storyboard_compB_led.Begin();
}
if (logicstring.StartsWith("compressor off")) // actually compressor off
{
if (compressorBkey == 1)
{
compressorB_status_light.Fill = greyBrush;
storyboard_compB_led.Stop();
}
if (compressorAkey == 1)
{
compressorA_status_light.Fill = greyBrush;
storyboard_compA_Led.Stop();
}
if (compressorBkey == 0)
{
compressorB_status_light.Fill = redBrush;
storyboard_compB_led.Stop();
}
if (compressorAkey == 0)
{
compressorA_status_light.Fill = redBrush;
storyboard_compA_Led.Stop();
}
}
if (logicstring.StartsWith("compressorA off")) // actually compressor off
{
compressorA_status_light.Fill = redBrush;
storyboard_compA_Led.Stop();
}
if (logicstring.StartsWith("compressorB off")) // actually compressor off
{
compressorB_status_light.Fill = redBrush;
storyboard_compB_led.Stop();
}
}
}
}
private async Task WriteAsync(string text2write)
{
Task<UInt32> storeAsyncTask;
if (text2write.Length != 0)
{
dataWriteObject.WriteString(text2write);
storeAsyncTask = dataWriteObject.StoreAsync().AsTask(); // Create a task object
UInt32 bytesWritten = await storeAsyncTask; // Launch the task and wait
if (bytesWritten > 0)
{
//txtStatus.Text = bytesWritten + " bytes written at " + DateTime.Now.ToString(System.Globalization.CultureInfo.CurrentUICulture.DateTimeFormat.LongTimePattern);
}
}
else { }
}
private async Task sendToPort(string sometext)
{
try
{
if (serialPort != null)
{
dataWriteObject = new DataWriter(serialPort.OutputStream);
await WriteAsync(sometext);
}
else { }
}
catch (Exception ex)
{
txtStatus.Text = ex.Message;
}
finally
{
if (dataWriteObject != null) // Cleanup once complete
{
dataWriteObject.DetachStream();
dataWriteObject = null;
}
}
}
private void CancelReadTask()
{
if (ReadCancellationTokenSource != null)
{
if (!ReadCancellationTokenSource.IsCancellationRequested)
{
ReadCancellationTokenSource.Cancel();
}
}
}
private void Page_Unloaded(object sender, RoutedEventArgs e)
{
CancelReadTask();
if (serialPort != null)
{
serialPort.Dispose();
}
serialPort = null;
}
private async void init_Click(object sender, RoutedEventArgs e)
{
double sendtempvalue = (double)set_temperature.Value;
if (serialPort == null) return;
await sendToPort("settemp," + sendtempvalue);
//System.Diagnostics.Debug.WriteLine("EEPROM " + sendtempvalue);
}
private async void init_timing_Click(object sender, RoutedEventArgs e)
{
double switching_time_minute = (double)set_switching_time.Value;
if (serialPort == null) return;
await sendToPort("switch_interval," + switching_time_minute);
//System.Diagnostics.Debug.WriteLine("EEPROM " + switching_time_minute);
}
/*
private void Page_Unloaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
}*/
}
}
Question 1: How do I modify the code to not change the uint
ReadBufferLength = 1; and make the ReadAsync function store my serial
incoming data until it finds \r\n ?
Please try to use following code. When using ReadBufferLength = 1, the transmission efficiency will be lower.
private async Task ReadAsync(CancellationToken cancellationToken)
{
Task<UInt32> loadAsyncTask;
uint ReadBufferLength = 128; // only when this buffer would be full next code would be executed
dataReaderObject.InputStreamOptions = InputStreamOptions.Partial;
loadAsyncTask = dataReaderObject.LoadAsync(ReadBufferLength).AsTask(cancellationToken); // Create a task object
//string debug = dataReaderObject.ReadString(bytesRead2);
//System.Diagnostics.Debug.WriteLine("writing string debug"+ debug);
UInt32 bytesRead = await loadAsyncTask; // Launch the task and wait until buffer would be full
if (bytesRead > 0)
{
strFromPort = dataReaderObject.ReadString(bytesRead);
final_string = final_string + strFromPort;
//txtStatus2.Text = final_string;
if (final_string.Contains(Environment.NewLine))
{
string logicstring = final_string;
final_string = final_string.Substring(final_string.IndexOf(Environment.NewLine) + 1);
//System.Diagnostics.Debug.WriteLine("writing logic string " + logicstring);
}
}
}
Problem 2: after running it for 1 hour it will consume over 120
megabytes whereas it starts at 30-50 megabytes initially.
Please try to build the app in release but not using the .NET native toolchain.It's a known issue. Please see here.
Problem 3: Cannot build the app in release mode using the option
"Compile with .NET Native tool chain", without this it runs fine in
the raspberry.
Please remove the references runtime.win10-arm-aot.Microsoft.NETCore.UniversalWindowsPlatform and runtime.win10-arm.Microsoft.NETCore.UniversalWindowsPlatform in your project. The project will be compiled with .Net native tool chain successfully without the two libraries, since that these two libs depends on the native lib of System.Runtime.InteropServices.
We are getting joint data in JSON Objects format. The data is streaming live on the Chrome Console as you can see in the picture.
How do we take this data (from the Chrome Console) and send it over to a C# file on the computer in real-time?
From Console: JSON Object data from kinectv2
Here is some basic c# websocket code, it doesnt have to convert the data it receives to a string. I was just testing some stuff
C# simple websocket connection
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows;
public partial class MainWindow : Window
{
private Websocket_listen _listener;
public MainWindow()
{
_listener = new Websocket_listen("127.0.0.1", 13000);
_listener.StringReceived += _listener_StringReceived;
_listener.Start();
}
private void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
_running = false;
_listener.Stop();
}
private void _listener_StringReceived(string received)
{
}
}
public class Websocket_listen
{
public readonly int port = 13000;
public readonly IPAddress localAddr;
private TcpListener server = null;
private TcpClient client = null;
private NetworkStream stream = null;
private bool connected = false;
private byte[] bytes = new byte[2048];
private byte[] _last_key = new byte[4];
private int _last_message_length;
private bool read_more_message;
private Thread _current_thread;
private bool _running = true;
private int _received = 0;
private StringBuilder _results = new StringBuilder();
public event Action<string> StringReceived = null;
public Websocket_listen(string ipaddress, int port)
{
this.port = port;
localAddr = IPAddress.Parse(ipaddress);
server = new TcpListener(localAddr, port);
}
public void _running_loop()
{
while (_running)
{
try
{
server.Server.ReceiveTimeout = 5000;
server.Server.SendTimeout = 5000;
client = server.AcceptTcpClient();
// Get a stream object for reading and writing
stream = client.GetStream();
}
catch (Exception ex)
{
continue;
}
while (_running)
{
try
{
try
{
l.AcquireWriterLock(-1);
if (messsages.Count > 0)
{
byte[] msg = System.Text.Encoding.ASCII.GetBytes(messsages[0]);
//byte[] msg = System.Text.Encoding.ASCII.GetBytes("Connected to " + Environment.MachineName);
Array.Copy(msg, 0, bytes, 2, msg.Length);
bytes[0] = 0x81;
bytes[1] = (byte)msg.Length;
// Send back a response.
stream.Write(bytes, 0, msg.Length + 2);
messsages.RemoveAt(0);
}
}
finally
{
l.ReleaseWriterLock();
}
}
catch { }
try
{
_received = stream.Read(bytes, 0, bytes.Length);
}
catch
{
continue;
}
if (_received == 0)
continue;
if (!connected)
{
_is_connection();
continue;
}
if (!_parse_message())
break;
}
}
try
{
stream.Close();
client.Close();
}
catch (Exception ex)
{
}
}
private bool _parse_message()
{
int offset = 0;
int message_length = 0;
if (read_more_message)
{
_last_message_length -= bytes.Length;
message_length = _last_message_length;
if (message_length < bytes.Length)
message_length += 8;
}
else
{
_results.Clear();
var trigger = bytes[0];
var magic_byte = bytes[1];
bool is_text = (0x1 & trigger) != 0;
bool is_fin = (0x80 & trigger) != 0;
if (trigger == 0x88)
{
connected = false;
return false;
}
/*
text = 0x81
binary = 0x82
close 0x88
ping 0x89
pong -0x8A
*/
if (!is_fin)
{
return true;
}
if (!is_text)
{
return true;
}
//If the second byte minus 128 is between 0 and 125, this is the length of message.
//If it is 126, the following 2 bytes (16-bit unsigned integer), if 127, the following 8 bytes (64-bit unsigned integer) are the length.
var r = magic_byte - 128;
var key_starts_at = 0;
if (r <= 125)
{
key_starts_at = 2;
message_length = r;
}
else if (r == 126)
{
key_starts_at = 4;
message_length = BitConverter.ToUInt16(new byte[] { bytes[3], bytes[2] }, 0);
}
else if (r == 127)
{
key_starts_at = 10;
for (var m = 7; m >= 0; --m)
{
message_length += bytes[m] << (8 * (7 - m));
}
}
else
{
// not documented
}
//// because its encoded
_last_message_length = message_length;
Array.Copy(bytes, key_starts_at, _last_key, 0, 4);
offset = key_starts_at + 4;
}
for (var mx = 0; mx < message_length && offset + mx < bytes.Length; ++mx)
{
bytes[offset + mx] = (byte)(bytes[offset + mx] ^ _last_key[mx % 4]);
}
var new_result = System.Text.Encoding.ASCII.GetString(bytes, offset, Math.Min(message_length, bytes.Length - offset));
_results.Append(new_result);
read_more_message = message_length > bytes.Length;
if (!read_more_message)
{
try
{
StringReceived?.Invoke(_results.ToString());
}
catch (Exception ex)
{
}
}
return true;
}
private void _is_connection()
{
try
{
string data = System.Text.Encoding.ASCII.GetString(bytes, 0, _received);
if (!new Regex("^GET").IsMatch(data))
return;
Byte[] response = Encoding.UTF8.GetBytes("HTTP/1.1 101 Switching Protocols" + Environment.NewLine
+ "Connection: Upgrade" + Environment.NewLine
+ "Upgrade: websocket" + Environment.NewLine
+ "Sec-WebSocket-Accept: " + Convert.ToBase64String(
SHA1.Create().ComputeHash(
Encoding.UTF8.GetBytes(
new Regex("Sec-WebSocket-Key: (.*)").Match(data).Groups[1].Value.Trim() + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
)
)
) + Environment.NewLine
+ Environment.NewLine);
stream.Write(response, 0, response.Length);
connected = true;
}
catch (Exception ex)
{
}
}
public void Stop()
{
_running = false;
}
public void Start()
{
// Start listening for client requests.
server.Start();
_current_thread = new Thread(new ThreadStart(_running_loop));
_current_thread.Start();
}
ReaderWriterLock l = new ReaderWriterLock();
List<string> messsages = new List<string>();
internal void Send(string msg)
{
try
{
l.AcquireWriterLock(-1);
messsages.Add(msg);
}
catch
{
}
finally
{
l.ReleaseWriterLock();
}
}
}
not a very robust javascript simple websocket to connect to c# with the same port
try {
var isopen = false;
connection = new WebSocket('ws://127.0.0.1:13000');
// When the connection is open, send some data to the server
connection.onopen = function () {
isopen = true;
connection.send('Ping'); // Send the message 'Ping' to the server
$('#open_messages').html("connection made\r\n<br>");
$('#socket_errors').html("");
};
// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ', error);
$('#socket_errors').html(error.currentTarget.url + " failed\r\n<br><button id=\"reconnect\">Try to connect</button>");
};
// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ', e);
$('#messages').append(e.data + "\r\n<br>");
};
connection.onclose = function(x) {
console.log('closed ', x);
$('#open_messages').html("Disconnected\r\n<br>");
$('#socket_errors').html("<button id=\"reconnect\">Connect</button>");
};
} catch (err) {
console.log(err);
}
That should get you started sending data back and forth, but will probably need extra exception handling.
Not sure if sending strings across is efficient, by you could probably change the data later.
I wrote a little web server on Linux using the Mono HTTPListener class. It works fine for http requests. However, if I use a self-signed SSL certificate (created with openssl and installed with httpcfg) it will throw an un-catchable exception as soon as the request forom a browser comes in.
The exception is:
Unhandled Exception:
System.IO.IOException: The authentication or decryption has failed. ---> Mono.Security.Protocol.Tls.TlsException: The client stopped the handshake.
at Mono.Security.Protocol.Tls.SslServerStream.EndNegotiateHandshake (IAsyncResult asyncResult) <0xb4b079c8 + 0x001cf> in <filename unknown>:0
at Mono.Security.Protocol.Tls.SslStreamBase.AsyncHandshakeCallback (IAsyncResult asyncResult) <0xb4b07428 + 0x0005f> in <filename unknown>:0
Here is the complete code:
using System;
using System.Net;
using System.IO;
using System.Text;
using System.Threading;
namespace SSLTest
{
class MainClass
{
static void Main ()
{
try
{
HttpListener l = new HttpListener ();
l.Prefixes.Add ("https://*:8443/");
l.Start ();
Console.WriteLine("Server is running.");
while (l.IsListening)
{
//create the worker thread
HttpListenerContext ctx = l.GetContext(); //.GetContext() blocks until something comes in
if(ctx != null)
{
if(ctx.Request.RemoteEndPoint != null)
{
Thread workerThread = new Thread(() => RunWorker(ctx));
workerThread.Start();
}
}
}
Console.WriteLine("Server is stopped.");
}
catch(Exception ex)
{
Console.WriteLine ("Exception in Main: " + ex);
}
}
static void RunWorker(HttpListenerContext ctx)
{
try
{
if(ctx.Request != null)
{
if(ctx.Request.RemoteEndPoint != null)
{
Console.WriteLine ("Got request from " + ctx.Request.RemoteEndPoint.ToString());
string rstr = "Test Website!\n" + DateTime.Now.ToString();
byte[] buf = Encoding.UTF8.GetBytes(rstr);
if(buf!=null)
{
ctx.Response.ContentLength64 = buf.Length;
ctx.Response.OutputStream.Write(buf, 0, buf.Length);
}
}
}
}
catch(Exception ex)
{
Console.WriteLine ("#Exception in RunWorker: " + ex.Message);
}
}
}
}
This is the case when I am using a browser for the first time. The browser will show something like "Unsafe certificate! Do you want to continue (not recommended)?". If I click on Yes and restart the crashed server app it will work from that moment on.
How can I fix this?
Also, I am not able to catch this exception with a try block. It will always terminate my application. How can I prevent that?
Should be fixed by an unreleased bug fix https://bugzilla.xamarin.com/show_bug.cgi?id=52675... although I have not had a chance to test.
I have seen this as well and am currently trying to find a way to handle the exception. This looks to be a bug in Mono.
It occurs when certificate verification fails in any way.
I obtained a CA signed cert which fixed the problem as long as the certificate common name (dns) is the same as the dns used in the URL I'm trying to send a get request to. If I instead specify the public IP address in the url (which the cert is not registered with) the mono app will crash with an unhandled exception.
One option we're considering is implementing a TCP based webserver which would use a TcpListener instead of the heavy HttpListener which would in turn fix other problems we're seeing with mono httplistener prefixes not working correctly when bound to the internal IP behind a NAT. This would mean the cert could be bound pro-grammatically as well, it just takes more work.
Below is a very rough version of what this might look like. By no means is this a finished product but it may serve to help others on this same path... I'll have to do this in two answers, this is the tcp web server.
using System;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
namespace Example.Lib.Net
{
internal class TcpWebServer : IDisposable
{
private TcpListener m_Listener = null;
private bool m_IsSSL = false;
private X509Certificate2 m_ServerCertificate = null;
internal X509Certificate2 ServerCertificate
{
get { return m_ServerCertificate; }
set { m_ServerCertificate = value; }
}
internal void Start(string ip, int port, bool useSsl = false)
{
if (useSsl) // for player streams always use ssl to
{
m_IsSSL = true;
m_ServerCertificate = new X509Certificate2("./cert/cert.pfx", "pass");
X509Store store = new X509Store(StoreName.TrustedPublisher, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadWrite);
store.Add(m_ServerCertificate);
store.Close();
}
IPAddress ipAddr = IPAddress.Any;
if (ip != "*") IPAddress.TryParse(ip, out ipAddr);
try
{
m_Listener = new TcpListener(ipAddr, port);
m_Listener.Start();
m_Listener.BeginAcceptTcpClient(OnClientAccepted, m_Listener);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
private void OnClientAccepted(IAsyncResult ar)
{
TcpListener listener = ar.AsyncState as TcpListener;
if (listener == null)
return;
TcpClient client = listener.EndAcceptTcpClient(ar);
client.ReceiveBufferSize = 65535;
client.Client.ReceiveBufferSize = 65535;
TcpWebConnection con = new TcpWebConnection(client, this, m_IsSSL);
listener.BeginAcceptTcpClient(OnClientAccepted, listener);
}
}
}
This is the tcp web connection code.
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Net.Security;
using System.Security.Authentication;
using System.Text;
using System.Threading;
using System.Web;
using Example.Lib.Common;
using Example.Lib.Models;
namespace Example.Lib.Net
{
internal enum RequestType
{
None = 0,
GET = 1,
POST = 2,
OPTIONS = 3
}
internal class TcpWebConnection : IDisposable
{
#region private members
private bool m_IsDisposed = false;
private bool m_IsSSL = false;
private bool m_HasHeaders = false;
private bool m_FileCreated = false;
private bool m_IsFileUpload = false;
private RequestType m_RequestType = RequestType.None;
private string m_ReadData = string.Empty;
private string m_Request = string.Empty;
private string m_RemoteIP = string.Empty;
private string m_AbsoluteURI = string.Empty;
private string m_ContentType = string.Empty;
private string m_TempFilename = string.Empty;
private byte[] m_EndBoundaryBytes = null;
private byte[] m_StartBoundaryBytes = null;
private int m_ContentLength = 0;
private long m_StartBoundaryIndex = -1;
private long m_EndBoundaryIndex = -1;
private long m_BytesRead = 0;
private NameValueCollection m_QueryString = null;
private string[] m_Segments = new string[1];
private string m_HttpVersion = "HTTP/1.1";
private byte[] m_PostData = null;
private byte[] m_Buffer = new byte[65535];
private ReadWriteBuffer m_TempBuffer;
private FileStream m_FileStream = null;
private MemoryStream m_FullBuffer = new MemoryStream();
private TcpClient m_Client = null;
private System.IO.Stream m_NetworkStream = null;
private TcpWebServer m_Parent = null;
private Thread m_Thread_Read = null;
private Timer m_Timer_Check = null;
private DateTime m_LastRead = DateTime.Now;
private AutoResetEvent m_WaitHandle_Write;
#endregion private members
#region constructors
internal TcpWebConnection(TcpClient client, TcpWebServer parent, bool ssl)
{
m_WaitHandle_Write = new AutoResetEvent(false);
m_TempBuffer = new ReadWriteBuffer(65535);
m_IsSSL = ssl;
m_Segments[0] = string.Empty;
m_Client = client;
m_Parent = parent;
m_RemoteIP = ((IPEndPoint)m_Client.Client.RemoteEndPoint).Address.ToString();
if (ssl)
{
m_NetworkStream = new SslStream(m_Client.GetStream(), false);
}
else
{
m_NetworkStream = m_Client.GetStream();
}
m_NetworkStream.ReadTimeout = 2000;
m_Timer_Check = new Timer(Timer_Check_Callback, this, 2000, 2000);
// start threads
m_Thread_Read = new Thread(DoRead);
m_Thread_Read.IsBackground = true;
m_Thread_Read.Start();
}
#endregion constructors
#region destructors
~TcpWebConnection()
{
try
{
if (m_Timer_Check != null) m_Timer_Check.Dispose();
m_Timer_Check = null;
}
catch { } // if the timer was
}
#endregion destructors
#region internal properties
internal bool IsLargeFileUpload { get; set; } = false;
internal string TempFilename
{
get { return m_TempFilename; }
set { m_TempFilename = value; }
}
/// <summary>
/// Remote IP
/// </summary>
internal string RemoteIP
{
get { return m_RemoteIP; }
}
internal string AbsoluteURI
{
get { return m_AbsoluteURI; }
}
internal string ContentType
{
get { return m_ContentType; }
}
internal string[] Segments
{
get { return m_Segments; }
}
internal NameValueCollection QueryString
{
get { return m_QueryString; }
}
internal Stream NetworkStream
{
get { return m_NetworkStream; }
}
internal int ContentLength
{
get { return m_ContentLength; }
}
#endregion internal properties
#region private methods
private void Timer_Check_Callback(object state)
{
if ((DateTime.Now - m_LastRead).TotalSeconds > 15)
{
try
{
Program.BlacklistIP(m_RemoteIP, "TcpWebConnection - Timer", "Connection Timed Out");
ProcessRequest(m_ReadData);
Dispose();
}
catch (Exception e) { }
}
}
private void DoRead()
{
if (m_IsSSL)
{
try
{
((SslStream)m_NetworkStream).AuthenticateAsServer(m_Parent.ServerCertificate, false, SslProtocols.Tls, false);
((SslStream)m_NetworkStream).BeginRead(m_Buffer, 0, m_Buffer.Length, new AsyncCallback(SslRead), m_NetworkStream);
m_NetworkStream.ReadTimeout = 5000;
m_NetworkStream.WriteTimeout = 5000;
}
catch (Exception e)
{
//Console.WriteLine("SSL Auth Error: " + e.Message);
}
}
else
{
NormalRead();
}
}
private void UpdatePostData()
{
m_FullBuffer.Position = 0;
byte[] fullBuffer = Common.Conversion.MemoryStreamToByteArray(m_FullBuffer);
m_FullBuffer.Dispose();
if (m_StartBoundaryIndex > -1 && m_EndBoundaryIndex > -1)
{
m_PostData = new byte[m_EndBoundaryIndex - m_StartBoundaryIndex];
Array.Copy(fullBuffer, m_StartBoundaryIndex, m_PostData, 0, m_EndBoundaryIndex - m_StartBoundaryIndex);
}
}
internal void SaveFile(string filepath)
{
try
{
UpdatePostData();
if (m_PostData == null) return;
if (!Directory.Exists(Path.GetDirectoryName(filepath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(filepath));
}
if (File.Exists(filepath))
{
File.Delete(filepath);
}
using (FileStream output = new FileStream(filepath, FileMode.Create, FileAccess.Write))
{
output.Write(m_PostData, 0, m_PostData.Length);
}
}
catch (Exception e)
{
// report error
}
}
private void AppendBuffer(byte[] newBuffer, int length)
{
// we need to keep a running buffer here, the last 1024 bytes? how best to find the end boundary? need to determine when the stream is finished!
m_TempBuffer.Write(newBuffer, length);
if (m_IsFileUpload)
{
if (m_EndBoundaryIndex < 0)
{
m_EndBoundaryIndex = StreamHelper.LastIndexOf(m_TempBuffer.RawBytes, m_TempBuffer.Count, m_EndBoundaryBytes);
if (!IsLargeFileUpload && m_EndBoundaryIndex > -1)
{
m_EndBoundaryIndex = (m_FullBuffer.Length + length) - (m_TempBuffer.Count - m_EndBoundaryIndex);
}
}
if (m_StartBoundaryIndex < 0)
{
m_StartBoundaryIndex = StreamHelper.IndexOf(m_FullBuffer, m_StartBoundaryBytes);
if (m_StartBoundaryIndex > -1)
{
m_StartBoundaryIndex = StreamHelper.IndexOf(m_FullBuffer, Encoding.UTF8.GetBytes("\r\n\r\n"), m_StartBoundaryIndex + m_StartBoundaryBytes.Length) + 4;
}
}
}
if (m_StartBoundaryIndex == -1 || !IsLargeFileUpload) // if this is not a file upload because no start boundary has been found then write buffer to memory
{
m_FullBuffer.Write(newBuffer, 0, length);
}
else
{
if (!m_FileCreated) // we have never written to the file, dump the contents of the full buffer now
{
bool exists = true;
while (exists)
{
m_TempFilename = Config.StaticConfig.TempFolder + "/" + Path.GetRandomFileName();
exists = File.Exists(m_TempFilename);
}
m_FileStream = new FileStream(m_TempFilename, FileMode.Create, FileAccess.Write);
m_FullBuffer.Position = m_StartBoundaryIndex;
m_FullBuffer.CopyTo(m_FileStream);
m_FileStream.Write(newBuffer, 0, length);
m_FileCreated = true;
}
else // we have previously written to the file, append new bytes
{
if (m_EndBoundaryIndex == -1)
{
m_FileStream.Write(newBuffer, 0, length);
}
else
{
m_FileStream.Write(newBuffer, 0, length - m_EndBoundaryBytes.Length);
}
}
}
}
private void NormalRead()
{
try
{
int bufferSize = m_Buffer.Length;
int bytesRead = m_Client.Client.Receive(m_Buffer, bufferSize, 0);
while (bytesRead > 0 && !m_IsDisposed)
{
m_LastRead = DateTime.Now;
m_BytesRead += bytesRead;
if (!m_HasHeaders || m_RequestType == RequestType.GET)
{
string sBuffer = Encoding.ASCII.GetString(m_Buffer, 0, bytesRead);
m_ReadData += sBuffer;
}
AppendBuffer(m_Buffer, bytesRead);
m_HasHeaders = UpdateUniqueHeaders();
if (!m_HasHeaders && m_BytesRead > 1024)
{
Program.BlacklistIP(m_RemoteIP, m_ReadData, "No HTTP headers found in the first 1024 bytes");
return;
}
if (m_RequestType != RequestType.POST)
{
break; // process the request
}
else if (m_EndBoundaryIndex != -1)
{
break; // process the request, we found our end boundary for posted data
}
bytesRead = m_Client.Client.Receive(m_Buffer, bufferSize, 0);
}
ProcessRequest(m_ReadData);
}
catch (Exception e)
{
// report error
}
}
private void SslRead(IAsyncResult ar)
{
if (m_IsDisposed) return;
try
{
int byteCount = -1;
int bufferSize = m_Buffer.Length;
m_LastRead = DateTime.Now;
byteCount = m_NetworkStream.EndRead(ar);
m_BytesRead += byteCount;
if (!m_HasHeaders || m_RequestType == RequestType.GET)
{
string sBuffer = Encoding.ASCII.GetString(m_Buffer, 0, byteCount);
m_ReadData += sBuffer;
}
AppendBuffer(m_Buffer, byteCount);
m_HasHeaders = UpdateUniqueHeaders();
if (!m_HasHeaders && m_BytesRead > 1024)
{
Program.BlacklistIP(m_RemoteIP, m_ReadData, "No HTTP headers found in the first 1024 bytes");
return;
}
if (byteCount > 0)
{
if (m_RequestType != RequestType.POST && m_RequestType != RequestType.None)
{
m_NetworkStream.BeginRead(m_Buffer, 0, bufferSize, new AsyncCallback(SslRead), m_NetworkStream);
}
else if (m_EndBoundaryIndex == -1) // as long as we haven't found the end of the stream continue reading
{
m_NetworkStream.BeginRead(m_Buffer, 0, bufferSize, new AsyncCallback(SslRead), m_NetworkStream);
return;
}
}
}
catch (Exception e)
{
return;
}
ProcessRequest(m_ReadData);
}
private bool UpdateUniqueHeaders()
{
if (m_RequestType == RequestType.None && m_ReadData.Length > 8)
{
m_RequestType = (m_ReadData.StartsWith("GET ") ? RequestType.GET : m_RequestType);
m_RequestType = (m_ReadData.StartsWith("POST ") ? RequestType.POST : m_RequestType);
m_RequestType = (m_ReadData.StartsWith("OPTIONS ") ? RequestType.OPTIONS : m_RequestType);
}
if (m_RequestType == RequestType.GET || m_RequestType == RequestType.POST)
{
string request = m_ReadData;
if (string.IsNullOrEmpty(m_HttpVersion)) m_HttpVersion = m_ReadData.Substring(request.IndexOf("HTTP", 1), 8);
if (string.IsNullOrEmpty(m_ContentType)) m_ContentType = GetHeader(request, "Content-Type");
if (m_ContentLength == 0)
{
int cLength = 0;
int.TryParse(GetHeader(request, "Content-Length"), out cLength);
m_ContentLength = cLength;
if (m_ContentLength / 1024 / 1024 > 20)
{
IsLargeFileUpload = true; // data is sent directly to a file instead of saving in memory
}
}
}
if (m_RequestType != RequestType.None && !string.IsNullOrEmpty(m_HttpVersion) && (!string.IsNullOrEmpty(m_ContentType) || m_RequestType != RequestType.POST))
{
if (m_RequestType == RequestType.POST)
{
try
{
if (m_IsFileUpload == false)
{
m_IsFileUpload = Segments[1].Replace("/", "") == "upload";
}
}
catch { }
if (m_RequestType == RequestType.POST && m_StartBoundaryBytes == null)
{
m_StartBoundaryBytes = Encoding.ASCII.GetBytes(GetStartBoundary());
m_EndBoundaryBytes = Encoding.ASCII.GetBytes(GetEndBoundary());
}
}
if (string.IsNullOrEmpty(m_Request) && m_Segments.Length <= 1 && m_QueryString == null)
{
// Extract the Requested Type and Requested file/directory
string m_Request = m_ReadData.Substring(0, m_ReadData.IndexOf("HTTP", 1) - 1);
//Replace backslash with Forward Slash, if Any
m_Request = m_Request.Replace("\\", "/");
m_Request = m_Request.Replace("GET ", "");
m_Request = m_Request.Replace("POST ", "");
Uri uri = new Uri("http://localhost" + m_Request);
NameValueCollection query = HttpUtility.ParseQueryString(uri.Query);
//SendHeader(sHttpVersion, "image/jpeg", Program.BlankImageBuffer.Length, " 200 OK");
m_AbsoluteURI = m_Request;
m_Segments = uri.Segments;
m_QueryString = query;
}
if (m_RequestType != RequestType.POST)
{
return true;
}
else if (m_ContentLength > 0 && m_EndBoundaryBytes != null)
{
return true;
}
}
return false;
}
private string GetStartBoundary()
{
return "--" + m_ContentType.Split(';')[1].Split('=')[1];
}
private string GetEndBoundary()
{
return "--" + m_ContentType.Split(';')[1].Split('=')[1] + "--\r\n";
}
private string GetHeader(string request, string key)
{
string result = string.Empty;
int iStartPos = request.IndexOf(key + ":", 0) + key.Length + 1;
if (request.IndexOf(key + ":", 0) > -1)
{
// Get the HTTP text and version e.g. it will return "HTTP/1.1"
int iEndPos = request.IndexOf("\r\n", iStartPos);
result = request.Substring(iStartPos, iEndPos - iStartPos).Trim();
}
return result;
}
private void CleanFile()
{
try
{
if (!string.IsNullOrEmpty(m_TempFilename) && File.Exists(m_TempFilename))
{
using (Stream stream = File.Open(m_TempFilename, FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[1024];
stream.Read(buffer, 0, buffer.Length);
//stream.Position = 0;
//stream.Write(data, 0, data.Length);
}
}
}
catch { }
}
private void ProcessRequest(string request)
{
try
{
if (request.Length < 5) return;
List<string> headers = null;
if (request.StartsWith("OPTIONS"))
{
headers = GetCommonHeader("", 0);
headers.Add("Access-Control-Allow-Credentials: true");
headers.Add("Access-Control-Allow-Headers: Authorization, X-Mashape-Authorization, Accept, Content-Type, X-Requested-With, X-PINGOTHER, X-File-Name, Cache-Control");
headers.Add("Access-Control-Allow-Methods: PUT, POST, GET, OPTIONS");
headers.Add("Keep-Alive: timeout=15,max=100");
headers.Add("Access-Control-Allow-Origin: *");
headers.Add("Connection: close");
SendHeader(headers);
return;
}
UpdateUniqueHeaders();
CleanFile();
CloseFile();
if (m_Timer_Check != null) m_Timer_Check.Dispose();
string responseText = Program.ProcessRequest(this);
if (string.IsNullOrEmpty(responseText)) responseText = "\r\n";
byte[] buf = Encoding.ASCII.GetBytes(responseText);
headers = GetCommonHeader("text/html", buf.Length, " 200 OK");
headers.Add("Access-Control-Allow-Origin: *");
SendHeaderAndData(headers, buf);
}
catch (Exception e) { }
finally
{
Dispose();
}
}
private void CloseFile()
{
try
{
if (m_FileStream != null)
{
m_FileStream.Dispose();
m_FileStream = null;
}
}
catch { }
}
/// <summary>
/// This function send the Header Information to the client (Browser)
/// </summary>
/// <param name="sHttpVersion">HTTP Version</param>
/// <param name="sMIMEHeader">Mime Type</param>
/// <param name="iTotBytes">Total Bytes to be sent in the body</param>
/// <param name="mySocket">Socket reference</param>
/// <returns></returns>
public List<string> GetCommonHeader(string mimeHeader = "text/html", int length = -1, string sStatusCode = " 200 OK", string filename = "", bool chunked = false)
{
// if Mime type is not provided set default to text/html
List<string> headers = new List<string>();
headers.Add(m_HttpVersion + sStatusCode);
headers.Add("Server: ExampleTcpWebServer");
if (!string.IsNullOrEmpty(mimeHeader))
{
headers.Add("Content-Type: " + mimeHeader);
}
if (length > -1)
{
headers.Add("Content-Length: " + length);
}
headers.Add("Date: " + DateTime.Now.ToUniversalTime().ToString("ddd, d MMM yyyy HH:mm:ss") + " GMT");
if (!string.IsNullOrEmpty(filename))
{
headers.Add("Content-Disposition: attachment; filename=\"" + filename + "\"");
}
if (chunked)
{
headers.Add("Transfer-Encoding: chunked");
}
return headers;
}
public void SendHeader(List<string> headers)
{
string sHeader = string.Empty;
foreach (string header in headers)
{
sHeader += header + "\r\n";
}
sHeader += "\r\n";
byte[] bSendData = Encoding.ASCII.GetBytes(sHeader);
SendToBrowser(bSendData, bSendData.Length);
}
public void SendHeaderAndData(List<string> headers, byte[] data)
{
string sHeader = string.Empty;
foreach (string header in headers)
{
sHeader += header + "\r\n";
}
sHeader += "\r\n";
byte[] bHeader = Encoding.ASCII.GetBytes(sHeader);
byte[] combined = new byte[bHeader.Length + data.Length];
Array.Copy(bHeader, combined, bHeader.Length);
Array.Copy(data, 0, combined, bHeader.Length, data.Length);
SendToBrowser(combined, combined.Length);
}
/// <summary>
/// Sends data to the browser (client)
/// </summary>
/// <param name="bSendData">Byte Array</param>
/// <param name="mySocket">Socket reference</param>
public void SendToBrowser(byte[] bSendData, int length)
{
try
{
if (Common.TcpHelper.SocketConnected(m_Client.Client))
{
if (m_IsSSL)
{
m_NetworkStream.Write(bSendData, 0, length);
}
else
{
m_Client.Client.Send(bSendData, length, 0);
}
}
else
{
Dispose();
}
}
catch (Exception e)
{
//Console.WriteLine("Error Occurred : {0} ", e);
}
}
#endregion private methods
#region IDisposable
public void Dispose()
{
if (!m_IsDisposed)
{
m_IsDisposed = true;
try
{
if (!string.IsNullOrEmpty(m_TempFilename) && File.Exists(m_TempFilename))
{
File.Delete(m_TempFilename);
}
}
catch { }
CloseFile();
try
{
m_Client.Client.Close(5);
m_Client.Close();
m_Client.Client.Dispose();
}
catch { }
try
{
m_NetworkStream.Dispose();
}
catch { }
try
{
if (Thread.CurrentThread != m_Thread_Read && m_Thread_Read.IsAlive)
{
m_Thread_Read.Join(1000);
if (m_Thread_Read.IsAlive) m_Thread_Read.Abort();
}
}
catch { }
try
{
m_ReadData = null;
m_PostData = null;
m_Buffer = null;
m_TempBuffer = null;
if (m_FullBuffer != null) m_FullBuffer.Dispose();
if (m_Timer_Check != null) m_Timer_Check.Dispose();
m_Timer_Check = null;
}
catch { }
}
}
#endregion IDisposable
}
}
I wrote code to send an SMS using my GSM phone which is attached to the computer through COM port. The code is below.
The problem is I do see that it is in the outbox of the phone and it actually appears to have been sent, but when I contact the recipient they say that I have not received the message.
I test the phone, and I create and send a message using only the phone and it works perfectly. However, when I do this with my code, it APPEARS to have been sent, and I am getting all the correct AT COMMAND responses from the phone, but the message is actually NOT sent.
Here is the code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.IO.Ports;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
SerialPort serialPort1;
int m_iTxtMsgState = 0;
const int NUM_MESSAGE_STATES = 4;
const string RESERVED_COM_1 = "COM1";
const string RESERVED_COM_4 = "COM4";
public Form1()
{
InitializeComponent();
this.Closing += new CancelEventHandler(Form1_Closing);
}
private void Form1_Load(object sender, EventArgs e)
{
serialPort1 = new SerialPort(GetUSBComPort());
if (serialPort1.IsOpen)
{
serialPort1.Close();
}
serialPort1.Open();
//ThreadStart myThreadDelegate = new ThreadStart(ReceiveAndOutput);
//Thread myThread = new Thread(myThreadDelegate);
//myThread.Start();
this.serialPort1.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
}
private void Form1_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
serialPort1.Close();
}
private void SendLine(string sLine)
{
serialPort1.Write(sLine);
sLine = sLine.Replace("\u001A", "");
consoleOut.Text += sLine;
}
public void DoWork()
{
ProcessMessageState();
}
public void ProcessMessageState()
{
switch (m_iTxtMsgState)
{
case 0:
m_iTxtMsgState = 1;
SendLine("AT\r\n"); //NOTE: SendLine must be the last thing called in all of these!
break;
case 1:
m_iTxtMsgState = 2;
SendLine("AT+CMGF=1\r\n");
break;
case 2:
m_iTxtMsgState = 3;
SendLine("AT+CMGW=" + Convert.ToChar(34) + "+9737387467" + Convert.ToChar(34) + "\r\n");
break;
case 3:
m_iTxtMsgState = 4;
SendLine("A simple demo of SMS text messaging." + Convert.ToChar(26));
break;
case 4:
m_iTxtMsgState = 5;
break;
case 5:
m_iTxtMsgState = NUM_MESSAGE_STATES;
break;
}
}
private string GetStoredSMSID()
{
return null;
}
/* //I don't think this part does anything
private void serialPort1_DataReceived_1(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
string response = serialPort1.ReadLine();
this.BeginInvoke(new MethodInvoker(() => textBox1.AppendText(response + "\r\n")));
}
*/
void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
Thread.Sleep(500);
char[] msg;
msg = new char[613];
int iNumToRead = serialPort1.BytesToRead;
serialPort1.Read(msg, 0, iNumToRead);
string response = new string(msg);
this.BeginInvoke(new MethodInvoker(() => textBox1.AppendText(response + "\r\n")));
serialPort1.DiscardInBuffer();
if (m_iTxtMsgState == 4)
{
int pos_cmgw = response.IndexOf("+CMGW:");
string cmgw_num = response.Substring(pos_cmgw + 7, 4);
SendLine("AT+CMSS=" + cmgw_num + "\r\n");
//stop listening to messages received
}
if (m_iTxtMsgState < NUM_MESSAGE_STATES)
{
ProcessMessageState();
}
}
catch
{ }
}
private void button1_Click(object sender, EventArgs e)
{
m_iTxtMsgState = 0;
DoWork();
}
private void button2_Click(object sender, EventArgs e)
{
string[] sPorts = SerialPort.GetPortNames();
foreach (string port in sPorts)
{
consoleOut.Text += port + "\r\n";
}
}
private string GetUSBComPort()
{
string[] sPorts = SerialPort.GetPortNames();
foreach (string port in sPorts)
{
if (port != RESERVED_COM_1
&& port != RESERVED_COM_4)
{
return port;
}
}
return null;
}
}
This is my code that works (tested) with U9 Telia cellular modem:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Configuration;
using UsbEject.Library;
using Utils;
namespace Hardware
{
public class TeliaModem : IDisposable
{
public delegate void NewSmsHandler(InboundSMS sms);
public event NewSmsHandler OnNewSMS;
#region private data
System.IO.Ports.SerialPort modemPort;
Timeouter _lastModemKeepAlive;
private delegate void DataReceivedDelegate();
private DataReceivedDelegate dataReceivedDelegate;
Queue<OutboundSMS> _outSmses = new Queue<OutboundSMS>();
enum ModemState
{
Error = -1, NotInitialized, PowerUp, Initializing, Idle,
SendingSMS, KeepAliveAwaitingResponse
};
Timeouter ModemStateTimeouter = new Timeouter(timeout_s: 10, autoReset: false);
ModemState _modemState = ModemState.NotInitialized;
ModemState CurrentModemState
{
get
{
return _modemState;
}
set
{
_modemState = value;
ModemStateTimeouter.Reset();
NihLog.Write(NihLog.Level.Debug, "State changed to: " + value.ToString());
}
}
private System.Windows.Forms.Control _mainThreadOwnder;
#endregion
public TeliaModem(System.Windows.Forms.Control mainThreadOwnder)
{
_mainThreadOwnder = mainThreadOwnder;
dataReceivedDelegate = new DataReceivedDelegate(OnDataReceived);
}
public void SendSMS(string phone, string text)
{
_outSmses.Enqueue(new OutboundSMS(phone, text));
HeartBeat();
}
private void SendSmsNow()
{
OutboundSMS sms = _outSmses.Peek();
sms.Attempt++;
if (sms.Attempt > sms.MaxTries)
{
NihLog.Write(NihLog.Level.Error, "Failure to send after " + sms.MaxTries + " tries");
_outSmses.Dequeue();
return;
}
NihLog.Write(NihLog.Level.Info, "Sending SMS: " + sms.ToString());
WriteToModem("AT+CMGS=\"" + sms.Destination + "\"\r");
System.Threading.Thread.Sleep(500);
WriteToModem(sms.Text);
byte[] buffer = new byte[1];
buffer[0] = 26; // ^Z
modemPort.Write(buffer, offset:0, count:1);
CurrentModemState = ModemState.SendingSMS;
}
public void Dispose()
{
UninitModem();
}
public void HeartBeat()
{
if (CurrentModemState == ModemState.NotInitialized)
{
TryInitModem();
return;
}
if (IsTransitionalState(CurrentModemState) && ModemStateTimeouter.IsTimedOut())
{
NihLog.Write(NihLog.Level.Error, "Modem error. Timed out during " + CurrentModemState);
CurrentModemState = ModemState.Error;
return;
}
if (CurrentModemState == ModemState.Idle && _lastModemKeepAlive.IsTimedOut())
{
// Send keepalive
WriteToModem("AT\r");
CurrentModemState = ModemState.KeepAliveAwaitingResponse;
return;
}
if (CurrentModemState == ModemState.Error)
{
NihLog.Write(NihLog.Level.Debug, "Reenumerating modem...");
UninitModem();
return;
}
if (_outSmses.Count != 0 && CurrentModemState == ModemState.Idle)
{
SendSmsNow();
return;
}
}
private string pendingData;
private void OnDataReceived()
{
// Called in the main thread
string nowWhat = modemPort.ReadExisting();
pendingData += nowWhat;
string[] lines = pendingData.Split(new string[] { "\r\n" }, StringSplitOptions.None);
if (lines.Length == 0)
{
pendingData = string.Empty;
return;
}
else
{
pendingData = lines[lines.Length - 1];
}
// This happens in main thread.
for (int i = 0; i < lines.Length - 1; i++)
{
string line = lines[i];
if (line.Length >= 5 && line.Substring(0, 5) == "+CMT:")
{
// s+= read one more line
if (i == lines.Length - 1) // no next line
{
pendingData = line + "\r\n" + pendingData; // unread the line
continue;
}
string line2 = lines[++i];
NihLog.Write(NihLog.Level.Debug, "RX " + line);
NihLog.Write(NihLog.Level.Debug, "RX " + line2);
InboundSMS sms = new InboundSMS();
sms.ParseCMT(line, line2);
if(OnNewSMS != null)
OnNewSMS(sms);
}
else // Not a composite
NihLog.Write(NihLog.Level.Debug, "RX " + line);
if (line == "OK")
{
OnModemResponse(true);
}
else if (line == "ERROR")
{
OnModemResponse(false);
NihLog.Write(NihLog.Level.Error, "Modem error");
}
}
}
private void ProcessSmsResult(bool ok)
{
if (!ok)
{
OutboundSMS sms = _outSmses.Peek();
if (sms.Attempt < sms.MaxTries)
{
NihLog.Write(NihLog.Level.Info, "Retrying sms...");
return;
}
NihLog.Write(NihLog.Level.Error, "Failed to send SMS: " + sms.ToString());
}
_outSmses.Dequeue();
}
private void OnModemResponse(bool ok)
{
if (CurrentModemState == ModemState.SendingSMS)
ProcessSmsResult(ok);
if (!ok)
{
NihLog.Write(NihLog.Level.Error, "Error during state " + CurrentModemState.ToString());
CurrentModemState = ModemState.Error;
return;
}
switch (CurrentModemState)
{
case ModemState.NotInitialized:
return;
case ModemState.PowerUp:
WriteToModem("ATE0;+CMGF=1;+CSCS=\"IRA\";+CNMI=1,2\r");
CurrentModemState = ModemState.Initializing;
break;
case ModemState.Initializing:
case ModemState.SendingSMS:
case ModemState.KeepAliveAwaitingResponse:
CurrentModemState = ModemState.Idle;
break;
}
}
private void CloseU9TelitNativeApp()
{
bool doneSomething;
do
{
doneSomething = false;
Process[] processes = Process.GetProcessesByName("wirelesscard");
foreach (Process p in processes)
{
p.CloseMainWindow();
doneSomething = true;
NihLog.Write(NihLog.Level.Info, "Killed native U9 app");
System.Threading.Thread.Sleep(1000); // Will not wait if no native app is started
}
} while (doneSomething);
}
void WriteToModem(string s)
{
modemPort.Write(s);
NihLog.Write(NihLog.Level.Debug, "TX " + s);
}
void UninitModem()
{
if (modemPort != null)
modemPort.Dispose();
modemPort = null;
CurrentModemState = ModemState.NotInitialized;
}
private bool IsTransitionalState(ModemState ms)
{
return ms == ModemState.Initializing
|| ms == ModemState.SendingSMS
|| ms == ModemState.PowerUp
|| ms == ModemState.KeepAliveAwaitingResponse;
}
Timeouter _initFailureTimeout =
new Timeouter(timeout_s: 10, autoReset:false, timedOut:true);
void TryInitModem()
{
// Try pistoning the modem with higher frequency. This does no harm (such as redundant logging)
PrepareU9Modem(); // Will do nothing if modem is okay
if (!_initFailureTimeout.IsTimedOut())
return; // Don't try too frequently
if (modemPort != null)
return;
const string modemPortName = "Basecom HS-USB NMEA 9000";
const int speed = 115200;
string portName = Hardware.Misc.SerialPortFromFriendlyName(modemPortName);
if (portName == null)
{
NihLog.Write(NihLog.Level.Error, "Modem not found (yet). ");
_initFailureTimeout.Reset();
return;
}
NihLog.Write(NihLog.Level.Info, string.Format("Found modem port \"{0}\" at {1}", modemPortName, portName));
modemPort = new System.IO.Ports.SerialPort(portName, speed);
modemPort.ReadTimeout = 3000;
modemPort.NewLine = "\r\n";
modemPort.Open();
modemPort.DiscardInBuffer();
modemPort.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(delegate { _mainThreadOwnder.Invoke(dataReceivedDelegate); }); // called in different thread!
_lastModemKeepAlive = new Timeouter(60, true);
WriteToModem("AT+CFUN=1\r");
CurrentModemState = ModemState.PowerUp;
}
void CheatU9Telit()
{
// U9 telit appears as USB CDrom on the bus, until disk eject is sent.
// Then, it reappears as normal stuff.
VolumeDeviceClass volumeDeviceClass = new VolumeDeviceClass();
foreach (Volume device in volumeDeviceClass.Devices)
{
if (device.FriendlyName == "PCL HSUPA Modem USB Device")
{
NihLog.Write(NihLog.Level.Info, "Trying to initialize: " + device.FriendlyName);
device.EjectCDRomNoUI();
}
}
}
void PrepareU9Modem()
{
CloseU9TelitNativeApp(); // Closes the autorun native app
CheatU9Telit();
}
}
public class OutboundSMS
{
public string Destination;
public string Text;
public int MaxTries;
public int Attempt = 0;
public OutboundSMS(string dest, string txt)
{
Destination = dest;
Text = txt;
MaxTries = 3;
}
override public string ToString()
{
if(Attempt > 1)
return string.Format("\"{0}\" to {1}, attempt {2}",
Text, Destination, Attempt);
else
return string.Format("\"{0}\" to {1}",
Text, Destination);
}
}
public class InboundSMS
{
public string SourcePhone;
public DateTime ReceiveTime;
public string Text;
public void ParseCMT(string line1, string line2)
{
string[] fields = line1.Split(new char[] { ',', ' ', '/', ':', '"' });
if (fields.Length < 12)
throw new ApplicationException("CMT message too short. Expected 2 fields");
SourcePhone = fields[3];
ReceiveTime = DateTime.Parse("20" + fields[7] + "-" + fields[8] + "-" + fields[9] + " " + fields[10] + ":" + fields[11] + ":" + fields[12].Substring(0, 2)); //test carefully
Text = line2;
}
};
}
Usage example in a winforms application:
Hardware.TeliaModem _modem;
public Form1()
{
InitializeComponent();
_modem = new Hardware.TeliaModem(this);
_modem.OnNewSMS += new Hardware.TeliaModem.NewSmsHandler(ProcessNewSMS);
_timer.Interval = 1000; // milliseconds
_timer.Tick += new EventHandler(OnTimerTick);
_timer.Start();
}
To send an SMS:
_modem.SendSMS(sms.SourcePhone, "Aircon is now " + BoolToString.ON_OFF(_aircon.On));
On timer event, once every second:
private void OnTimerTick(object o, EventArgs e)
{
_modem.HeartBeat();
}
This is registered as Winforms timer handler, so that modem will not have a thread.
Some utility classes used:
namespace Utils
{
public class StopWatch
{
protected DateTime _resetTime;
public StopWatch() { Reset(); }
public void Reset() { _resetTime = DateTime.Now; }
public double TotalSeconds { get { return (DateTime.Now - _resetTime).TotalSeconds; } }
public TimeSpan Time { get { return DateTime.Now - _resetTime; } }
};
public class Timeouter : StopWatch
{
private bool _autoReset;
private double _timeout_s;
public Timeouter(double timeout_s, bool autoReset, bool timedOut = false)
{
_timeout_s = timeout_s;
_autoReset = autoReset;
if (timedOut)
_resetTime -= TimeSpan.FromSeconds(_timeout_s + 1); // This is surely timed out, as requested
}
public bool IsTimedOut()
{
if (_timeout_s == double.PositiveInfinity)
return false;
bool timedout = this.TotalSeconds >= _timeout_s;
if (timedout && _autoReset)
Reset();
return timedout;
}
public void Reset(double timeout_s) { _timeout_s = timeout_s; Reset(); }
public double TimeLeft { get { return _timeout_s - TotalSeconds; } }
}
}
I implemented this in order to turn on air conditioner with SMS (yeah, I live in a hot country). It works. Please feel free to use any of this code.
Enjoy.
Microsoft provides a pretty decent VB.Net example in a KB article "How to access serial and parallel ports by using Visual Basic .NET". I'm not sure what your phone returns, however, if you change the buffer loop in the example, you can at least print out the reply:
MSComm1.Output = "AT" & Chr(13)
Console.WriteLine("Send the attention command to the modem.")
Console.WriteLine("Wait for the data to come back to the serial port...")
' Make sure that the modem responds with "OK".
' Wait for the data to come back to the serial port.
Do
Buffer = Buffer & MSComm1.Input
Console.WriteLine("Found: {0}", Buffer)
Loop Until InStr(Buffer, "OK" & vbCrLf)
GSMComm is a C# library that will allow you to easily interact with a GSM mobile in text or PDU mode. (The site also has a bunch of testing utilities)
Working with GSM modem to send SMS has never been a good idea as such. Here's a checklist to make sure your SMS is sent out properly.
You are sending the SMS in text mode. Try sending it in PDU mode.
Try this library http://phonemanager.codeplex.com/
Do you change SMSC number before sending out the SMS, if so make sure the SMSC is correct.
Also make sure your SIM card has enough credits to let you send outbound SMS.
Check the network status especially when you're sending out SMS from your program. This might be a problem.
I recommend you checkout the Lite edition of NowSMS gateway that comes for free and lets you work with GSM modem very continently. NowSMS will give you api abstraction over a GSM modem connection, so you wil just need to call the Http Api of NowSMS gateway, rest will be taken care by the NowSMS gateway.
Above all this, if your end-users are going to remain internet connected while using your application or if your application is web-based and hosted over internet, I strongly recoomend to do away with GSM modem option and opt-in for reliable Http SMS gateway service providers.