[英]How to connect to a Unix Domain Socket in .NET Core in C#
I've created a unix socket in .NET Core on Linux (Ubuntu 16.04):我在 Linux (Ubuntu 16.04) 上的 .NET Core 中创建了一个 unix 套接字:
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: .NET Core API 列出了各种Socket.Connect选项,但除了第一个都处理 IP 地址:
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)
System.Net API定义了一个
DNSEndpoint
和一个IPEndpoint
,但我似乎无法找到一个UnixEndpoint
或类似的类来传递给Socket.Connect(EndPoint remoteEP)
Update: .NET Standard 2.1 / .NET Core 2.1 contains a UnixDomainSocketEndPoint Class更新:.NET Standard 2.1 / .NET Core 2.1 包含一个UnixDomainSocketEndPoint 类
Original answer, applies to versions of .NET Standard / .NET Core prior to 2.1:原始答案,适用于 2.1 之前的 .NET Standard / .NET Core 版本:
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.在
UnixEndPoint
似乎没有用于 .NET Core 的内置UnixEndPoint
类或实现该类的库。 The UnixEndPoint class from the Mono.Posix project can be easily adapted to work with .NET Core, however: Mono.Posix项目中的UnixEndPoint 类可以很容易地适应与 .NET Core 一起使用,但是:
// 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 (gonzalo@ximian.com)
//
// (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);
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));
Because I couldn't find a good example for unix sockets I designed my own.因为我找不到我自己设计的 Unix 套接字的好例子。 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.我用 .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 :)如果您发现错误或可以进行改进,请告诉我,我会看看我们是否可以将其添加到示例中:)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.