Go unix domain socket通信

简介: Go unix domain socket通信

Go unix domain socket通信

  ‍

  socket大家应该很熟悉,以tcp/ip协议族为传输协议,用于跨主机通信,而unixsocket就是在socket的框架上发展出一种IPC机制(进程间通信),UDS(UNIX Domain Socket)提供面向流和面向数据包两种API接口,类似于TCP和UDP,其中SOCK_STREAM是很可靠的,消息既不会丢失也不会顺序错乱,比传统的socket效率更高,一般是tcp传输的两倍,并且不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程

  unixsocket服务端和客户端连接及发送消息的过程如下:

  ​image

  ‍

C实现客户端与服务器

server

#include <sys/types.h>  
#include <sys/socket.h>  
#include <sys/un.h>  
#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  

int main()  
{
   
     
  /* delete the socket file */  
  unlink("server_socket");  

  /* create a socket */  
  int server_sockfd = socket(AF_UNIX, SOCK_STREAM, 0);  

  struct sockaddr_un server_addr;  
  server_addr.sun_family = AF_UNIX;  
  strcpy(server_addr.sun_path, "server_socket");  

  /* bind with the local file */  
  bind(server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));  

  /* listen */  
  listen(server_sockfd, 5);  

  char ch;  
  int client_sockfd;  
  struct sockaddr_un client_addr;  
  socklen_t len = sizeof(client_addr);  
  while(1)  
  {
   
     
    printf("server waiting:\n");  

    /* accept a connection */  
    client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_addr, &len);  

    /* exchange data */  
    read(client_sockfd, &ch, 1);  
    printf("get char from client: %c\n", ch);  
    ++ch;  
    write(client_sockfd, &ch, 1);  

    /* close the socket */  
    close(client_sockfd);  
  }  

  return 0;  
}

socket

  函数原型:

int socket(int domain, int type, int protocol)
  • domain:指定socket所属的域,常用的是AF_UNIX或AF_INET

    • AF_UNIX表示以文件方式创建socket,
    • AF_INET表示以端口方式创建socket
  • type:指定socket的类型,可以是SOCK_STREAM或SOCK_DGRAM

    • SOCK_STREAM:表示创建一个有序的,可靠的,面向连接的socket,因此如果我们要使用TCP,就应该指定为SOCK_STREAM
    • SOCK_DGRAM:表示创建一个不可靠的,无连接的socket,因此如果我们要使用UDP,就应该指定为SOCK_DGRAM
  • protocol:指定socket的协议类型,我们一般指定为0表示由第一第二两个参数自动选择。
  • socket()函数返回新创建的socket,出错则返回-1

地址格式

  地址格式:

  常用的有两种socket域:AF_UNIX或AF_INET,因此就有两种地址格式:sockaddr_un和sockaddr_in,分别定义如下:

struct sockaddr_un  
{
   
     
  sa_family_t sun_family;  /* AF_UNIX */  
  char sun_path[];         // socket的本地文件名
}
struct sockaddr_in  
{
   
     
  short int sin_family;          /* AF_INET */  
  unsigned short int sin_port;   // socket 端口号
  struct in_addr sin_addr;       // socket ip地址
}  

// 其中in_addr正是用来描述一个ip地址的:
struct in_addr  
{
   
     
  unsigned long int s_addr;  
}

bind

  创建完一个socket后,我们需要使用bind将其绑定:

int bind(int socket, const struct sockaddr * address, size_t address_len)

  如果我们使用AF_UNIX来创建socket,相应的地址格式是sockaddr_un,而如果我们使用AF_INET来创建socket,相应的地址格式是sockaddr_in,因此我们需要将其强制转换为sockaddr这一通用的地址格式类型,而sockaddr_un中的sun_family和sockaddr_in中的sin_family分别说明了它的地址格式类型,因此bind()函数就知道它的真实的地址格式。第三个参数address_len则指明了真实的地址格式的长度。

  bind()函数正确返回0,出错返回-1

listen

  接下来我们需要开始监听了:

int listen(int socket, int backlog)

  backlog:等待连接的最大个数,如果超过了这个数值,则后续的请求连接将被拒绝

  listen()函数正确返回0,出错返回-1

accept

  接受连接:

int accept(int socket, struct sockaddr * address, size_t * address_len)

  同样,第二个参数也是一个通用地址格式类型,这意味着我们需要进行强制类型转化

  这里需要注意的是,address是一个传出参数,它保存着接受连接的客户端的地址,如果我们不需要,将address置为NULL即可。

  address_len:我们期望的地址结构的长度,注意,这是一个传入和传出参数,传入时指定我们期望的地址结构的长度,如果多于这个值,则会被截断,而当accept()函数返回时,address_len会被设置为客户端连接的地址结构的实际长度。

  另外如果没有客户端连接时,accept()函数会阻塞

  accept()函数成功时返回新创建的socket描述符,出错时返回-1

read/write

  可以通过read/write来传递数据

close

  通信完成后,我们需要关闭socket:

  int close(int fd)

  close是一个通用函数(和read,write一样),不仅可以关闭文件描述符,还可以关闭socket描述符

client

#include <sys/types.h>  
#include <sys/socket.h>  
#include <sys/un.h>  
#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  

int main()  
{
   
     
  /* create a socket */  
  int sockfd = socket(AF_UNIX, SOCK_STREAM, 0);  


  struct sockaddr_un address;  
  address.sun_family = AF_UNIX;  
  strcpy(address.sun_path, "server_socket");  

  /* connect to the server */  
  int result = connect(sockfd, (struct sockaddr *)&address, sizeof(address));  
  if(result == -1)  
  {
   
     
    perror("connect failed: ");  
    exit(1);  
  }  

  /* exchange data */  
  char ch = 'A';  
  write(sockfd, &ch, 1);  
  read(sockfd, &ch, 1);  
  printf("get char from server: %c\n", ch);  

  /* close the socket */  
  close(sockfd);  

  return 0;  
}

connect

  客户端通过connect()函数与服务器连接:

int connect(int socket, const struct sockaddr * address, size_t address_len)

  对于第二个参数,我们同样需要强制类型转换

  address_len指明了地址结构的长度

  connect()函数成功时返回0,出错时返回-1

makefile

all: tcp_client.c tcp_server.c  
    gcc -g -Wall -o tcp_client tcp_client.c  
    gcc -g -Wall -o tcp_server tcp_server.c  

clean:  
    rm -rf *.o tcp_client tcp_server

Go实现客户端与服务器

服务器

package main

import (
        "bufio"
        "fmt"
        "net"
        "time"
)

func main() {
   
   
        var unixAddr *net.UnixAddr

        unixAddr, _ = net.ResolveUnixAddr("unix", "/tmp/unix_sock")

        unixListener, _ := net.ListenUnix("unix", unixAddr)

        defer unixListener.Close()

        for {
   
   
                unixConn, err := unixListener.AcceptUnix()
                if err != nil {
   
   
                        continue
                }

                fmt.Println("A client connected : " + unixConn.RemoteAddr().String())
                go unixPipe(unixConn)
        }

}

func unixPipe(conn *net.UnixConn) {
   
   
        ipStr := conn.RemoteAddr().String()
        defer func() {
   
   
                fmt.Println("disconnected :" + ipStr)
                conn.Close()
        }()
        reader := bufio.NewReader(conn)

        for {
   
   
                message, err := reader.ReadString('\n')
                if err != nil {
   
   
                        return
                }

                fmt.Println(string(message))
                msg := time.Now().String() + "\n"
                b := []byte(msg)
                conn.Write(b)
        }
}

客户端

package main

import (
        "bufio"
        "fmt"
        "net"
        "time"
)

var quitSemaphore chan bool

func main() {
   
   
        var unixAddr *net.UnixAddr
        unixAddr, _ = net.ResolveUnixAddr("unix", "/tmp/unix_sock")

        conn, _ := net.DialUnix("unix", nil, unixAddr)
        defer conn.Close()
        fmt.Println("connected!")

        go onMessageRecived(conn)

        b := []byte("time\n")
        conn.Write(b)

        <-quitSemaphore
}

func onMessageRecived(conn *net.UnixConn) {
   
   
        reader := bufio.NewReader(conn)
        for {
   
   
                msg, err := reader.ReadString('\n')
                fmt.Println(msg)
                if err != nil {
   
   
                        quitSemaphore <- true
                        break
                }
                time.Sleep(time.Second)
                b := []byte(msg)
                conn.Write(b)
        }
}

参考资料

  好用的进程间通信方式---UnixDomainSocket

  本地socket unix domain socket_bingqingsuimeng的博客-CSDN博客

  本机网络 IO 之 Unix Domain Socket 性能分析 - 知乎 (zhihu.com)

  https://blog.csdn.net/Nurke/article/details/77621782

  go语言实现unix domain socket 客户端/服务端_ebayboy的技术博客_51CTO博客

  ‍

相关文章
|
网络协议 安全 Unix
UNIX域套接字(Unix Domain Socket,UDS)之所以高效
UNIX域套接字(Unix Domain Socket,UDS)之所以高效
816 3
|
监控 安全 Unix
UNIX域套接字(Unix Domain Socket)在安全性和隐私性
UNIX域套接字(Unix Domain Socket)在安全性和隐私性
619 2
|
7月前
|
存储 算法 物联网
解析局域网内控制电脑机制:基于 Go 语言链表算法的隐秘通信技术探究
数字化办公与物联网蓬勃发展的时代背景下,局域网内计算机控制已成为提升工作效率、达成设备协同管理的重要途径。无论是企业远程办公时的设备统一调度,还是智能家居系统中多设备间的联动控制,高效的数据传输与管理机制均构成实现局域网内计算机控制功能的核心要素。本文将深入探究 Go 语言中的链表数据结构,剖析其在局域网内计算机控制过程中,如何达成数据的有序存储与高效传输,并通过完整的 Go 语言代码示例展示其应用流程。
142 0
|
网络协议 Java Go
【Go语言专栏】Go语言中的WebSocket实时通信应用
【4月更文挑战第30天】Go语言(Golang)是Google开发的编程语言,适用于云计算、微服务等领域。本文介绍了WebSocket,一种实现浏览器与服务器全双工通信的协议,其特点是实时性、全双工和轻量级。在Go中实现WebSocket,可以使用gorilla/websocket库。示例展示了如何创建服务器端和客户端,实现消息的收发。WebSocket广泛应用于聊天、游戏、通知推送和实时数据同步等场景。学习Go语言中的WebSocket对于开发实时通信应用至关重要。
556 0
|
缓存 监控 前端开发
Go 语言中如何集成 WebSocket 与 Socket.IO,实现高效、灵活的实时通信
本文探讨了在 Go 语言中如何集成 WebSocket 与 Socket.IO,实现高效、灵活的实时通信。首先介绍了 WebSocket 和 Socket.IO 的基本概念及其优势,接着详细讲解了 Go 语言中 WebSocket 的实现方法,以及二者集成的重要意义和具体步骤。文章还讨论了集成过程中需要注意的问题,如协议兼容性、消息格式、并发处理等,并提供了实时聊天、数据监控和在线协作工具等应用案例,最后提出了性能优化策略,包括数据压缩、缓存策略和连接管理优化。旨在帮助开发者更好地理解并应用这些技术。
642 3
|
网络协议 Linux Go
Go语言TCP Socket编程(下)
Go语言TCP Socket编程
256 1
|
安全 Java Go
【Go语言专栏】Go语言中的加密与安全通信
【4月更文挑战第30天】本文介绍了Go语言中的加密与安全通信。通过使用golang.org/x/crypto/ssh/terminal库实现终端加密,以及golang.org/x/net/websocket库实现WebSocket安全通信。文章展示了安装库的命令、加密操作及WebSocket通信的示例代码。此外,还列举了安全通信在数据传输加密、用户认证、密码保护和文件加密等场景的应用。掌握这些知识对开发安全的Web应用至关重要。
241 0
|
缓存 监控 前端开发
【Go 语言专栏】Go 语言中的 WebSocket 与 Socket.IO 集成
【4月更文挑战第30天】本文介绍了在 Go 语言中集成 WebSocket 与 Socket.IO 的相关技术,WebSocket 是一种高效的双向通信协议,Socket.IO 是一个实时通信库,提供丰富的事件处理。集成两者能实现更强大的实时通信功能。文章讨论了 Go 中 WebSocket 的实现,Socket.IO 与 WebSocket 的关系,集成的意义及步骤,并提醒注意协议兼容性、消息格式等问题。此外,还提到了性能优化策略和应用案例,如实时聊天、数据监控和在线协作工具。通过集成,开发者可以构建出满足多样化需求的实时通信应用。
614 0
|
缓存 监控 前端开发
【Go 语言专栏】Go 语言中的 WebSocket 实时通信应用
【4月更文挑战第30天】本文探讨了Go语言在WebSocket实时通信中的应用。WebSocket作为全双工通信协议,允许持续的双向通信。Go语言凭借其高效和并发特性,适合构建实时应用。文中概述了在Go中实现WebSocket的基本步骤,包括服务器和客户端的建立与通信,并列举了实时聊天、数据监控和在线协作等应用案例。同时,强调了消息格式、并发处理、错误处理和安全性的注意事项。通过数据压缩、缓存管理和连接管理等策略可优化性能。Go语言还能与数据库和前端框架结合,提升用户体验。总之,Go语言为WebSocket实时通信提供了强大支持,有望在更多领域发挥作用。
278 0
|
网络协议 Ubuntu Unix
Go语言TCP Socket编程(上)
Go语言TCP Socket编程
320 0