当前位置:   article > 正文

Linux 多进程通信开发(七): unix domain socket 之 UDP 通信_domain socket 多进程

domain socket 多进程

这会是一系列文章,讲解的内容也很简单,文章的目的是让自己的知识固话和文档化,以备自己不时的复习,同时也希望能够给予初学者一些帮助。

前面的文章一系列文章有介绍了 linux 下常见的 IPC 机制,如管道、消息队列、信号量、共享内存。

之前有讲到共享内存是最高效的 IPC 方式,但是在 linux 环境下,应用最广泛的可能是 Socket

什么是 Unix Domain Socket ?

Socket 翻译过来就叫做套接字,一般我们指代的 Socket 其实是网络通信的 Socket,它用于在网络中不同的设备之间进行通信。

但 Socket 也可以用于同一台设备中不同的进程通信的,Android 系统开发中,它叫做 LocalSocket,我觉得 LocalSocket 更适合概括它。

那我们在 Linux 下开发,用于在本机不同进程通信的 Socket 就叫做 Unix Domain Socket。

本文就是讲解 Unix Domain Socket。

Socket 通信机制主要有 2 种,TCP 和 UDP.

TCP 是可靠的通信机制。
UDP 是不可靠的通信机制。

本文讲解 UDP 通信步骤,并将给出示例代码.

Socket 开发套路

我初学 Socket 开发时,很烦它,因为我是一个懒人,它的步骤太多了。

但后来克服恐惧后,细细想来,它也很简单,心平气和把他有条理的捋一捋,还是很简单的嘛,再复杂都是套路。

下面细细来讲,其实也不复杂。

在这里插入图片描述
通信一般分为服务端和客户端 2 种角色,在 UDP 开发中,两种角色几乎是对等的。

建立 Socket
#include <sys/types.h>
#include <sys/socket.h>

int socket (int domain, int type, int protocol)

  • 1
  • 2
  • 3
  • 4
  • 5
  • domain 指定 Socket 类型,AF_INET,AF_INET6,AF_UNIX,分别对应 ipv4、ipv6 和 Unix Domain Socket
  • type 指定通信机制类型 SOCK_STREAM 指定 TCP,SOCK_DGRAM 指定 UDP,SOCK_RAW 原始套接字
  • protocol 协议 一般为 0 就好了

所以,我们可以这样编码

int fd = socket(AF_UNIX,SOCK_DGRAM,0);
  • 1
绑定地址

socket 是为了建立套接字,真正要工作的话,还需要进行绑定,在 UDP 中绑定的是一个文件。

int bind (int fd, CONST_SOCKADDR_ARG addr, socklen_t len)
  • 1
  • fd 由 socket 返回来的套接字描述符
  • addr 指定了地址,它是 sockaddr_un 类型,在 sys/un.h 头文件中定义
  • len addr 的长度

用法如下:

char* server_file = "server.sock";

struct sockaddr_un addr;
memset(&addr,0,sizeof(addr));
addr.sun_family = AF_UNIX;
strcpy(addr.sun_path,server_file);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

UDP 编程中,服务端不需要监听和接受连接

读写信息
写信息
ssize_t sendto (int fd, const void *buf, size_t n,
		       int flags, CONST_SOCKADDR_ARG addr,
		       socklen_t addr_len)
  • 1
  • 2
  • 3
  • fd socket 的文件描述符
  • buf 存了要发送的消息
  • n 要发送的消息的容量
  • flags 操作位,取 0 就好
  • addr 要发送的对象的地址
  • addr_len addr 的字节大小

如果写入成功就返回实际的消息字节数量,否则返回 -1.

用法如下:

char *p = "OK,I got id!";
//clientaddr 要指定发送对象的地址
int ssize = sendto(fd,p,strlen(p),0,(sockaddr*)&clientaddr,len);
if (ssize < 0)
{
    perror("sendto");
    return -1;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
读信息
ssize_t recvfrom (int fd, void *restrict buf, size_t n,
			 int flags, SOCKADDR_ARG addr,
			 socklen_t *restrict addr_len)
  • 1
  • 2
  • 3
  • fd socket 的文件描述符
  • buf 用来存放接收到的新消息
  • n 接收到的信息的大小
  • flags 操作位,取 0 就好
  • addr 用来存放消息发送者的地址,是指针类型
  • addr_len addr 的字节大小,注意的是它也是指针类型

用法如下:

struct sockaddr_un clientaddr;
socklen_t len = sizeof(clientaddr);

char msgrecv[1024];

memset(msgrecv,'\0',1024);
int size = recvfrom(fd,msgrecv,sizeof(msgrecv),0,(sockaddr*)&clientaddr,&len);
if (size < 0)
{
    perror("recv");
    return -1;
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
关闭 Socket
int close (int fd)
  • 1

示例代码

udpserver.cpp

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

using namespace std;


char* server_file = "server.sock";

int main(int argc,char** argv)
{
    int fd = socket(AF_UNIX,SOCK_DGRAM,0);

    if (fd < 0)
    {
        perror("socket");
        return -1;
    }


    struct sockaddr_un addr;
    memset(&addr,0,sizeof(addr));
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path,server_file);

    if (access(addr.sun_path,0) != -1)
    {
        remove(addr.sun_path);
    }

    if(bind(fd,(sockaddr*)&addr,sizeof(addr)) < 0)
    {
        perror("bind");
        return -1;
    }


    struct sockaddr_un clientaddr;
    socklen_t len = sizeof(clientaddr);

    char msgrecv[1024];

    

    while (1)
    {
        memset(msgrecv,'\0',1024);
        int size = recvfrom(fd,msgrecv,sizeof(msgrecv),0,(sockaddr*)&clientaddr,&len);
        if (size < 0)
        {
            perror("recv");
            return -1;
        }


        cout << "I'm server,receive a msg: " << msgrecv  << " from: " << clientaddr.sun_path << endl;

        if (strncmp("quit",msgrecv,4) == 0)
        {
            cout << "Server is exiting!" << endl;
            break;
        }

        char *p = "OK,I got id!";
        int ssize = sendto(fd,p,strlen(p),0,(sockaddr*)&clientaddr,len);
        if (ssize < 0)
        {
            perror("sendto");
            return -1;
        }

        sleep(1);
    }


    if (close(fd) < 0)
    {
        perror("close");
        return -1;
    }

    return 0;
    
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88

代码很简单,udpserver 作为服务端,循环接受和打印消息。

如果收到了 quit 的消息就退出。

udpclient.cpp


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

using namespace std;


char* server_file = "server.sock";
char* client_file = "client.sock";

int main(int argc,char** argv)
{
    int fd = socket(AF_UNIX,SOCK_DGRAM,0);

    if (fd < 0)
    {
        perror("socket");
        return -1;
    }


    struct sockaddr_un addr;
    memset(&addr,0,sizeof(addr));
    addr.sun_family = AF_UNIX;
    strcpy(addr.sun_path,client_file);

    if (access(addr.sun_path,0) != -1)
    {
        remove(addr.sun_path);
    }

    if(bind(fd,(sockaddr*)&addr,sizeof(addr)) < 0)
    {
        perror("bind");
        return -1;
    }


    struct sockaddr_un clientaddr;
    socklen_t len = sizeof(clientaddr);

    char msgrecv[1024];
    struct sockaddr_un serveraddr;
    memset(&serveraddr,0,sizeof(serveraddr));
    serveraddr.sun_family = AF_UNIX;
    strcpy(serveraddr.sun_path,server_file);

    

    char *p = "Hello,how are you?";
    int ssize = sendto(fd,p,strlen(p),0,(sockaddr*)&serveraddr,len);
    if (ssize < 0)
    {
        perror("sendto");
        return -1;
    }

    int size = recvfrom(fd,msgrecv,sizeof(msgrecv),0,(sockaddr*)&serveraddr,&len);
    if (size < 0)
    {
        perror("recv");
        return -1;
    }

    cout << "I'm client,receive a msg :" << msgrecv << endl;

    sleep(2);

    char* goodbye = "quit";

    if (sendto(fd,goodbye,strlen(goodbye),0,(sockaddr*)&serveraddr,len) < 0)
    {
        perror("sendto");
        return -1;
    }



    if (close(fd) < 0)
    {
        perror("close");
        return -1;
    }

    return 0;
    
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91

udpclient 作为客户端,发送了两条消息。最后一条是 quit ,目的是结束本次对话。

现在编译代码,然后运行。

g++ udpserver.cpp -o udpserver
g++ udpclient.cpp -o udpclient

./udpserver &
./udpclient
  • 1
  • 2
  • 3
  • 4
  • 5

最终结果如下:

I'm server,receive a msg: Hello,how are you? from: client.sock
I'm client,receive a msg :OK,I got id!�
I'm server,receive a msg: quit from: client.sock
Server is exiting!
[1]+  已完成               ./udpserver

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

自此,我们已经掌握了最基本的 Unix Domain Socket 中 UDP 通信,应对复杂的业务场景,我们根据实际条件稍作修改就好了。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/158814
推荐阅读
相关标签
  

闽ICP备14008679号