0

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.

Yorick
  • 51
  • 8
  • Shouldn't the server read from the socket? – 500 - Internal Server Error Apr 14 '21 at 16:31
  • Reading works just fine for the server. Reading does NOT work on the CLIENT. When the server writes to the client, the client ReceiveAsync() will block SendAsync() because it has received some data, but not data it can read succesfully. – Yorick Apr 14 '21 at 17:10

1 Answers1

0

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.

Yorick
  • 51
  • 8