C# WebSockets On Windows 7 with OWIN - c#

I am trying to use WebSockets on Windows 7. I trying to use System.Net.Sockets, I have tried following these examples, but each of them has one problem or another running on Windows 7:
WebSocket Server in C# ,Writing a WebSocket server in C# ,
Creating a “Hello World” WebSocket example , How to implement an asynchronous socket in C#, MULTI-THREADED TCP SERVER IN C# ,Paul Batum
I have looked into several 3rd party tools, but it looks like they are either no longer supported or in beta.
Here's the code I have so far:
public class WebSocketService {
private TcpListener _listener;
private TcpClient _tcpClient;
public event Action<NetworkStream> OnLoadData;
public WebSocketService() {
Listen();
}
public void Listen(int port) {
_listener = new TcpListener(IPAddress.Parse("127.0.0.1"), port);
_listener.Start();
_listener.BeginAcceptTcpClient(HandleAsyncConnection, null);
}
private void HandleAsyncConnection(IAsyncResult result)
{
try
{
_tcpClient = _listener.EndAcceptTcpClient(result);
_tcpClient.NoDelay = true;
NetworkStream stream = _tcpClient.GetStream();
// extract the connection details and use those details to build a connection
ConnectionDetails connectionDetails = GetConnectionDetails(stream, _tcpClient);
PerformHandshake(stream, connectionDetails.Header);
if (connectionDetails.Path.Equals("/myApiPath"))
{
OnLoadData?.Invoke(stream);
}
}
catch (Exception ex)
{
//write ex to log
throw;
}
}
private static ConnectionDetails GetConnectionDetails(NetworkStream stream, TcpClient tcpClient)
{
// read the header and check that it is a GET request
string header = HttpHelper.ReadHttpHeader(stream);
var getRegex = new Regex(#"^GET(.*)HTTP\/1\.1", RegexOptions.IgnoreCase);
Match getRegexMatch = getRegex.Match(header);
if (getRegexMatch.Success)
{
// extract the path attribute from the first line of the header
string path = getRegexMatch.Groups[1].Value.Trim();
// check if this is a web socket upgrade request
var webSocketUpgradeRegex = new Regex("Upgrade: websocket", RegexOptions.IgnoreCase);
Match webSocketUpgradeRegexMatch = webSocketUpgradeRegex.Match(header);
if (webSocketUpgradeRegexMatch.Success)
{
return new ConnectionDetails(stream, tcpClient, path, ConnectionType.WebSocket, header);
}
return new ConnectionDetails(stream, tcpClient, path, ConnectionType.Http, header);
}
return new ConnectionDetails(stream, tcpClient, string.Empty, ConnectionType.Unknown, header);
}
private void PerformHandshake(Stream stream, string header)
{
try
{
Regex webSocketKeyRegex = new Regex("Sec-WebSocket-Key: (.*)");
Regex webSocketVersionRegex = new Regex("Sec-WebSocket-Version: (.*)");
// check the version. Support version 13 and above
const int webSocketVersion = 13;
int secWebSocketVersion = Convert.ToInt32(webSocketVersionRegex.Match(header).Groups[1].Value.Trim());
if (secWebSocketVersion < webSocketVersion)
{
//throw some exception
}
string secWebSocketKey = webSocketKeyRegex.Match(header).Groups[1].Value.Trim();
string setWebSocketAccept = ComputeSocketAcceptString(secWebSocketKey);
var newLine = "\r\n";
string response = ("HTTP/1.1 101 Switching Protocols" + newLine
+ "Connection: Upgrade" + newLine
+ "Upgrade: websocket" + newLine
+ "Sec-WebSocket-Accept: " + setWebSocketAccept + newLine + newLine);
WriteHttpHeader(response, stream);
}
catch (Exception ex)
{
// Write Log Entry(ex);
WriteHttpHeader("HTTP/1.1 400 Bad Request", stream);
throw;
}
}
}
When the OnLoadData event fires it calls another class which simply has:
var jsonSerializerSettings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
var message = JsonConvert.SerializeObject(someData, jsonSerializerSettings);
var messageData = Encoding.UTF8.GetBytes(message);
_clientStream.Write(messageData, 0, messageData.Length);
_clientStream.Flush();
I then get this error:
An established connection was aborted by the software in your host
machine at System.Net.Sockets.Socket.Send(Byte[] buffer, Int32
offset, Int32 size, SocketFlags socketFlags) at
System.Net.Sockets.NetworkStream.Write(Byte[] buffer, Int32 offset,
Int32 size)
NativeErrorCode 10053 int
SocketErrorCode ConnectionAborted System.Net.Sockets.SocketError
I am looking for a simple C# WebSockets implementation that works on Windows 7. I cannot use SignalR or ASP.NET because I am using OWIN to self-host Web API.
I took a look at this post, but I can't figure out how the OP made it work.
Anyone know how to do this?

Related

TcpListener write to ClientWebSocket

Im currently creating a project that will run in a browser & has an c# server connected.
The Server uses an TcpListener to accept connections & receive messages, but I want the server to be able to respond to the client. This has given me a few issues.
Here is the code for my client:
private ClientWebSocket socket;
internal async Task InitAsync(string host, int port, GamePacketParser parser)
{
Logger.Info("Setting up the socket connection...");
socket = new ClientWebSocket();
await socket.ConnectAsync(new Uri($"ws://{host}:{port}/"), CancellationToken.None);
Logger.Info("Successfully established the connection.");
this.parser = parser;
buffer = new byte[GameSocketManagerStatics.BUFFER_SIZE];
Task.Run(recieve);
}
private async Task recieve()
{
Logger.Debug("Starting Reciever.....");
var result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
var packet = new byte[result.Count];
Array.Copy(buffer, packet, result.Count);
///parser.handlePacketData(packet);
Logger.Debug($"Recieved: {Encoding.UTF8.GetString(packet)}");
///Task.Run(recieve); //Start receiving again
}
public async Task SendData(byte[] data)
{
Logger.Debug("Triggerd send");
string packet = BitConverter.ToString(data);
await socket.SendAsync(new ArraySegment<byte>(data), WebSocketMessageType.Text, true, CancellationToken.None);
Logger.Info($"Sended Data: {packet}");
}
The code above simply connects to the server over a web socket. Sending packets works fine. The second the server sends data back, the client won't send any data anymore to the server. Like its stuck.
static void Main(string[] args)
{
string ip = "127.0.0.1";
int port = 30000;
var server = new TcpListener(IPAddress.Parse(ip), port);
server.Start();
Console.WriteLine("Server has started on {0}:{1}, Waiting for a connection...", ip, port);
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("A client connected.");
NetworkStream stream = client.GetStream();
if (Regex.IsMatch(s, "^GET", RegexOptions.IgnoreCase))
{
Console.WriteLine("=====Handshaking from client=====\n{0}", s);
// 1. Obtain the value of the "Sec-WebSocket-Key" request header without any leading or trailing whitespace
// 2. Concatenate it with "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" (a special GUID specified by RFC 6455)
// 3. Compute SHA-1 and Base64 hash of the new value
// 4. Write the hash back as the value of "Sec-WebSocket-Accept" response header in an HTTP response
string swk = Regex.Match(s, "Sec-WebSocket-Key: (.*)").Groups[1].Value.Trim();
string swka = swk + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
byte[] swkaSha1 = System.Security.Cryptography.SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(swka));
string swkaSha1Base64 = Convert.ToBase64String(swkaSha1);
// HTTP/1.1 defines the sequence CR LF as the end-of-line marker
byte[] response = Encoding.UTF8.GetBytes(
"HTTP/1.1 101 Switching Protocols\r\n" +
"Connection: Upgrade\r\n" +
"Upgrade: websocket\r\n" +
"Sec-WebSocket-Accept: " + swkaSha1Base64 + "\r\n\r\n");
stream.Write(response, 0, response.Length);
}
byte[] message = Encoding.UTF8.GetBytes("Connection is established");
stream.Write(message, 0, message.Length);
}
The problem is probably because it is not encoded for WebSockets, but I tried a lot of online solutions for encoding (For example: How can I send and receive WebSocket messages on the server side?) But even with those encoders, it did not seem to solve the problem.
Thanks for your help in advance. Im still new to WebSockets, so spoonfeeding is allowed.
aepot your answer is a good one, but i really wanted my server on the TCP level, I would have needed to change to much code if I wanted to use it on my official server that uses sockets.
I have been doing some more digging into WebSockets, after some searching I figured it out, I basically needed to send a header before sending the data. I did not know how to create that header, but I found some code online that did. (I have been searching for about 12 hours :?)
The solution:
protected int GetHeader(bool finalFrame, bool contFrame)
{
int header = finalFrame ? 1 : 0;//fin: 0 = more frames, 1 = final frame
header = (header << 1) + 0;//rsv1
header = (header << 1) + 0;//rsv2
header = (header << 1) + 0;//rsv3
header = (header << 4) + (contFrame ? 0 : 1);//opcode : 0 = continuation frame, 1 = text
header = (header << 1) + 0;//mask: server -> client = no mask
return header;
}
protected byte[] IntToByteArray(ushort value)
{
var ary = BitConverter.GetBytes(value);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(ary);
}
return ary;
}
public static IEnumerable<string> SplitInGroups(this string original, int size)
{
var p = 0;
var l = original.Length;
while (l - p > size)
{
yield return original.Substring(p, size);
p += size;
}
yield return original.Substring(p);
}
public static void SendMessage(string packet) {
Queue<string> que = new Queue<string>(packet.SplitInGroups(125)); //Make it so the message is never longer then 125 (Split the message into parts & store them in a queue)
int len = que.Count;
while (que.Count > 0)
{
var header = GetHeader(
que.Count > 1 ? false : true,
que.Count == len ? false : true
); //Get the header for a part of the queue
byte[] list = Encoding.UTF8.GetBytes(qui.Dequeue()); //Get part of the message out of the queue
header = (header << 7) + list.Length; //Add the length of the part we are going to send
//Send the header & message to client
stream.write(IntToByteArray((ushort)header));
stream.write(list);
}
}
I have not coded this myself, but sadly I cant find the link where I got it from to credit the person who did.

android aQuery post send images to server C#

Im trying to send several images from android client to C# server via http post with AQuery library.
urlport="http://192.168.100.139:1111";
Map<String, Object> params = new HashMap<>();
params.put("testimage", new File(getRealPathFromURI(imageUris.get(0))) );
aQuery
.ajax(urlport,params,JSONObject.class,new AjaxCallback<JSONObject>(){
#Override
public void callback(String url, JSONObject json, AjaxStatus status){
alert("post complete");//not working
}
});
C# HTTP Listener
lstnr = new Server();
lstnr.url = "http://" + ipbox.Text;
lstnr.port = portfield.Text;
lstnr.Received += Server_Received_multipart;
lstnr.StartListen();
in Server.class I set delegate that runs in receiving loop, accepts Stream
class Server{
......
public event EventHandler<Stream> Received = delegate { };
......
public void Start(){
string prefix = String.Format("{0}:{1}/", url, port);
listener.Prefixes.Add(prefix);
if (!listener.IsListening) {
listener.Start();
isListening = true;
try
{
while (isListening)
{
HttpListenerContext context = listener.GetContext();
HttpListenerRequest request = context.Request;
Stream postdata = request.InputStream;
Received(this, postdata);
}
}
catch (Exception e)
{
MessageBox.Show("Start() error: " + e.Message);
}
}
}
}
private void Server_Received_multipart(object sender, Stream msg)
{
Dispatcher.Invoke((Action)(() =>
{
//here want to handle stream and show images in window and save on disk
MessageBox.Show("message received"); //this not working too
}));
}
Before I made a fork where server accepts string with imagees in it encoded in base64/ works fine, but app crashes woth OurOfMemory error if send several images with size>3-4 Mb. Sorry if grammatic not good, Im russian

C# Socket Server Continuously Reading

I just made a C# socket server and it works perfectly, but if I would like to make it to be able to listen the inputs the program crashes. My question is: how can I make to continuously listen after the clients commands.
Here is my code:
using System.Net;
using System.Net.Sockets;
using System.IO;
namespace mc_server
{
class Program
{
private static TcpListener serverSocket;
private static TcpClient clientSocket;
private static StreamReader reader;
private static StreamWriter writer;
private static int PORT = 5000;
static void Main(string[] args)
{
Console.WriteLine(DateTime.Now.ToString() + " >> Server starting...");
StartServer();
Console.ReadKey(true);
serverSocket.Stop();
}
private static void StartServer()
{
try
{
serverSocket = new TcpListener(IPAddress.Any, PORT);
serverSocket.Start();
Console.WriteLine(DateTime.Now.ToString() + " >> Server started");
while (true)
{
clientSocket = serverSocket.AcceptTcpClient();
reader = new StreamReader(clientSocket.GetStream());
writer = new StreamWriter(clientSocket.GetStream());
StartListening();
}
}
catch (Exception e)
{
if (!System.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable())
Console.WriteLine(DateTime.Now.ToString() + " [ERR]: Internet connection unavailable! (" + e.ToString() + ")");
else
Console.WriteLine(DateTime.Now.ToString() + " [ERR]: Server can't be started! (" + e.ToString() + ")");
}
}
private static void StartListening()
{
try
{
string line;
//while (true)
//{
line = reader.ReadLine();
if (line.Contains("connected"))
{
Console.WriteLine(DateTime.Now.ToString() + " >> Remote connected");
}
else if (line.Contains("get_dir"))
{
Console.WriteLine("in_get_dir");
foreach (string directory in Directory.GetDirectories(path))
{
DirectoryInfo dir_name = new DirectoryInfo(directory);
string dirName = dir_name.Name;
writer.WriteLine(line);
}
}
//}
}
catch (Exception e)
{
Console.WriteLine(DateTime.Now.ToString() + " [ERR]: " + e.ToString() + ")");
}
}
}
}
Thank you so much for your help!
Regards, Stanley.
I just made a C# socket server and it works perfectly, but if I would like to make it to be able to listen the inputs the program crashes.
I am not sure what you meant by that, but I think you have a problem in receiving/reading the response.
serverSocket = new TcpListener(IPAddress.Any, PORT);
serverSocket.Start();
You've created a TcpListener named 'serverSocket'. Now let's create a socket that helps us send and receive data between the server and the client. Creating a Streamwriter and Streamreader is too long and complicated. You'll have to deal with flushing, disposing, and closing them.
Here's my 3 simplified guide of using Socket to communicate:
1.) Creating a Socket:
Socket socket = serverSocket.AcceptSocket(); // This is a
//synchronous function. It will not execute the next line until a client
//connects to the server
2.) Sending data:
socket.Send(Encoding.ASCII.GetBytes("I am sending a short text to
the client")); // This will send the text to client. It ONLY accepts byte
//array, therefore we have to convert it from string to byte array to be
//able
//to send it.
3.) Receiving/Reading Data:
// For Receiving Text from the Client
byte[] responseByteArray = new byte[socket.ReceiveBufferSize]; //
//This will create a byte array to store the data. socket.ReceiveBufferSize
//will tells us the length of the data sent in byte array (not string yet)
int responseLength = socket.Receive(responseByteArray); // Tells us
//the length of the response in byte array (not string yet)
string response = null; // We will create a variable 'response' to
//store the final result of the conversion
for (int i = 0; i < responseLength; i++) // Loop to convert All byte
//from byte array to string
{
response = response + (char)responseByteArray[i]; // Converts
//every single byte of character to char
}
Console.WriteLine(response); // Prints out the final result in
//string
You can use async/await keywords to read & write data asynchronously on the sockets.
That way, your input logic will stay separate. If you use async/await, you won't have to manage threads manually.

worker role with TCPListener stops working

I have this very basic worker role running, listening to a incoming TCP connections and processing them. But for some reason now fails after a while.. seems like it is no longer receiving data... no exception seen in the trace.
Now make it synchronous... still the same problem.
The idea is to accept an HTTP request and give a 302 redirection.
When I connect to it via a telnet on port 80 it works.
As soon as I use a browser, it starts failing quickly.
When I try again afterwards on port 80, no response anymore.
public class WorkerRole : RoleEntryPoint
{
public override void Run()
{
TcpListener server = null;
IPEndPoint ipin = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["Server"].IPEndpoint;
server = new TcpListener(ipin);
server.ExclusiveAddressUse = false;
server.Start();
try
{
while (true)
{
TcpClient client = server.AcceptTcpClient();
HandleSyncConnection(client);
}
}
catch (Exception ex)
{
Trace.WriteLine("Server stopped. Exception:" + ex.Message);
return;
}
}
private void HandleSyncConnection(TcpClient client)
{
try
{
// Setup reader/writer
NetworkStream netStream = client.GetStream();
StreamReader reader = new StreamReader(netStream, Encoding.ASCII);
StreamWriter writer = new StreamWriter(netStream, Encoding.ASCII);
writer.AutoFlush = true;
byte[] baBuffer = new byte[10000];
byte[] baHeader = { 13, 10, 13, 10 };
Int32 iTotalBytesReceived = 0;
Int32 iHeaderFound = 0;
while (iTotalBytesReceived < baBuffer.Length)
{
Int32 iBytesReceived = netStream.Read(baBuffer, iTotalBytesReceived, baBuffer.Length - iTotalBytesReceived);
if (iBytesReceived == 0)
break;
for (int i = 0; i < iBytesReceived; i++)
{
if (baBuffer[iTotalBytesReceived + i] == baHeader[iHeaderFound])
iHeaderFound++;
else
iHeaderFound = 0;
}
iTotalBytesReceived += iBytesReceived;
if (iHeaderFound == baHeader.Length)
break;
Thread.Sleep(50);
}
String strResponse;
Trace.TraceInformation("Request received");
strResponse = "HTTP/1.1 302 Redirect" + EOL + "Location: http://www.google.com");
strResponse += EOL + "Content-Type: text/html" + EOL + "Cache-Control: no-cache" + EOL + "Connection: close" + EOL + "Content-Length: 0" + EOL + EOL;
writer.Write(strResponse);
writer.Close();
reader.Close();
// Done!
client.Close();
}
catch (Exception e)
{
Trace.TraceError("Server stopped in the handling of sync " + e.Message);
}
}
public override bool OnStart()
{
return base.OnStart();
}
I think the problem is not on how your application is implemented.
Have you consider the Azure load balance timeout?
Here is a good blog post detailing it:
Windows Azure Load Balancer Timeout Details
Azure will stop your application after a minute or so independent of your implementation.
Take a look at that and see if that is problem that you are facing.
Regards

Server refuses to accept requests from clients

This is a program to search for strings from a file. The string required by the client is given from the client side, in my case, using telnet. The program I have written is a server side one. It accepts multiple clients.
But, the problems I am unable rectify are-
It doesn't check for strings from the file.
As soon as the client gets connected, the client cannot type in the strings they want to search in that particular file.
It doesn't send the reply back (i.e. If the string is present in the file or not) to the client. Its only shown on the server side.
How do I proceed further? Could someone tell me where am I going wrong? Could someone please help me out with the code?
This is my try at the program..
class Program
{
static void Main(string[] args)
{
IPAddress ipad = IPAddress.Parse("192.168.0.181");
TcpListener serversocket = new TcpListener(ipad, 8888);
TcpClient clientsocket = default(TcpClient);
Byte[] bytes = new Byte[256];
serversocket.Start();
Console.WriteLine(">> Server Started");
while(true)
{
clientsocket = serversocket.AcceptTcpClient();
Console.WriteLine("Accepted Connection From Client");
LineMatcher lm = new LineMatcher(clientsocket);
Thread thread = new Thread(new ThreadStart(lm.Run));
thread.Start();
Console.WriteLine("Client connected");
}
Console.WriteLine(" >> exit");
Console.ReadLine();
clientsocket.Close();
serversocket.Stop();
}
}
public class LineMatcher //I've jumbled it up here. Don't know what exactly to do..
{
public string fileName = "c:/myfile2.txt";
private TcpClient _client;
public LineMatcher(TcpClient client)
{
_client = client;
}
public void Run()
{
try
{
StreamReader sr = new StreamReader("c:/myfile2.txt");
using (var reader = new StreamReader(_client.GetStream()))
{
string line ="";
int lineNumber = 0;
while (null != (line = sr.ReadLine()))
{
lineNumber += 1;
byte[] data = new byte[1024];
NetworkStream stream = _client.GetStream();
//if (line.Equals(line))
for (int ct = stream.Read(data,0, data.Length-1); 0 < ct; ct = stream.Read(data,0,data.Length-1))
line += Encoding.ASCII.GetString(data, 0, ct);
line = line.Trim();
{
lineNumber.ToString();
data = Encoding.ASCII.GetBytes(line);
_client.Client.Send(data, data.Length, SocketFlags.None);
Console.WriteLine("Line {0} matches {1}", lineNumber, line);
}
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.ToString());
}
Console.WriteLine("Closing client");
_client.Close();
}
}
I think you got some pieces in your Run-method swapped - here is a version that should do the job:
public void Run()
{
byte[] data;
try
{
using (var r = new StreamReader("c:/myfile2.txt"))
{
string line ="";
int lineNumber = 0;
while (null != (line = r.ReadLine()))
{
data = Encoding.ASCII.GetBytes(line + "\n");
_client.Client.Send(data, data.Length, SocketFlags.None);
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.ToString());
}
Console.WriteLine("Closing client");
_client.Close();
}
Please note that I'm not 100% sure what you are trying to do (I think you want your textfile send line-by-line to your Terminal) - so you might have to change some bits here and there.
Don't know where the Stream-messes in your code came from but I guess you tried various tutorials/snippets and forgot to clean up ;)

Categories