0

I'm new to C# sockets and have tried the Microsoft example in here but I can't for the life of me get it to do anything. I'm trying to get it to listen on localhost port 12000 but I can't connect via telnet at all. Has anybody had success with this example?

using System.Net.Sockets;
using System.Net;
using System.Text;


IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, 12000);

using Socket listener = new(
ipEndPoint.AddressFamily,
SocketType.Stream,
ProtocolType.Tcp);

listener.Bind(ipEndPoint);
listener.Listen(100);

var handler = await listener.AcceptAsync();
while (true)
{
    // Receive message.
   var buffer = new byte[1_024];
   var received = await handler.ReceiveAsync(buffer, SocketFlags.None);
   var response = Encoding.UTF8.GetString(buffer, 0, received);

var eom = "<|EOM|>";
if (response.IndexOf(eom) > -1 /* is end of message */)
{
    Console.WriteLine(
        $"Socket server received message: \"{response.Replace(eom, "")}\"");

    var ackMessage = "<|ACK|>";
    var echoBytes = Encoding.UTF8.GetBytes(ackMessage);
    await handler.SendAsync(echoBytes, 0);
    Console.WriteLine(
        $"Socket server sent acknowledgment: \"{ackMessage}\"");

    break;
}
// Sample output:
//    Socket server received message: "Hi friends !"
//    Socket server sent acknowledgment: "<|ACK|>"

}

zobbo
  • 117
  • 2
  • 13
  • 2
    Word of warning: Microsoft socket code is nowhere near production quality *at all*. They are examples of "how to pass parameters to this .NET method"; they are *not* examples of "how to write a socket server in C#". They require *significant* and *non-obvious* modifications before they can be used in real-world code. – Stephen Cleary Jun 10 '23 at 14:53
  • 1
    The article that you provided a URL to doesn't mention _telnet_ - the code for the client and server are meant to be used together. Also, `new byte[1_024];` should probably be `new byte[1024];`. – Tu deschizi eu inchid Jun 10 '23 at 15:20
  • 1
    @Tudeschizieuinchid C# lets you put underscores in literal numbers. They're non-functional / cosmetic sugar. IE, I could write `new byte[1_048_576]`. They basically can represent commas to make literal numbers easier to read by humans. – B.O.B. Jun 10 '23 at 15:36
  • @B.O.B.: Thanks for the info. I didn't know that. – Tu deschizi eu inchid Jun 10 '23 at 15:38
  • @Tudeschizieuinchid I couldn't find an official Microsoft post on it (I'm sure one exists - just failing at the google). Here's a Jetbrains one if you want more info: https://blog.jetbrains.com/dotnet/2018/05/07/leading-digit-separators-ref-structs-parameters-c-7-2-rider-resharper/ – B.O.B. Jun 10 '23 at 15:41
  • @B.O.B.: It's ok. I probably won't use it. Here's a SO post for [digit separator](https://stackoverflow.com/questions/43476056/what-do-the-underscores-mean-in-a-numeric-literal-in-c). Looks like it's also mentioned in [Floating-point numeric types (C# reference)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/floating-point-numeric-types#real-literals). – Tu deschizi eu inchid Jun 10 '23 at 15:54
  • Using a telnet client is irrelevant it's still connecting to the same address and socket. It should work fine. – zobbo Jun 10 '23 at 17:56
  • 1
    Is there any reason you want to mess around with raw sockets and not just use `TcpListener` which handles a lot of things for you? You say Telnet doesn't work, what are you actually doing to prove that? Bear in mind the obvious issue with your code here: no message framing, because it's possible that a single message is split over multiple `Receive` – Charlieface Jun 10 '23 at 23:50
  • do you see the app waiiting on 12000? use `netstat -a`. Turn off windows firewall. What does the telnet client say is the problem? Put more trace statements in the code – pm100 Jun 12 '23 at 01:02
  • I can see it listening: TCP 0.0.0.0:12000 MyLaptop:0 LISTENING – zobbo Aug 27 '23 at 13:08

2 Answers2

1

Here's a shorter version that can be used with telent. However, this version doesn't support multiple clients. It also disconnects after a single message transmission. One adds the text <|EOM|> to the end of the message to indicate that it's the end of the message.

Here's a message example: Hello World <|EOM|>

using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

namespace SocketsTest
{
    public class Server
    {
        private async Task HandleCommunication(ListenerClient client)
        {
            //create reference
            Socket handler = client.ClientSocket;

            if (handler == null)
                return;

            //holds all data received
            byte[] cumulativeBuffer = new byte[1024];
            int bufferPosition = 0;

            //send NULL to client
            await handler.SendAsync(new byte[] { 0x00}, 0);

            //send message to client
            await handler.SendAsync(Encoding.UTF8.GetBytes("Welcome"), SocketFlags.None);

            //send CRLF (newline) to client
            await handler.SendAsync(new byte[] { 0xD, 0xA }, SocketFlags.None);

            //send message to client
            await handler.SendAsync(Encoding.UTF8.GetBytes("At the end of the message type \"<|EOM|>\" which signals"), SocketFlags.None);

            //send CRLF (newline) to client
            await handler.SendAsync(new byte[] { 0xD, 0xA }, SocketFlags.None);

            //send message to client
            await handler.SendAsync(Encoding.UTF8.GetBytes("that this is the end of the message (ie: end of the transmission)"), SocketFlags.None);

            //send CRLF (newline) to client
            await handler.SendAsync(new byte[] { 0xD, 0xA, 0xD, 0xA }, SocketFlags.None);

            //send message to client
            await handler.SendAsync(Encoding.UTF8.GetBytes("Please enter a message"), SocketFlags.None);

            //send CRLF (newline) to client
            await handler.SendAsync(new byte[] { 0xD, 0xA }, SocketFlags.None);

            while (true)
            {
                //loops each time data is received
                //when using telnet, this is when a single character has been received

                //holds the current data received
                byte[] currentDataBuffer = new byte[1024];

                //wait for data from client
                int bytesReceived = await handler.ReceiveAsync(currentDataBuffer, SocketFlags.None);

                if (bytesReceived > 0)
                {
                    //copy data received from the current data buffer to cumulative buffer
                    Buffer.BlockCopy(currentDataBuffer, 0, cumulativeBuffer, bufferPosition, bytesReceived);

                    //set value
                    bufferPosition += bytesReceived;

                    //send NULL to client; this echos (ie: displays) the input
                    await handler.SendAsync(new byte[] { 0x00 }, SocketFlags.None);
                }

                //get cumulative message
                //a message is complete when "<|EOM|>" is found
                string cumulativeMessage = Encoding.UTF8.GetString(cumulativeBuffer, 0, bufferPosition);

                string eom = "<|EOM|>";
                if (cumulativeMessage.IndexOf(eom) > -1) //is end of message
                {
                    //send CRLF (newline) to client
                    await handler.SendAsync(new byte[] { 0xD, 0xA }, SocketFlags.None);

                    string ackMessage = "<|ACK|>";
                    var echoBytes = Encoding.UTF8.GetBytes(ackMessage);

                    //send message to client 
                    await handler.SendAsync(echoBytes, SocketFlags.None);

                    //send CRLF (newline) to client
                    await handler.SendAsync(new byte[] { 0xD, 0xA }, SocketFlags.None);

                    //send message to client
                    await handler.SendAsync(Encoding.UTF8.GetBytes("Goodbye"), SocketFlags.None);

                    //send CRLF (newline) to client
                    await handler.SendAsync(new byte[] { 0xD, 0xA }, SocketFlags.None);

                    //end of message was found, so there won't be any more data
                    //exit loop
                    break;
                }
            }
        }


        public async Task Start(int portNumber, int backlog = Int32.MaxValue)
        {
            //listen on all - 0.0.0.0
            IPAddress ipAddress = IPAddress.Any;
            IPEndPoint ipEndPoint = new(ipAddress, portNumber);

            using Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            listener.Bind(ipEndPoint);
            listener.Listen(100);

            //wait for connection
            Socket handler = await listener.AcceptAsync();

            //connected to client; handle communication
            await HandleCommunication(new ListenerClient(Guid.NewGuid().ToString("N"), handler));

            handler.Shutdown(SocketShutdown.Both);
            handler.Close();      
        }
    }

    internal class ListenerClient
    {
        public Socket ClientSocket { get; private set; }
        public string Id { get; private set; }

        public DateTime ConnectedOn { get; private set; }

        public ListenerClient(string id, Socket clientSocket)
        {
            Id = id;
            ClientSocket = clientSocket;
        }

        public ListenerClient(string id, Socket clientSocket, DateTime connectedOn)
        {
            Id = id;
            ClientSocket = clientSocket;
            ConnectedOn = ConnectedOn;
        }
    }
}

Adapted from here,

Tu deschizi eu inchid
  • 4,117
  • 3
  • 13
  • 24
  • Thanks for going to so much effort @Tu deschizi eu inchid . Yes I've started working with Async Tasks at work so this is definitely the way to go compared to very basic example MS gave. – zobbo Aug 27 '23 at 13:18
0

I got a connection at least, partly because @pm100 suggested looking at netstat. I could see there it was listed under the ip 0.0.0.0 and I was using the localhost ip. Once I used the proper ip I could connect.

Also once I had that working the loop is entered, but only after the telnet client is disconnected.

zobbo
  • 117
  • 2
  • 13