C# TCP Socket stream message confusion - c#

I'm trying to write a C# TCP server and get my web app connected. This is the tutorial I'm following. Link
I managed to start the server but the message I got from my webapp is encrypted.
C# Snippet
using System;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
namespace Application
{
class MainClass
{
public static void Main()
{
TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 8181);
server.Start();
Console.WriteLine("Server has started on 127.0.0.1:8181.{0}Waiting for a connection...", Environment.NewLine);
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("A client connected.");
NetworkStream stream = client.GetStream();
while (true)
{
while (!stream.DataAvailable) ;
Byte[] bytes = new Byte[client.Available];
stream.Read(bytes, 0, bytes.Length);
String data = Encoding.UTF8.GetString(bytes);
//handshaking
if (new Regex("^GET").IsMatch(data))
{
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);
}
else
{
Console.WriteLine(data);
}
}
}
}
}
HTML Snippet
<form>
<input id="textField1" type="text" style="margin: 0 5px; width: 200px; height: 40px;" placeholder="Enter data">
<button id="sendTextField1" type="button" class="btn btn-info">Send</button>
</form>
<script type="text/javascript">
var start = function () {
var wsImpl = window.WebSocket || window.MozWebSocket;
window.ws = new wsImpl('ws://127.0.0.1:8181/');
ws.onmessage = function (evt) {
console.log(evt.data);
};
ws.onopen = function () {
console.log("Connected");
};
ws.onclose = function () {
console.log("Closed");
};
document.getElementById("sendTextField1").onclick = function() {
sendToSocket(document.getElementById('textField1').value);
};
function sendToSocket(value){
console.log("Sending value to socket " + value + " ");
ws.send(value);
};
}
window.onload = start;
This is what I got from my console
I have tried to encode the message to another format but no luck at all.
According to this stackoverflow user,
Messages are not sent in raw format, but they have Data Framing.
Is it relevant to the problem I'm having now? Do I need to find a way to deframe the data I got from the app?

Well, this isn't a great start:
String data = Encoding.UTF8.GetString(bytes);
The data sent after the handshake isn't UTF8 text. Parts of it are, but there are binary headers, and there's also something (in RFC6455 at least - possibly not if your client is using Hixie-76) called "Client-to-Server Masking", which means that the data is indeed munged (this is, IIRC, largely to avoid problems with proxy servers, and is not intended to be encryption).
There's also the problem of you looping over the handshake, but: you only handshake once.
Basically, the web-sockets protocol is quite nuanced (and: it is badly/incorrectly implemented by some browsers/versions). Unless you have a very good reason*, I strongly recommend using a library instead of trying to write this by hand. Microsoft provides some level of support in the framework, which should help.
*=I've done this, but for our scenario, the key design reason was scalability - we currently have 464515 open web-socket connections, and the MS implementation just wasn't going to cut it. We made our code freely available, if it helps.

Related

C# Websocket server not receiving messages from HTML5 client

I'm attempting to create a bare bones websocket chat room server. My client is able to connect to the server, but it is unable to send messages to the server, despite the server being in a listening state.
When the client connects, a bunch of what looks like header information gets written to the console. But when WebSocket.send() gets executed in Javascript, nothing occurs server side.
HTML:
<button id="closeSocket">disconnect</button><br />
<input id = "inputField" /><button id="sendMessage">send</button>
<div id = "output"></div>
<script type = 'text/javascript' src = 'websockets.js'></script>
Javascript:
websocket = new WebSocket("ws://127.0.0.1:80");
document.getElementById("closeSocket").onclick = closeSocket;
document.getElementById("sendMessage").onclick = sendMessage;
websocket.onopen = function(){
output("connected");
}
function sendMessage(){
output("sent: " + document.getElementById('inputField').value);
websocket.send(document.getElementById('inputField').value);
}
websocket.onmessage = function(e){
output("got response: " + e.data);
}
function closeSocket(){
websocket.close();
}
websocket.onclose = function(){
output("disconnected");
}
function output(t){
document.getElementById("output").innerHTML += t + "<br />";
}
C# server:
using System;
using System.Net;
using System.Net.Sockets;
namespace WebSocketsTutorial {
class Program {
static void Main(string[] args) {
TcpListener server = new TcpListener(IPAddress.Parse("127.0.0.1"), 80);
TcpClient client = default(TcpClient);
server.Start();
Console.WriteLine("server started");
while (true) {
client = server.AcceptTcpClient();
byte[] receivedBuffer = new byte[100];
NetworkStream stream = client.GetStream();
stream.Read(receivedBuffer, 0, receivedBuffer.Length);
foreach (byte b in receivedBuffer) {
Console.Write(Convert.ToChar(b).ToString());
}
}
}
}
}
This is what is output on the console when the client connects:
What I'm mainly looking to do is to allow an arbitrary number of connections and ultimately have the server echo a user's submission to all connected clients.
First of all, a WebSocket is not just a regular socket. It defines a connection handshake using HTTP and its own framing protocol that you need to use.
https://en.wikipedia.org/wiki/WebSocket
https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers
https://hpbn.co/websocket/
Second, you are reading just the first 100 bytes of the request. You should read until the Read operation returns 0.
There is a myriad of components you can use to create a WebSocket server, including the default one: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/websockets

Proxy won't work in chrome and only partly in firefox

I've written a proxy in c#. It works by getting the http request from the browser sending the request to the site and sending back the site's response to the client. It works in firefox but some of the pages are cut like if it did not send all the response and in chrome it gives blank pages and for google.co.uk in chrome the browser gives "no data received". Can you see a mistake in my code which might be causing all this?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Windows.Forms;
namespace LexProxy
{
class ProxyServer
{
private TcpListener tcpListener;
public ProxyServer()
{
this.tcpListener = new TcpListener(IPAddress.Any, 3000);
this.tcpListener.Start();
while (true)
{
Console.Write("Waiting for a connection... ");
TcpClient client = tcpListener.AcceptTcpClient();
Thread thread = new Thread(delegate()
{
Serve(client);
});
thread.Start();
}
}
private void Serve(TcpClient client)
{
Console.WriteLine("Connected!");
NetworkStream stream = client.GetStream();
byte[] request = GetBytesFromStream(stream, client.ReceiveBufferSize);
if (request != null)
{
string requestString = System.Text.Encoding.UTF8.GetString(request);
string[] requestParts = requestString.Split(' ');
if (requestParts.Length >= 2)
{
string method = requestParts[0];
if (!requestParts[1].Contains("http://") && !requestParts[1].Contains("https://"))
requestParts[1] = "http://" + requestParts[1];
Uri uri = new Uri(requestParts[1], UriKind.RelativeOrAbsolute);
string host = StringUtils.ReplaceFirst(uri.Host, "www.", "");
int port = uri.Port;
byte[] response = getResponse(host, port, request);
if (response != null)
stream.Write(response, 0, response.Length);
client.Close();
}
}
}
private byte[] getResponse(string host, int port, byte[] request)
{
TcpClient client = new TcpClient(host, port);
NetworkStream stream = client.GetStream();
stream.Write(request, 0, request.Length);
byte[] response = GetBytesFromStream(stream, client.ReceiveBufferSize);
return response;
}
private byte[] GetBytesFromStream(NetworkStream stream, int bufferSize)
{
Byte[] bytes = new Byte[bufferSize];
int i;
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
return bytes;
}
return null;
}
}
}
One minor error is that you're using UTF8 to read first line of request. HTTP request line and headers are ASCII (and body may be UTF8, but doesn't have to be, and may not even be a string at all). The reason it works is b/c for english charset, UTF8 and ASCII happen to be encoded using the same bytes. But that's the minor issue.
Most likely primary issue is hiding in your GetBytesFromStream. You only call .Read once, but that doesn't guarantee that the whole message is returned. It may return as little as just 1 byte... so you need to continue getting more data. How do you know how much more? It's dictated by HTTP protocol which you'd need to properly parse and examine. (At a minimum you're reading until you reach \r\n\r\n byte sequence indicating the end of request headers.
However, this is not enough as there may be a request body, length of which will be specified via an HTTP header, Content-Length: (IIRC), or possibly using chunked encoding. I don't see any code beyond URI examination, so most likely you do not handle HTTP messaging protocol itself, and so it is unlikely to work properly (unless you somehow manage to force browser to use HTTP/0.9 or HTTP/1.0 since those do not reuse connection and send one message per connection, at which point you could just blindly read everything the socket has to give you until browser signals the end of stream by closing its write endpoint of the connection).
But your main problem is that GetBytesFromStream as written won't give you "all" bytes.

WebSocket connection is closing

I'm a beginner with the WebSocket API. I'm trying to connect to my server locally but I'm obtaining the connection closed message. Can anyone tell me what I am doing wrong?
That's my code:
Server
using System.Net.Sockets;
using System.Net;
using System.IO;
using System;
class Program
{
static void Main(string[] args)
{
var listener = new TcpListener(IPAddress.Loopback, 8181);
listener.Start();
while (true)
{
Console.WriteLine("Listening...");
using (var client = listener.AcceptTcpClient())
using (var stream = client.GetStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
{
string line = null, key = "", responseKey = "";
string MAGIC_STRING = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
while (line != "")
{
line = reader.ReadLine();
if (line.StartsWith("Sec-WebSocket-Key:"))
{
key = line.Split(':')[1].Trim();
}
}
if (key != "")
{
key += MAGIC_STRING;
using (var sha1 = SHA1.Create())
{
responseKey = Convert.ToBase64String(sha1.ComputeHash(Encoding.ASCII.GetBytes(key)));
}
}
// send handshake to the client
writer.WriteLine("HTTP/1.1 101 Web Socket Protocol Handshake");
writer.WriteLine("Upgrade: WebSocket");
writer.WriteLine("Connection: Upgrade");
writer.WriteLine("WebSocket-Origin: http://localhost:8080");
writer.WriteLine("WebSocket-Location: ws://localhost:8181/websession");
if (!String.IsNullOrEmpty(responseKey))
writer.WriteLine("Sec-WebSocket-Accept: " + responseKey);
writer.WriteLine("");
Console.ReadLine();
writer.Flush();
}//using
Console.WriteLine("Finished");
}//while
}
}
The Client
<!DOCTYPE HTML>
<html>
<head><title></title>
<script type="text/javascript">
function WebSocketTest() {
var msg = document.getElementById("msg");
if ("WebSocket" in window) {
msg.innerHTML="WebSocket is supported by your Browser!";
// Let us open a web socket
var ws = new WebSocket("ws://localhost:8181/websession");
ws.onopen = function () {
// Web Socket is connected, send data using send()
msg.innerHTML="connection open";
//ws.send("Message to send");
//msg.innerHTML="Message is sent...";
};
ws.onclose = function () {
// websocket is closed.
msg.innerHTML = "Connection is closed...";
};
ws.onerror = function(error){
console.log('Error detected: ' + error);
};
ws.onmessage = function (evt) {
var received_msg = evt.data;
msg.innerHTML="Message is received...";
};
}
else {
// The browser doesn't support WebSocket
msg.innerHTML="WebSocket NOT supported by your Browser!";
}
}
</script>
</head>
<body>
<div id="sse">
Run WebSocket<br />
<p id="msg"></p>
</div>
</body>
</html>
Any solution would be appreciated and thank you :)
That is not a valid web-socket response under any specification. The initial web-socket response always requires you to crunch some numbers as part of the response headers, to prove you're a web-socket server. Which headers to read and write, and what crunching to do, depends on the version of web-sockets (hibi/hixie 76/rfc). It actually looks like your server is using the headers of a client.
For example, a RFC6455 (13) response would start:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: {crunch some numbers}
Note that a Hixie-76 response is different, and there are bits in the above that I have omitted.
From the RFC6455 specification:
To prove that the handshake was received, the server has to take two
pieces of information and combine them to form a response. The first
piece of information comes from the |Sec-WebSocket-Key| header field
in the client handshake:
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
For this header field, the server has to take the value (as present in
the header field, e.g., the base64-encoded [RFC4648] version minus any
leading and trailing whitespace) and concatenate this with the
Globally Unique Identifier (GUID, [RFC4122]) "258EAFA5-E914-47DA-
95CA-C5AB0DC85B11" in string form, which is unlikely to be used by
network endpoints that do not understand the WebSocket Protocol. A
SHA-1 hash (160 bits) [FIPS.180-3], base64-encoded (see Section 4 of
[RFC4648]), of this concatenation is then returned in the server's
handshake.
After the javascript code connects to the webserver. C# runs through all using statements and prints all the lines to your file after which all using statements are closed and thus the generated client is disposed, which closes the connection.
A quick (but dirty) fix for this problem is to add "Console.Readline" at the end of the using statements. Be carefull with this though, your process will hang!
For more information about TcpClients accepting clients, go to msdn.
Notice that the given example there, only allows one connection at a time.
Last remark: there exist libraries for accepting websocket connections like Marc suggested.

How to send OK response to EWS

I am trying to get EWS's push notifications set up in my c# app.
After getting the response from the server and reading it using a NetworkStream I need to respond to the server with Ok in a SOAP message. The only example that I can find uses Microsoft.Web.Services3 and a SoapEnvelope. My understanding is that this has now been replaced by WCF and I really want to use the newer technologies (to learn them).
How would I go by sending a SOAP message back to the server, presumably using the same NetworkStream that I get the notification on?
Here is some code that I tried, but it fails for some reason.
const string RESPONSE_OK = "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope\"><soap:Body>" +
"<SendNotificationResult xmlns=\"http://schemas.microsoft.com/exchange/services/2006/messages\">" +
"<SubscriptionStatus>OK</SubscriptionStatus></SendNotificationResult></soap:Body></soap:Envelope>";
responseBytes = encoding.GetBytes(RESPONSE_OK);
// Send the result
HTTPResponseStruct _httpResponse;
_httpResponse.version = "HTTP/1.1";
_httpResponse.BodyData = responseBytes;
_httpResponse.Headers = new Hashtable();
_httpResponse.Headers.Add("Server", "IT12");
_httpResponse.Headers.Add("Date", DateTime.Now.ToString("r"));
_httpResponse.Headers.Add("Content-Type", "text/xml; charset=utf-8");
_httpResponse.Headers.Add("Content-Length", _httpResponse.BodyData.Length);
_httpResponse.Headers.Add("Connection", "close");
string HeadersString = _httpResponse.version + " "
+ "200 OK" + "\r\n";
foreach (DictionaryEntry Header in _httpResponse.Headers)
{
HeadersString += Header.Key + ": " + Header.Value + "\r\n";
}
HeadersString += "\r\n";
byte[] bHeadersString = Encoding.ASCII.GetBytes(HeadersString);
// Send headers
clientStream.Write(bHeadersString, 0, bHeadersString.Length);
// Send body
if (_httpResponse.BodyData != null)
clientStream.Write(_httpResponse.BodyData, 0,
_httpResponse.BodyData.Length);
// clientStream.Write(responseBytes, 0, responseBytes.Length);
clientStream.Flush();
Thanks,
Pieter
you can use Microsoft.Exchange.WebServices.Data (EWS Managed API Reference)
http://msdn.microsoft.com/en-us/library/dd633710%28v=EXCHG.80%29.aspx
You marked the other answer as "accepted" but the link you are referring to talks about streaming subscriptions. These are only available for Exchange 2010 and later. For those who are stuck with Exchange Server 2007, there is a Push-Notification library on CodePlex.

Problem in Sending messages from Server to Client

im making a Window Application in C# using Socket Programming. I have developed a Server & a Client. Both are working fine but the problem which im gettin is that when ever i send any message from CLIENT, its send perfectly and receives on SERVER but whenever i try to send any message from SERVER it doesn't send to Client, since at the beginning when a connection is built, server sends the message to client that "Connection Established" and received at Client perfectly,but later on server does not send any message to client!!! Could anyone please help me out ???????
Regards
Umair
EDIT:
//Code at SERVER for SENDING...
private void button_send(object sender, EventArgs e)
{
string input = textBoxWrite.Text;
byte[] SendData = new byte[1024];
ASCIIEncoding encoding = new ASCIIEncoding();
SendData = encoding.GetBytes(input);
client.Send(SendData,SendData.Length,SocketFlags.None);
textBoxShow.Text = "Server: " + input;
}
//Code at CLIENT for receiving
NetworkStream networkStream = new NetworkStream(server);
string input = textBoxUser.Text + ": " + textBoxWrite.Text;
ASCIIEncoding encoding = new ASCIIEncoding();
byte[] inputByte = encoding.GetBytes(input);
if (networkStream.CanWrite)
{
networkStream.Write(inputByte, 0, inputByte.Length);
textBoxShow.Text = textBoxShow.Text + Environment.NewLine + input;
textBoxWrite.Text = "";
networkStream.Flush();
}
I'm not sure how best to help based on the information you've provided, but perhaps you could look at something like this example of C# socket programming and compare with your own application.

Categories

Resources