17

I've created a unix socket in .NET Core on Linux (Ubuntu 16.04):

var unixSocket = "/var/run/mysqld/mysqld.sock";
var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.IP);

Now how do I connect the socket?

The .NET Core API lists various Socket.Connect options, but all except for the first one deal with IP Addresses:

public void Connect(EndPoint remoteEP)
public void Connect(IPAddress address, int port)
public void Connect(IPAddress[] addresses, int port)
public void Connect(string host, int port)

The System.Net API defines a DNSEndpoint and an IPEndpoint, but I can't seem to find a UnixEndpoint or similar class to pass to Socket.Connect(EndPoint remoteEP)

3
  • @PeterDuniho I am using .NET core on Linux (updated my question to reflect this). Surely there is a way to make the POSIX call to open a unix domain socket? Please unmark this as duplicate, the marked duplicate does not relate to this question. Commented Oct 22, 2016 at 20:19
  • 3
    This is now available in .NET Standard 2.1 via UnixDomainSocketEndPoint. Commented Jan 2, 2019 at 23:33
  • @MarkG Add that as an answer please, so that it can be chosen / upvoted. Commented Jan 16, 2019 at 22:58

3 Answers 3

11

Update: .NET Standard 2.1 / .NET Core 2.1 contains a UnixDomainSocketEndPoint Class

Original answer, applies to versions of .NET Standard / .NET Core prior to 2.1:

There does not seem to be a built-in UnixEndPoint class for .NET Core or a library that implements one at the time of this writing. The UnixEndPoint class from the Mono.Posix project can be easily adapted to work with .NET Core, however:

// copied from https://github.com/mono/mono/blob/master/mcs/class/Mono.Posix/Mono.Unix/UnixEndPoint.cs

//
// Mono.Unix.UnixEndPoint: EndPoint derived class for AF_UNIX family sockets.
//
// Authors:
//  Gonzalo Paniagua Javier ([email protected])
//
// (C) 2003 Ximian, Inc (http://www.ximian.com)
//

//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

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

namespace System.Net
{

    public class UnixEndPoint : EndPoint
    {
        string filename;

        public UnixEndPoint (string filename)
        {
            if (filename == null)
                throw new ArgumentNullException ("filename");

            if (filename == "")
                throw new ArgumentException ("Cannot be empty.", "filename");
            this.filename = filename;
        }

        public string Filename {
            get {
                return(filename);
            }
            set {
                filename=value;
            }
        }

        public override AddressFamily AddressFamily {
            get { return AddressFamily.Unix; }
        }

        public override EndPoint Create (SocketAddress socketAddress)
        {
            /*
             * Should also check this
             *
            int addr = (int) AddressFamily.Unix;
            if (socketAddress [0] != (addr & 0xFF))
                throw new ArgumentException ("socketAddress is not a unix socket address.");
            if (socketAddress [1] != ((addr & 0xFF00) >> 8))
                throw new ArgumentException ("socketAddress is not a unix socket address.");
             */

            if (socketAddress.Size == 2) {
                // Empty filename.
                // Probably from RemoteEndPoint which on linux does not return the file name.
                UnixEndPoint uep = new UnixEndPoint ("a");
                uep.filename = "";
                return uep;
            }
            int size = socketAddress.Size - 2;
            byte [] bytes = new byte [size];
            for (int i = 0; i < bytes.Length; i++) {
                bytes [i] = socketAddress [i + 2];
                // There may be junk after the null terminator, so ignore it all.
                if (bytes [i] == 0) {
                    size = i;
                    break;
                }
            }

            string name = Encoding.UTF8.GetString (bytes, 0, size);
            return new UnixEndPoint (name);
        }

        public override SocketAddress Serialize ()
        {
            byte [] bytes = Encoding.UTF8.GetBytes (filename);
            SocketAddress sa = new SocketAddress (AddressFamily, 2 + bytes.Length + 1);
            // sa [0] -> family low byte, sa [1] -> family high byte
            for (int i = 0; i < bytes.Length; i++)
                sa [2 + i] = bytes [i];

            //NULL suffix for non-abstract path
            sa[2 + bytes.Length] = 0;

            return sa;
        }

        public override string ToString() {
            return(filename);
        }

        public override int GetHashCode ()
        {
            return filename.GetHashCode ();
        }

        public override bool Equals (object o)
        {
            UnixEndPoint other = o as UnixEndPoint;
            if (other == null)
                return false;

            return (other.filename == filename);
        }
    }
}

With this class in your project, the socket can be connected like so:

var unixSocket = "/var/run/mysqld/mysqld.sock";
var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.IP);
var unixEp = new UnixEndPoint(unixSocket);
socket.Connect(unixEp);
Sign up to request clarification or add additional context in comments.

2 Comments

There's already a UnixDomainSocketEndpoint since .net core 2.1: learn.microsoft.com/en-us/dotnet/api/…
@fbiagi could you please edit the answer and add some of the code from the docs into caleb answer? Because If I don't see your comment, It takes time for me to check the code that's caleb share... Thank you
2
using System.Net.Sockets;

var unixSocketName = "/var/run/mysqld/mysqld.sock";
var unixSocket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.IP);
unixSocket.Connect(new UnixDomainSocketEndPoint(unixSocketName));

Comments

1

Because I couldn't find a good example for unix sockets I designed my own. Its very rudementary and has not many errorcatching, but it works.

Here it is:

using System;
using System.Net.Sockets;
using System.Threading;

namespace mps.unix.socket
{

    public class UnixSocket
    {
        private Socket recvSocket;
        private string unixSocketpath;  // needed for cleanup
        private bool running = true;
        private byte[] data = new byte[0];

        public UnixSocket(string unixSocket)
        {
            this.unixSocketpath = unixSocket;
            this.recvSocket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified);

            System.IO.File.Delete(unixSocketpath);
            var ep = new UnixDomainSocketEndPoint(unixSocketpath);
            recvSocket.Bind(ep);

            recvSocket.Listen();

            Thread w = new Thread(worker);
            w.Start();
        }

        private void worker()
        {
            while (this.running)
            {
                try
                {
                    var clientSocket = this.recvSocket.Accept();                        // waits for 'client' to 'connect'

                    Thread t = new Thread(delegate () { clientWorker(clientSocket); });
                    t.Start();
                }
                catch (Exception) { }
            }
        }

        private void clientWorker(Socket s)
        {
            while (running)
            {
                byte[] d = new byte[s.ReceiveBufferSize];

                int length = s.Receive(d);

                lock (this.data)
                {
                    byte[] newData = new byte[this.data.Length + length];
                    Buffer.BlockCopy(this.data, 0, newData, 0, this.data.Length);
                    Buffer.BlockCopy(d, 0, newData, this.data.Length, length);

                    this.data = newData;
                }
            }

            s.Close();
        }

        public bool isRunning()
        {
            return this.running;
        }

        public void Stop()
        {
            this.running = false;
            Thread.Sleep(10);
            this.recvSocket.Close();
            System.IO.File.Delete(this.unixSocketpath);
        }

        public bool hasData()
        {
            return this.data.Length > 0;
        }

        public int dataLength()
        {
            lock (this.data)
                return this.data.Length;
        }

        public byte[] getData()
        {
            lock (this.data)
            {
                var d = this.data;
                this.data = new byte[0];

                return d;
            }
        }

        public byte[] getData(int length)
        {
            lock (this.data)
            {
                if (this.data.Length < length)
                    return this.getData();

                //var d = this.data.Take(length).ToArray();
                //var rest = new byte[0];

                byte[] taken = new byte[length];
                byte[] rest = new byte[this.data.Length - length];

                Buffer.BlockCopy(this.data, 0, taken, 0, length);
                Buffer.BlockCopy(this.data, length, rest, 0, rest.Length);

                this.data = rest;

                return taken;
            }
        }
    }
}

Here is a litte program where you can see how to use the class:

using System;
using System.Threading;
using mps.unix.socket;


namespace TEST_mps.unix.socket
{
    class Program { static void Main(string[] args) { var P = new TEST(); } }

    class TEST
    {
        public TEST()
        {
            var v = new UnixSocket("/tmp/test.mps.unix.sock");

            Thread t = new Thread(delegate () { runner(v); });
            t.Start();
        }

        private void runner(UnixSocket s)
        {
            while (s.isRunning())
            {
                Thread.Sleep(5);

                if (s.hasData())
                {
                    string responseData = System.Text.Encoding.ASCII.GetString(s.getData());
                    Console.WriteLine("Message received: {0}", responseData);

                    if (responseData == "END")
                        s.Stop();
                }
            }
        }
    }
}

I compiled this with .Net 6.0 for linux-x64.

For testing run the software and then send something to the socket:

On server:

root@server:# ./test.mps.unix.socket
Message received: this is a message for the socket!
Message received: here is another one..
Message received: END
root@server:#

From another shell:

root@server:# echo -n 'this is a message for the socket!' | socat UNIX-CONNECT:/tmp/test.mps.unix.sock STDIO
root@server:# echo -n 'here is another one..' | socat UNIX-CONNECT:/tmp/test.mps.unix.sock STDIO
root@server:# echo -n 'END' | socat UNIX-CONNECT:/tmp/test.mps.unix.sock STDIO

If you found bugs or can make improvements tell me and I will see if we can add it to the example :)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.