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.
Related
I am working on a tracking device and it is working perfectly when it sends the packet and the server acknowledge it but there is a case when the device needs two acknowledgements against one packet.
I am sending both of two but I don't know why the device is not accepting it because after that the device is sending the login packet.
Note: Login packet is the packet which is sent by the device at the beginning of the connection and also when the device does not receive the desired acknowledgement.Can somebody help me in this matter?
Here is the server side code.
public async Task ParseAvlDataAsync(TcpClient client, NetworkStream stream , byte[] array)
{
var resp = Utils.GetInformationContent(array);
if (resp.Item2)
{
string imei = Utils.BytesToString(resp.Item1);
Console.WriteLine("IMEI received: " + imei);
Console.WriteLine("--------------------------------------------");
var response = Utils.CreateResponse(array);
await stream.WriteAsync(response, 0, response.Length).ConfigureAwait(false);
Console.WriteLine("Login response sent");
while (client.Connected)
{
stream = client.GetStream();
byte[] buffer = new byte[client.ReceiveBufferSize];
await stream.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);
//it can be Location data, status info, alarm data
//if it is location data then response will not be sent to the server
Console.WriteLine(string.Format("Get {0} Packet", (PROTOCOL_NUMBER)buffer[3]));
if ((PROTOCOL_NUMBER)buffer[3] != PROTOCOL_NUMBER.LOCATION_DATA && (PROTOCOL_NUMBER)buffer[3] != PROTOCOL_NUMBER.STRING_INFO)
{
if (Utils.IsVaidProtocol((PROTOCOL_NUMBER)buffer[3]))
{
//send first response for every packet
response = Utils.CreateResponse(buffer);
if ((PROTOCOL_NUMBER)buffer[3] == PROTOCOL_NUMBER.ALARM_DATA)
{
//send second response of alarm packet
Utils.SendAlarmSecondResponse(client, buffer);
}
await stream.WriteAsync(response, 0, response.Length).ConfigureAwait(false);
Console.WriteLine(string.Format("{0} response sent.", (PROTOCOL_NUMBER)buffer[3]));
}
else
{
client.Close();
client.Dispose();
Console.WriteLine("Invalid packet recieved : " + buffer[3]);
Console.WriteLine(Utils.BytesToString(buffer));
}
}
if (Utils.IsVaidProtocol((PROTOCOL_NUMBER)buffer[3]) && (PROTOCOL_NUMBER)buffer[3] != PROTOCOL_NUMBER.LOGIN_MESSAGE)
{
Parser.Parse((PROTOCOL_NUMBER)buffer[3], imei, stream, buffer);
}
else if ((PROTOCOL_NUMBER)buffer[3] != PROTOCOL_NUMBER.LOGIN_MESSAGE)
{
client.Close();
client.Dispose();
Console.WriteLine("Invalid protocol : " + (PROTOCOL_NUMBER)buffer[3]);
}
}
}
}
public async static void SendAlarmSecondResponse(TcpClient client , byte[] buffer)
{
try
{
NetworkStream stream = client.GetStream();
UInt16 sendCRC = 0;
int messageLength = buffer[2];
//skip start bytes, message length. then go back 4 bytes (CRC and serial number)
byte[] serialNumber = buffer.Skip(2 + 1 + messageLength - 4).Take(2).ToArray();
int alarmDataAddressPacketLen = alarmDataAddressResponse.Length - 5;
alarmDataAddressResponse[2] = (byte)(alarmDataAddressPacketLen & 0xFF);
serialNumber.CopyTo(alarmDataAddressResponse, alarmDataAddressPacketLen - 1);
sendCRC = CRCBytes(alarmDataAddressResponse.Skip(2).Take(alarmDataAddressPacketLen - 1).ToArray());
alarmDataAddressResponse[alarmDataAddressPacketLen + 1] = (byte)((sendCRC >> 8) & 0xFF);
alarmDataAddressResponse[alarmDataAddressPacketLen + 2] = (byte)((sendCRC) & 0xFF);
await stream.WriteAsync(alarmDataAddressResponse, 0, alarmDataAddressResponse.Length).ConfigureAwait(false);
}
catch (Exception ex)
{
throw ex;
}
}
TCP is not message-oriented, it's stream-oriented.
You are likely reading more than one of your application-level "packets" into buffer, and you only parse the first, then throw the rest of the buffer away.
The inverse can also happen; there's no guarantee that you read a full application-level packet with a single read call.
I am trying to get a websocket connection between an iOS Client (iPad Air 2) and my C# Server.
On iOS I'm using Starscream (https://github.com/daltoniam/Starscream) for websocket connections. But right after I successfully connect to my server, it automatically disconnects.
The Sec-WebSocket-Accept are the same on both sides.
I am guessing that the error is in my C# server, but i can't figure out what exactly is wrong.
Here my iOS code:
class ViewController: UIViewController {
var socket : WebSocket!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
socket = WebSocket(url: URL(string: "ws://192.168.188.35:80")!)
socket.connect()
socket.pongDelegate = self
socket.advancedDelegate = self
}
deinit {
socket.disconnect(forceTimeout: 0, closeCode: 0)
socket.delegate = nil
}
}
extension ViewController: WebSocketAdvancedDelegate {
func websocketDidConnect(socket: WebSocket) {
print("connect")
}
func websocketDidDisconnect(socket: WebSocket, error: Error?) {
print("disconnect")
}
func websocketDidReceiveMessage(socket: WebSocket, text: String, response: WebSocket.WSResponse) {
print("receivedMessage")
}
func websocketDidReceiveData(socket: WebSocket, data: Data, response: WebSocket.WSResponse) {
print("receivedData")
}
func websocketHttpUpgrade(socket: WebSocket, request: String) {
print("httpUpgradeRequest")
print(request)
}
func websocketHttpUpgrade(socket: WebSocket, response: String) {
print("httpUpgradeResponse")
print(response)
}
}
And here is my C# server:
public TcpListener server;
public MainWindow()
{
server = new TcpListener(IPAddress.Parse("192.168.188.35"), 80);
server.Start();
Console.WriteLine("Server has started on 127.0.0.1:80.{0}Waiting for a connection...", Environment.NewLine);
TcpClient client = server.AcceptTcpClient();
Console.WriteLine("A client connected.");
NetworkStream stream = client.GetStream();
while (client.Available < 3)
{
// wait for enough bytes to be available
}
Byte[] bytes = new Byte[client.Available];
stream.Read(bytes, 0, bytes.Length);
//translate bytes of request to string
String data = Encoding.UTF8.GetString(bytes);
if (Regex.IsMatch(data, "^GET"))
{
const string eol = "\r\n"; // 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" + eol
+ "Connection: Upgrade" + eol
+ "Upgrade: websocket" + eol
+ "Sec-WebSocket-Accept: " + Convert.ToBase64String(
System.Security.Cryptography.SHA1.Create().ComputeHash(
Encoding.UTF8.GetBytes(
new System.Text.RegularExpressions.Regex("Sec-WebSocket-Key: (.*)").Match(data).Groups[1].Value.Trim() + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
)
)
) + eol
+ eol);
stream.Write(response, 0, response.Length);
}
else
{
}
}
All you need is a listening loop. Because this is a top down execution, so after you write the response, the tcp client listener stops. You can take a look at this implementation https://learn.microsoft.com/en-us/dotnet/api/system.net.sockets.tcplistener?view=netframework-4.7.2
I found the problem, it was inside my C# Server:
After a client connected and the Handshake succeeded, you have to call
server.AcceptSocket(); //or server.AcceptSocketAsync()
Problem:
I'm making a small app in which, when ran it has a login page requiring the user to enter username and password. When these are entered, the information goes to the server via TCP using sockets. However, from what I found online, in order to do so you need to specify a length of bytes in order to receive the information (see code below). The problem is that when I specify the length the rest of the string becomes \0\0\0 until all the byte slots are filled which causes problems later in the process.
What I tried:
I have tried removing the part "\0\0\0.." from the string but it failed as the program kept failing on finding the character "\". I'm not if I'm using the correct protocol or method for this but any advice is welcomed.
NetworkStream stream = client.GetStream(); //Gets input stream
byte[] receivedBuffer = new byte[100];
stream.Read(receivedBuffer, 0, receivedBuffer.Length);
string msg = Encoding.ASCII.GetString(receivedBuffer,0,receivedBuffer.Length); //translates msg
if(msg.Contains("|")) //if the msg contains "|" = log-in
{
bool cr1 = false;
bool cr2 = false;
string[] cre = msg.Split("|");
if(cre[0] == "admin") //the whole checking system will be made properly and I know this is wrong but its for testing
{
cr1 = true;
}
if (cre[1] == "pass")
{
cr2 = true;
}
if (cr1 == true && cr2 == true)
{
string answer = "True";
Tosend(answer); //Sends response to client
}
else
{
string answer = "False";
Tosend(answer);
}
}
Class to send things:
static void Tosend(string msg)
{
string ip3 = "localhost";
TcpClient client = new TcpClient(ip3, 8081);
int bc = Encoding.ASCII.GetByteCount(msg);
byte[] sd = new byte[bc];
sd = Encoding.ASCII.GetBytes(msg);
NetworkStream st = client.GetStream();
st.Write(sd, 0, sd.Length);
st.Close();
client.Close();
}
EXAMPLE
What I get:
Input: user|pass => to bytes => Sends bytes => Bytes received => Bytes translated => msg = user|pass\0\0\0\0\0\0\0\0...
Expectation:
Input: user|pass => to bytes => Sends bytes from client => Bytes received by server => Bytes translated => msg = user|pass
NetworkStream.Read will return the number of bytes read. You can use that to pull out only the actual data.
int receivedBytes = stream.Read(receivedBuffer, 0, receivedBuffer.Length);
string msg = Encoding.ASCII.GetString(receivedBuffer,0,receivedBytes);
I am doing an embedded project in C# and I have written a socket based web server. Everything works well, except I can't for the life of me get the request body. Content-Length says that there is 12 characters, but the socket.Recieve method only gets the headers.
while (true)
{
using (Socket clientSocket = listeningSocket.Accept())
{
IPEndPoint clientIP = clientSocket.RemoteEndPoint as IPEndPoint;
Debug.Print("Received request from " + clientIP.ToString());
var x = clientSocket.RemoteEndPoint;
int availableBytes = clientSocket.Available;
Debug.Print(DateTime.Now.ToString() + " " + availableBytes.ToString() + " request bytes available");
int bytesReceived = (availableBytes > maxRequestSize ? maxRequestSize : availableBytes);
if (bytesReceived > 0)
{
byte[] buffer = new byte[bytesReceived]; // Buffer probably should be larger than this.
int readByteCount = clientSocket.Receive(buffer, bytesReceived, SocketFlags.None);
using (Request r = new Request(clientSocket, Encoding.UTF8.GetChars(buffer)))
{
Debug.Print(DateTime.Now.ToString() + " " + r.URL);
if (requestReceived != null) requestReceived(r);
}
}
}
Thread.Sleep(10);
}
availableBytes = 499
bytesReceived = 499
readByteCount = 487 (12 characters short)
What am I missing here? The body is multipart form data if that makes any difference.
int bytesReceived = (availableBytes > maxRequestSize ? maxRequestSize : availableBytes);
If maxRequestSize is 487, then you will get the results you describe.
Also remember Content-Length is not bytes - it's octets: What's the "Content-Length" field in HTTP header? (OK I'm being pedantic - and octet is 8 bits ;))
I wonder how reliable that Available property is. It looks like it's only useful for non-blocking sockets, and then only as a boolean flag saying that something is available.
Why not just read the stream in a loop parsing messages as you go?
I am creating a socket server in C# and a client in PHP. The conversation between client and server is something like this:
client connects
server sends welcome message
client sends data
server sends response
(repeat step 3 - 4 until client disconnects)
It works until step 3. The data from the client is received by the server. However, the client waits forever until the server sends its response and eventually times out.
Here is the relevant C# code:
class Program
{
private const int CONNECT_QUEUE_LENGTH = 4;
static void ListenForRequests()
{
Socket listenSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
listenSock.Bind(new IPEndPoint(IPAddress.Any, 9999));
listenSock.Listen(CONNECT_QUEUE_LENGTH);
listenSock.Blocking = true;
byte[] data = new byte[1024];
while (true)
{
Socket newConnection = listenSock.Accept();
newConnection.Blocking = true;
// Create new thread for each request
var t = new Thread(() => new RequestHandler(newConnection));
t.Start();
}
}
}
class RequestHandler
{
public RequestHandler(Socket socket)
{
Util.WriteToSocket("A message", socket);
Console.WriteLine("Received: " + Util.ReadSocketToEnd(socket).Length);
Util.WriteToSocket("Fin!", socket);
}
}
static class Util
{
public static string ReadSocketToEnd(Socket newConnection)
{
var sb = new StringBuilder();
byte[] data = new byte[1024];
int receivedDataLength = newConnection.Receive(data);
while (receivedDataLength > 0)
{
try
{
sb.Append(Encoding.UTF8.GetString(data, 0, receivedDataLength));
receivedDataLength = newConnection.Receive(data);
}
catch (SocketException)
{
break;
}
}
return sb.ToString();
}
public static void WriteToSocket(string message, Socket client)
{
byte[] data = Encoding.UTF8.GetBytes(message);
client.Send(data, SocketFlags.None);
}
}
And here is the simple PHP client, without any error handling etc:
<?php
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$result = socket_connect($socket, '127.0.0.1', 9999);
print 'receiving: ' . chr(10);
print socket_read ($socket, 10) . chr(10);
print 'receive end' . chr(10);
$message = str_repeat("Message to sent", 1000);
socket_write($socket, $message, strlen($message));
print 'message sent' . chr(10);
print 'reading fin' . chr(10); // This is printed, but a response is never received
print socket_read ($socket, 10) . chr(10);
print 'fin read!' . chr(10);
socket_shutdown($socket);
You're reading from the socket until all the data has been read - meaning "all the data until the socket is closed". Your PHP client is writing data, but not closing the socket - so the server is waiting forever.
TCP sockets provide a stream of data. There's no concept of "the end of the current message" which is what it looks like you want.
The three ways of handling this are usually:
A message delimiter (e.g. a line break)
A message length prefix (number of bytes in the message just before the message itself)
Use a different connection for each request/response pair
You could potentially use a read with a timeout, and assume that if you haven't seen any more data in the last 5 seconds, you've got the whole message - but that's fragile, inefficient and generally a bad idea.