I found TCPListener code from here.
In MainWindow class Im starting server.
Then opening websocket from html page
let socket = new WebSocket("ws://192.168.1.149:1112");
Right after this CPU loads to 25% even if nothing sent to server. Further data sending to server going normal. What to do to prevent CPU load? Please help.
class TcpServer
{
public string ip;
public int port;
private Thread bgThread;
public void StartListen()
{
bgThread = new Thread(new ThreadStart(Start))
{
IsBackground = true
};
bgThread.Start();
}
public void Start()
{
TcpListener server = new TcpListener(IPAddress.Parse(ip), port);
server.Start();
TcpClient client = server.AcceptTcpClient();
NetworkStream stream = client.GetStream();
while (true)
{
while (!stream.DataAvailable) ;
while (client.Available < 3) ; // match against "get"
byte[] bytes = new byte[client.Available];
stream.Read(bytes, 0, client.Available);
string strbytes = Encoding.UTF8.GetString(bytes);
if(strbytes.StartsWith("GET"))
{
Console.WriteLine("=====Handshaking from client=====\n{0}", strbytes);
string swk = Regex.Match(strbytes, "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);
}
else
{
bool mask = (bytes[1] & 0b10000000) != 0;
int msglen = bytes[1] - 128,
offset = 2;
if (msglen == 126)
{
msglen = BitConverter.ToUInt16(new byte[] { bytes[3], bytes[2] }, 0);
offset = 4;
}
else if (msglen == 127)
{
Console.WriteLine("TODO: msglen == 127, needs qword to store msglen");
}
if (msglen == 0)
Console.WriteLine("msglen == 0");
else if (mask)
{
try
{
byte[] decoded = new byte[msglen];
byte[] masks = new byte[4] { bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3] };
offset += 4;
for (int i = 0; i < msglen; ++i)
decoded[i] = (byte)(bytes[offset + i] ^ masks[i % 4]);
string text = Encoding.UTF8.GetString(decoded);
// other code
}
catch(Exception exc)
{
Console.WriteLine(exc.Message + "\n--------\n" + exc.StackTrace);
}
}
else
Console.WriteLine("mask bit not set");
}
}
}
}
private void startServer()
{
tcpserver = new TcpServer
{
ip = ipbox.Text,
port = 1112
};
tcpserver.StartListen();
}
startServer();
P.S: I have not to say anymore but SO says "It looks like your post is mostly code; please add some more details.". So: some words
Related
how are you? I hope that you are good, I have a question, how can I do to the server consume multi clients? I put all my code
Server
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
namespace Consola
{
internal class Program
{
static void Main(string[] args)
{
string ip = "127.0.0.1";
int port = 8080;
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();
// enter to an infinite cycle to be able to handle every change in stream
while (true)
{
while (!stream.DataAvailable) ;
while (client.Available < 3) ; // match against "get"
byte[] bytes = new byte[client.Available];
stream.Read(bytes, 0, client.Available);
string s = Encoding.UTF8.GetString(bytes);
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);
}
else
{
bool fin = (bytes[0] & 0b10000000) != 0,
mask = (bytes[1] & 0b10000000) != 0; // must be true, "All messages from the client to the server have this bit set"
int opcode = bytes[0] & 0b00001111, // expecting 1 - text message
offset = 2;
ulong msglen = (ulong)(bytes[1] & 0b01111111);
if (msglen == 126)
{
// bytes are reversed because websocket will print them in Big-Endian, whereas
// BitConverter will want them arranged in little-endian on windows
msglen = BitConverter.ToUInt16(new byte[] { bytes[3], bytes[2] }, 0);
offset = 4;
}
else if (msglen == 127)
{
// To test the below code, we need to manually buffer larger messages — since the NIC's autobuffering
// may be too latency-friendly for this code to run (that is, we may have only some of the bytes in this
// websocket frame available through client.Available).
msglen = BitConverter.ToUInt64(new byte[] { bytes[9], bytes[8], bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2] }, 0);
offset = 10;
}
if (msglen == 0)
{
Console.WriteLine("msglen == 0");
}
else if (mask)
{
byte[] decoded = new byte[msglen];
byte[] masks = new byte[4] { bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3] };
offset += 4;
for (ulong i = 0; i < msglen; ++i)
decoded[i] = (byte)(bytes[(ulong)offset + i] ^ masks[i % 4]);
string text = Encoding.UTF8.GetString(decoded);
Console.WriteLine("{0}", text);
}
else
Console.WriteLine("mask bit not set");
Console.WriteLine();
}
}
}
}
}
Client
<!DOCTYPE html>
<html lang="en">
<head>
<link href="Content/bootstrap.min.css" type="text/css" rel="stylesheet" />
<script src="Scripts/jquery-3.6.1.min.js" type="text/javascript"></script>
</head>
<body>
<div class="card">
<div class="card-header">
<h2>Prueba simple de WebSocket</h2>
</div>
<div class="card-body">
<p>
<textarea cols="60" rows="6" id="cajadetexto"></textarea>
</p>
<p>
<button id="boton" class="btn btn-primary">Enviar</button>
</p>
<p>
<div id="salida"></div>
</p>
</div>
</div>
<style type="text/css">
textarea {
vertical-align: bottom;
}
#salida {
overflow: auto;
}
#salida > p {
overflow-wrap: break-word;
}
#salida span {
color: blue;
}
#salida span.error {
color: red;
}
</style>
<script type="text/javascript">
$(document).ready(function () {
const wsUri = "ws://127.0.0.1:8080/";
const websocket = new WebSocket(wsUri);
$(document).on("click", "#boton", onClickButton);
websocket.onopen = (e) => {
writeToScreen("CONNECTED");
doSend("WebSocket rocks");
};
websocket.onclose = (e) => {
writeToScreen("DISCONNECTED");
};
websocket.onmessage = (e) => {
writeToScreen(`<span>RESPONSE: ${e.data}</span>`);
};
websocket.onerror = (e) => {
writeToScreen(`<span class="error">ERROR:</span> ${e.data}`);
};
function doSend(message) {
writeToScreen(`SENT: ${message}`);
websocket.send(message);
}
function writeToScreen(message) {
$("#salida").append("<p>" + message + "</p>");
}
function onClickButton() {
var text = $("#cajadetexto").val();
text && doSend(text);
$("#cajadetexto").val("");
$("#cajadetexto").focus();
}
});
</script>
</body>
</html>
I based my code on this link:
https://github.com/mdn/content/blob/main/files/en-us/web/api/websockets_api/writing_websocket_server/index.md?plain=1
Thanks and I wait your answers.
UPGRADE 23/12/2022 12:05
Hi, finally i could implement the threads to the multi clients in the server, this is the code:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
namespace Consola
{
public class Program
{
private static TcpListener tcpListener = new TcpListener(
IPAddress.Parse("127.0.0.1"),
8080
);
public static void Main(string[] args)
{
tcpListener.Start();
while(true)
{
Thread nuevoHilo = new Thread(new ThreadStart(Listeners));
nuevoHilo.Start();
}
}
private static void Listeners()
{
Socket client = tcpListener.AcceptSocket();
if (client.Connected)
{
Console.WriteLine("Client:" + client.RemoteEndPoint + " now connected to server.");
NetworkStream stream = new NetworkStream(client);
while (true)
{
while (!stream.DataAvailable) ;
while (client.Available < 3) ; // match against "get"
byte[] bytes = new byte[client.Available];
stream.Read(bytes, 0, client.Available);
string s = Encoding.UTF8.GetString(bytes);
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);
}
else
{
bool fin = (bytes
[o]& 0b10000000) != 0,
mask = (bytes[1] & 0b10000000) != 0; // must be true, "All messages from the client to the server have this bit set"
int opcode = bytes
[o]& 0b00001111, // expecting 1 - text message
offset = 2;
ulong msglen = (ulong)(bytes[1] & 0b01111111);
if (msglen == 126)
{
// bytes are reversed because websocket will print them in Big-Endian, whereas
// BitConverter will want them arranged in little-endian on windows
msglen = BitConverter.ToUInt16(new byte[] { bytes[3], bytes[2] }, 0);
offset = 4;
}
else if (msglen == 127)
{
// To test the below code, we need to manually buffer larger messages — since the NIC's autobuffering
// may be too latency-friendly for this code to run (that is, we may have only some of the bytes in this
// websocket frame available through client.Available).
msglen = BitConverter.ToUInt64(new byte[] { bytes[9], bytes[8], bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2] }, 0);
offset = 10;
}
if (msglen == 0)
{
Console.WriteLine("msglen == 0");
}
else if (mask)
{
byte[] decoded = new byte[msglen];
byte[] masks = new byte[4] { bytes[offset], bytes[offset + 1], bytes[offset + 2], bytes[offset + 3] };
offset += 4;
for (ulong i = 0; i < msglen; ++i)
decoded[i] = (byte)(bytes[(ulong)offset + i] ^ masks[i % 4]);
string text = Encoding.UTF8.GetString(decoded);
Console.WriteLine("{0}", text);
}
else
Console.WriteLine("mask bit not set");
Console.WriteLine();
}
}
}
client.Close();
}
}
}
Now I need that the client view can notify the other clients messages and prevent the OutOfMemoryException when I leave the system very quiet.
This image shows my situation
enter image description here
When the file is sent to c# server through firefox, it works but it doesn't work on chrome.
In firefox, every files size can be received well.
in chrome, only small files are received. however, received bytes of Files larger than 50kb are not equal to the file size.
public class Client
{
private static readonly string guid = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
public Socket Socket { set; get; }
private byte[] Buffer { get; set; }
private int BufferSize { get; set; } = 1024 * 1024;
private List<byte> Buffers = new List<byte>();
private BytesDecoder decoder = new BytesDecoder();
public void Listen()
{
SendHandshake();
BeginReceive();
}
private void SendHandshake()
{
try
{
byte[] buffer = new byte[1024];
Socket.Receive(buffer);
var key = new Regex(#"Sec-WebSocket-Key:\s(.*?)\r\n").Match(Encoding.UTF8.GetString(buffer)).Groups[1].Value.Trim();
byte[] keyBytes = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(key + guid));
key = Convert.ToBase64String(keyBytes);
var response = string.Format(string.Join("\r\n", new string[]{
"HTTP/1.1 101 Switching Protocols",
"Upgrade: websocket",
"Connection: Upgrade",
"Sec-WebSocket-Accept: {0}",
"",
""
}), key);
Socket.Send(Encoding.UTF8.GetBytes(response));
}
catch (SocketException ex)
{
Console.WriteLine("{0}: {1}", "SendHandshake", ex.Message);
Close();
}
catch (Exception ex)
{
Console.WriteLine("{0}: {1}", "SendHandshake", ex.Message);
}
}
private void BeginReceive()
{
try
{
Buffer = new byte[BufferSize];
Socket.BeginReceive(Buffer, 0, BufferSize, SocketFlags.None, new AsyncCallback(ReceiveCallback), this);
}
catch (SocketException ex)
{
Console.WriteLine("{0}: {1}", "BeginReceive1", ex.Message);
Close();
}
catch (Exception ex)
{
Console.WriteLine("{0}: {1}", "BeginReceive2", ex.Message);
}
}
public static byte[] TrimEnd(byte[] array)
{
int lastIndex = Array.FindLastIndex(array, b => b != 0);
Array.Resize(ref array, lastIndex + 1);
return array;
}
private void ReceiveCallback(IAsyncResult ar)
{
try
{
var bytes = new byte[0];
if (decoder.indexFirstMask==0)
{
bytes = decoder.DecodeMessage(TrimEnd(Buffer));
}
else
{
bytes = decoder.DecodeRemainingMessage(TrimEnd(Buffer));
}
Buffers = Buffers.Concat(bytes).ToList();
if (Buffers.ToArray().Length == Convert.ToInt32(fileInfo["size"]))
{
File.WriteAllBytes(#"C:\" + fileInfo["name"], Buffers.ToArray());
}
BeginReceive();
}
catch (SocketException ex)
{
Close();
Console.WriteLine("{0}: {1}", "ReceiveCallback", ex.Message);
}
catch (Exception ex)
{
BeginReceive();
Console.WriteLine("{0}: {1}", "ReceiveCallback", ex.Message);
}
}
public void Close()
{
Socket.Shutdown(SocketShutdown.Both);
Socket.Close();
Socket.Dispose();
}
}
when file size less than 50kb, byte 0 is 130 (opcode=2) and file size larger than 50 kb, byte 0 is 2;
It is clear that there is a problem with frame decoding or handshake response....
public class BytesDecoder
{
public int dataLength, indexFirstMask=0;
public IEnumerable<byte> keys;
public byte[] DecodeMessage(byte[] bytes)
{
Console.WriteLine("+DecodeMessage+");
byte secondByte = bytes[1];
bool masked = (bytes[1] & 128) != 0;
dataLength = secondByte & 127;
indexFirstMask = 2;
if (masked)
{
Console.WriteLine("Masked bit SET");
}
if (dataLength == 126)
{
indexFirstMask = 4;
dataLength = bytes[3] | bytes[2] << 8;
}
else if (dataLength == 127)
{
indexFirstMask = 10;
dataLength = bytes[9] | bytes[8] << 8 | bytes[7] << 16 | bytes[6] << 24 | bytes[5] << 32 |
bytes[4] << 40 | bytes[3] << 48 | bytes[2] << 56;
}
keys = bytes.Skip(indexFirstMask).Take(4);
int indexFirstDataByte = indexFirstMask + 4;
byte[] decoded = new byte[bytes.Length - indexFirstDataByte];
Console.WriteLine("dataLength : " + dataLength + " ; bytes.Length : " + bytes.Length);
int j = 0;
for (int i = indexFirstDataByte; i < bytes.Length; i++)
{
decoded[j] = (byte)(bytes[i] ^ keys.ElementAt(j % 4));
j++;
}
Console.WriteLine("-DecodeMessage-");
return decoded;
}
public byte[] DecodeRemainingMessage(byte[] bytes)
{
Console.WriteLine("+DecodeRemainingMessage+");
int indexFirstDataByte = 0;
byte[] decoded = new byte[bytes.Length - indexFirstDataByte];
int j = 0;
for (int i = indexFirstDataByte; i < bytes.Length; i++)
{
decoded[j] = (byte)(bytes[i] ^ keys.ElementAt(j % 4));
j++;
}
Console.WriteLine("-DecodeRemainingMessage-");
return decoded;
}
}
js code:
connect:function(){
var root = this;
root.websocket = new WebSocket(root.url);
root.websocket.binaryType = "arraybuffer";
root.websocket.onopen = () => root.fireEvent('connect');
root.websocket.onmessage = (e) => {
root.fireEvent('receive',JSON.parse(e.data));
}
window.addEventListener("beforeunload", function() {
root.websocket.onclose = function () {};
root.websocket.close();
});
},
sendMessage:function(msg){
var root = this;
root.controller.websocket.send(message);
},
sendFile:function(){
var root = this;
var file = document.getElementById('filename').files[0];
var root = this;
var loader = new FileReader();
loader.onload = (e) => {
var byteArray = new Uint8Array(e.target.result);
root.buffer = byteArray.buffer;
root.byteLength = root.buffer.byteLength;
root.server.websocket.send(root.buffer);
}
loader.readAsArrayBuffer(file);
}
Edit 1:
problem found large messages dataLength not currect
dataLength = (int)BitConverter.ToInt64(new byte[] { buffer[9], buffer[8], buffer[7], buffer[6], buffer[5], buffer[4], buffer[3], buffer[2] }, 0);
for example file size is 169174 received data length
received data length in firefox= 169174
received data length in chrome= 131000
Edit 2:
FIN bit in chrome is 2 and in firefox is 1
Faced a problem. There is a 0x03 function, but I need to redo it in 0x06, I don’t understand how to do it.
I know that function 06 does not have a variable part. The value of one register is always transferred to it. But I can not understand what needs to be corrected. Please, help.
Here I create a package for functions:
private void BuildMessage(byte address, byte type, ushort start, ushort registers, ref byte[] message)
{
if (type == 3 || type == 16)
{
//Array to receive CRC bytes:
byte[] CRC = new byte[2];
message[0] = address;
message[1] = type;
message[2] = (byte)(start >> 8);
message[3] = (byte)start;
message[4] = (byte)(registers >> 8);
message[5] = (byte)registers;
GetCRC(message, ref CRC);
message[message.Length - 2] = CRC[0];
message[message.Length - 1] = CRC[1];
}
else if (type == 6)
{
byte[] CRC = new byte[2];
message[0] = address;
message[1] = type;
message[2] = (byte)(start >> 8);
message[3] = (byte)start;
message[4] = (byte)(registers >> 8);
message[5] = (byte)registers;
GetCRC(message, ref CRC);
message[6] = CRC[0];
message[7] = CRC[1];
}
}
This is my function number 3:
public bool SendFunc(int funcNumer, string connectType, byte address, ushort start, ushort registers, ref short[] values)
{
#region №3
if (funcNumer == 3)
{
#region serial-port
if (connectType.Equals("COM"))
{
//Ensure port is open:
if (sp.IsOpen)
{
//Clear in/out buffers:
sp.DiscardOutBuffer();
sp.DiscardInBuffer();
//Function 3 request is always 8 bytes:
byte[] message = new byte[8];
//Function 3 response buffer:
byte[] response = new byte[5 + 2 * registers];
//Build outgoing modbus message:
BuildMessage(address, (byte)3, start, registers, ref message);
//Send modbus message to Serial Port:
try
{
sp.Write(message, 0, message.Length);
GetResponse("COM", ref response);
}
catch (Exception err)
{
modbusStatus = "" + err.Message;
return false;
}
//Evaluate message:
if (CheckResponse(response))
{
//Return requested register values:
for (int i = 0; i < (response.Length - 5) / 2; i++)
{
values[i] = response[2 * i + 3];
values[i] <<= 8;
values[i] += response[2 * i + 4];
}
modbusStatus = "";
return true;
}
else
{
modbusStatus = "";
return false;
}
}
else
{
modbusStatus = "";
return false;
}
}
And here I am trying to implement function number 6:
if (funcNumer == 6)
{
#region serial-port
if (connectType.Equals("COM"))
{
//Ensure port is open:
if (sp.IsOpen)
{
//Clear in/out buffers:
sp.DiscardOutBuffer();
sp.DiscardInBuffer();
//Function 3 request is always 8 bytes:
byte[] message = new byte[8];
//Function 3 response buffer:
byte[] response = new byte[5 + 2 * registers];
//Build outgoing modbus message:
BuildMessage(address, (byte)6, start, registers, ref message);
//Send modbus message to Serial Port:
try
{
sp.Write(message, 0, message.Length);
GetResponse("COM", ref response);
}
catch (Exception err)
{
modbusStatus = "" + err.Message;
return false;
}
//Evaluate message:
if (CheckResponse(response))
{
//Return requested register values:
for (int i = 0; i < (response.Length - 5) / 2; i++)
{
values[i] = response[2 * i + 3];
values[i] <<= 8;
values[i] += response[2 * i + 4];
}
modbusStatus = "";
return true;
}
else
{
modbusStatus = "";
return false;
}
}
else
{
modbusStatus = "";
return false;
}
}
This is my function to check the response:
private bool CheckResponse(byte[] response)
{
//Perform a basic CRC check:
byte[] CRC = new byte[2];
GetCRC(response, ref CRC);
if (CRC[0] == response[response.Length - 2] && CRC[1] == response[response.Length - 1])
return true;
else
return false;
}
This is my function for get response:
private void GetResponse(string connect, ref byte[] response)
{
if (connect.Equals("COM"))
{
for (int i = 0; i < response.Length; i++)
{
response[i] = (byte)(sp.ReadByte());
}
}
else if (connect.Equals("TCP"))
{
NetworkStream stream = tcpClient.GetStream();
for (int i = 0; i < response.Length; i++)
{
response[i] = (byte)(stream.ReadByte());
}
}
else
{
}
}
The difference between Modbus function 0x03 and 0x06 is actually the part of the code you did not show on your question: CheckResponse(response).
Function 0x03 reads a number of registers, and the values in those registers (coming from the slave) are included in the response.
Function 0x06 writes a single register and gives an echo back. So the response is the same as the query.
With that information, it should be easy to modify your code: remove the for loop where you retrieve the register values.
Other than that you might need to modify your CheckResponse() function too, but that should also be quite straight forward: just check the response is exactly the same as the query (message).
EDIT: if your CheckResponse()` function only checks for the correct CRC on the responses I suppose you can keep it as it is.
Below I attached a segment of code I have in my server. It is the main segment that controls all incoming client messages. Some messages are just strings where others are images. It works sometimes but other times the while loop (will label) will break before the whole image is done.
I am not sure if there is a better way to detect what is being sent over because at the moment I have it just looking for key words to see if it's an image.
Main issue isn't how to detect if incoming bytes are images because it seems to not be a problem search for words like PNG and such... the main concern is the permature breaking of the while loop indicated below
Also I noted that the while loop performs normally on my laptop and not my desktop for one reason or another. Might be a fuke but I am not sure if this statement is useful.
if (stream.DataAvailable)
{
Task DataInterpetTask = Task.Factory.StartNew(() =>
{
byte[] buffer = new byte[0];
byte[] tbuffer;
int rLength, prevLength;
byte[] buf = new byte[c.getClientSocket().ReceiveBufferSize];
//below is the while loop that breaks early
while (stream.DataAvailable)
{
rLength = stream.Read(buf, 0, buf.Length);
if (rLength < buf.Length)
{
byte[] temp = new byte[rLength];
Array.Copy(buf, temp, rLength);
buf = temp;
}
prevLength = buffer.Length;
tbuffer = buffer;
buffer = new byte[buffer.Length + rLength];
Array.Copy(tbuffer, buffer, tbuffer.Length);
buf.CopyTo(buffer, prevLength);
MainWindow.mainWindowDispacter.BeginInvoke(PrintDebugLog, new Object[] { "prevLength : " + prevLength + " new Length: " + buffer.Length });
}
// below searches for image related key words seems to work but if loop above
//break early I only get part of an image
String msg = Encoding.ASCII.GetString(buffer);
if (msg.Contains("PNG") || msg.Contains("RBG") || msg.Contains("HDR"))
{
RowContainer tempContainer;
if ((tempContainer = MainWindow.mainWindow.RowExists(c)) != null)
{
tempContainer.Image = buffer;
MainWindow.mainWindowDispacter.BeginInvoke(new Action(() =>
MainWindow.mainWindow.UpdateRowContainer(tempContainer, 0)));
}
else
{
MainWindow.mainWindowDispacter.BeginInvoke(new Action(() =>
MainWindow.mainWindow.CreateClientRowContainer(c, buffer)));
}
return;
}
if (msg.Length < 4)
return;
// The rest of the image is handle here like its some sort of string message...
String switchString = msg.Substring(0, 4);
if (msg.Length > 4)
msg = msg.Substring(4);
MainWindow.mainWindowDispacter.BeginInvoke(new Action(() =>
{
if (MainWindow.debugWindow != null)
MainWindow.debugWindow.LogTextBox.AppendText("Received message " + msg + " from client: " + c.getClientIp() + " as a " + switchString + " type" + Environment.NewLine);
}));
switch (switchString)
{
case "pong":
c.RespondedPong();
break;
case "stat":
RowContainer tContain = MainWindow.mainWindow.RowExists(c);
if (tContain == null)
break;
tContain.SetState(msg);
MainWindow.mainWindow.UpdateRowContainer(tContain, 1);
break;
}
});
}
}
Here is some updated code that works for those who need to understand it.
Server side:
if (stream.DataAvailable)
{
Task DataInterpetTask = Task.Factory.StartNew(() =>
{
int totalBuffer, totalRecieved = 0;
byte[] totalBufferByte = new byte[4];
byte[] buffer = new byte[0];
byte[] tbuffer;
int rLength, prevLength;
byte[] buf = new byte[c.getClientSocket().ReceiveBufferSize];
stream.Read(totalBufferByte, 0, 4);
totalBuffer = BitConverter.ToInt32(totalBufferByte, 0);
totalBuffer = totalBuffer - 3;
while (totalBuffer > totalRecieved)
{
rLength = stream.Read(buf, 0, buf.Length);
totalRecieved = rLength + totalRecieved;
if (rLength < buf.Length)
{
byte[] temp = new byte[rLength];
Array.Copy(buf, temp, rLength);
buf = temp;
}
prevLength = buffer.Length;
tbuffer = buffer;
buffer = new byte[buffer.Length + rLength];
Array.Copy(tbuffer, buffer, tbuffer.Length);
buf.CopyTo(buffer, prevLength);
}
String msg = Encoding.ASCII.GetString(buffer);
if (msg.Contains("PNG") || msg.Contains("RBG") || msg.Contains("HDR"))
{
RowContainer tempContainer;
if ((tempContainer = MainWindow.mainWindow.RowExists(c)) != null)
{
tempContainer.Image = buffer;
MainWindow.mainWindowDispacter.BeginInvoke(new Action(() =>
MainWindow.mainWindow.UpdateRowContainer(tempContainer, 0)));
}
else
{
MainWindow.mainWindowDispacter.BeginInvoke(new Action(() =>
MainWindow.mainWindow.CreateClientRowContainer(c, buffer)));
}
return;
}
String switchString = msg.Substring(0, 4);
if (msg.Length > 4)
msg = msg.Substring(4);
MainWindow.mainWindowDispacter.BeginInvoke(new Action(() =>
{
if (MainWindow.debugWindow != null)
MainWindow.debugWindow.LogTextBox.AppendText("Received message " + msg + " from client: " + c.getClientIp() + " as a " + switchString + " type" + Environment.NewLine);
}));
switch (switchString)
{
case "pong":
c.RespondedPong();
break;
case "stat":
RowContainer tContain = MainWindow.mainWindow.RowExists(c);
if (tContain == null)
break;
tContain.SetState(msg);
MainWindow.mainWindow.UpdateRowContainer(tContain, 1);
break;
}
});
}
}
Client side:
public void SendResponse(int command, Object[] args)
{
if (ClientSocket == null)
{
Console.WriteLine("Command: ClientSocket");
return;
}
var serverStream = this.ClientSocket.GetStream();
if (!serverStream.CanRead || !serverStream.CanWrite)
{
Console.WriteLine("Command: serverStream Error");
return;
}
byte[] toSend = null;
switch (command)
{
// 0 - genneral, 1 - handshake response
case 0:
toSend = Encoding.ASCII.GetBytes(args[0].ToString());
break;
case 1:
Rectangle bounds = Screen.GetBounds(Point.Empty);
using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size);
}
toSend = ImageToByte(bitmap);
}
break;
}
if (toSend == null)
Console.WriteLine("what happened");
try
{
byte[] bufferedToSend = new byte[toSend.Length + 4];
byte[] lengthOfSend = BitConverter.GetBytes(toSend.Length);
Array.Copy(lengthOfSend, bufferedToSend, 4);
toSend.CopyTo(bufferedToSend, 4);
Console.WriteLine("Sending " + toSend.Length + " bytes" + " buffer len: "+bufferedToSend.Length);
serverStream.Write(bufferedToSend, 0, bufferedToSend.Length);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
I'm trying to stream a video feed form the Xbox Kinect from a client to a server. I got it working with TCP but I could only get about 5 fps so now I'm trying it with UDP. UDP should be faster because of how the protocol works but it seems to be slower. Here is my post about the TCP (http://stackoverflow.com/questions/9627242/c-sharp-streaming-video-over-networkstream-tcpclient)
I can send this all the data I want over my LAN but I start losing a lot of packets if I push them out too fast. That is why I am using the Thread.Sleep(20); Increasing the chuck size speeds it up a lot but I'm at my max for sending over my LAN and if I understand correctly the max chunk size for sending over the internet is about 1500 bytes. If I only sent 1500 bytes at a time this would go really slow. I must be doing something wrong.
Here is the code.
private const int constChunkSize = 38400;
protected UdpClient udpObject;
private void HandleComm()
{
byte[] fullMessage = new byte[1228800];
byte[] byteReceived;
int currentIndex = 0;
IPEndPoint remoteIPEndPoint = new IPEndPoint(ip, port);
while (true)
{
byteReceived = udpObject.Receive(ref remoteIPEndPoint);
if (currentIndex + byteReceived.Length > 1228800)
{
int wtf = 0;
}
Array.Copy(byteReceived, 0, fullMessage, currentIndex, byteReceived.Length);
currentIndex += byteReceived.Length;
//Console.WriteLine("Recieved: " + currentIndex);
if (currentIndex == 1228800)
{
if (OnDataReceived != null)
{
FrameReceivedArgs args = new FrameReceivedArgs();
args.frame = new byte[fullMessage.Length];
fullMessage.CopyTo(args.frame, 0);
OnDataReceived(this, args);
}
currentIndex = 0;
Console.WriteLine("Done receiving" + DateTime.Now.Ticks);
}
}
}
public void sendData(byte[] data)
{
sending = true;
sendThread = new Thread(sendDataThread);
sendThread.Priority = ThreadPriority.Highest;
sendThread.Start(data);
}
private void sendDataThread(object tempData)
{
byte[] data = (byte[]) tempData;
int totalBytes = data.Length;
int currentBytes = 0;
int bufferLength = constChunkSize;
byte[] sendBytes = new byte[constChunkSize];
while (currentBytes < totalBytes)
{
if (totalBytes - currentBytes < constChunkSize)
bufferLength = totalBytes - currentBytes;
Array.Copy(data, currentBytes, sendBytes, 0, bufferLength);
currentBytes += bufferLength;
udpObject.BeginSend(sendBytes, bufferLength, new AsyncCallback(sendingData), udpObject);
Thread.Sleep(20);
//Console.WriteLine("Sent: " + currentBytes);
}
Console.WriteLine("done sending" + DateTime.Now.Ticks);
sending = false;
}
private void sendingData(IAsyncResult ar)
{
udpObject.EndSend(ar);
}