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

class server
{
    static void Main()
    {
        string path = @"C:\temp\test.sock";
        Socket serverSocket = null;

        try
        {
            if (File.Exists(path))
                File.Delete(path);

            var endpoint = new UnixDomainSocketEndPoint(path);

            serverSocket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified);

            serverSocket.Bind(endpoint);
            serverSocket.Listen(5);

            Console.WriteLine("Unix socket ready: " + path);
            Console.WriteLine("Press Ctrl+C to stop.");

            while (true)
            {
                try
                {
                    Socket client = serverSocket.Accept();

                    byte[] buffer = new byte[1024];
                    int len = client.Receive(buffer);

                    string msg = Encoding.UTF8.GetString(buffer, 0, len);
                    Console.WriteLine("Received: " + msg);

                    client.Send(Encoding.UTF8.GetBytes("hello"));

                    client.Close();
                }
                catch (SocketException ex)
                {
                    Console.WriteLine("Client socket error: " + ex.Message);
                }
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine("Server error:");
            Console.WriteLine(ex.ToString());
        }
        finally
        {
            if (serverSocket != null)
            {
                try
                {
                    serverSocket.Close();
                }
                catch { }
            }

            Console.WriteLine();
            Console.WriteLine("Press any key to exit...");
            Console.ReadKey();
        }
    }
}
using System;
using System.Net.Sockets;
using System.Text;

class Client
{
    static void Main()
    {
        var endpoint = new UnixDomainSocketEndPoint(@"C:\temp\test.sock");

        Socket client = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified);

        client.Connect(endpoint);

        client.Send(Encoding.UTF8.GetBytes("ping"));

        byte[] buffer = new byte[1024];
        int len = client.Receive(buffer);

        Console.WriteLine("Server: " + Encoding.UTF8.GetString(buffer, 0, len));

        client.Close();
    }
}


这段代码使用的是 Unix Domain Socket(UDS),而传统的 127.0.0.1:xxxx 属于 TCP/IP Socket(AF_INET)。两者都是进程间通信(IPC)机制,但工作层次和行为差异很大。下面从 架构、性能、安全、系统实现、沙箱行为几个关键角度说明。


一、两种 Socket 的核心区别

特性 Unix Domain Socket TCP Socket (127.0.0.1:port)
地址形式 文件路径 C:\temp\test.sock IP + Port 127.0.0.1:5000
协议族 AF_UNIX AF_INET / AF_INET6
网络协议栈 不经过 TCP/IP 必须走 TCP/IP
通信范围 仅本机 本机或网络
访问控制 文件权限 端口权限/防火墙
性能 更高(少一层协议) 略低
安全性 更高(无端口) 需要端口
可被扫描 不能 可以

二、数据路径差异(最重要)

1 Unix Domain Socket

数据路径:

进程A
  ↓
AF_UNIX
  ↓
Kernel IPC
  ↓
进程B

特点:

  • 完全不走网络栈

  • 内核直接复制内存

  • 延迟更低


2 TCP Socket (127.0.0.1)

数据路径:

进程A
  ↓
TCP/IP stack
  ↓
loopback interface
  ↓
TCP/IP stack
  ↓
进程B

即使是 127.0.0.1 也会经过:

TCP
IP
Loopback driver

所以会多一层协议处理。


三、性能差异(实际)

一般 IPC benchmark:

类型 延迟 吞吐
Unix Socket 最低 最高
Named Pipe 中等
TCP localhost 较高 中等

在 Linux 中:

Unix Socket ≈ 2x TCP

Windows 上差距稍小。


四、系统实现差异

Unix Socket

创建:

socket(AF_UNIX)
bind("/tmp/test.sock")
listen()

路径是 filesystem namespace

你代码中的:

C:\temp\test.sock

只是一个 socket endpoint 文件


TCP Socket

创建:

socket(AF_INET)
bind(127.0.0.1:5000)
listen()

端口由 network stack 管理。


五、权限控制机制

Unix Socket

通过 文件权限控制

Linux:

srwxrwxr-x test.sock

Windows:

NTFS ACL

TCP Socket

通过:

port
firewall
network ACL

六、典型应用

Unix Domain Socket 常见用途:

软件 用途
Docker container runtime
PostgreSQL 本地连接
MySQL local socket
Nginx fastcgi
Redis IPC
JetBrains IDE DirectoryLock

例如 PyCharm 就使用:

UnixDomainSocket

来做 IDE 单实例锁。


七、在 Sandboxie 中的行为

Sandboxie 对 IPC 的处理非常关键。

1 TCP Socket

在沙箱中:

127.0.0.1:5000

通常 不会被阻止

因为 TCP/IP 属于:

network stack

沙箱不会拦截。


2 Unix Domain Socket

Unix socket 属于:

filesystem namespace
+ kernel IPC

Sandboxie 会:

路径虚拟化
IPC隔离

可能出现:

现象 原因
BindException socket endpoint冲突
AccessDenied 路径虚拟化
残留.sock文件 内核对象未释放

八、Sandboxie 下 socket 文件路径

真实路径:

C:\temp\test.sock

沙箱运行时可能变成:

C:\Sandbox\<user>\<box>\drive\C\temp\test.sock

这就是为什么:

File.Delete()

有时会失败。


九、JetBrains IDE 的问题来源

PyCharm 启动时执行:

DirectoryLock.tryListen
   ↓
UnixDomainSockets.bind

如果 Sandboxie 拦截:

bind()

就会出现:

java.net.BindException
Address already in use

实际上并不是端口冲突,而是:

IPC namespace冲突

十、一个关键对比

行为 Unix Socket TCP
netstat可见
端口扫描
沙箱影响 较大 较小
文件系统参与

十一、你的代码行为

Server:

bind(C:\temp\test.sock)
listen
accept

Client:

connect(C:\temp\test.sock)
send
recv

通信完全在:

kernel IPC

完成。


十二、什么时候应该使用哪种

建议:

场景 推荐
本机服务 Unix Socket
跨主机通信 TCP
高性能IPC Unix Socket
简单调试 TCP

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐