linux用户空间 - 多进程编程(四)

2021-06-12  本文已影响0人  404Not_Found
# Unix domain
  ## 关于STREAM 和 DGRAM的 Connect
  ## unix domain 的文件路径
     ## 面向流代码举例
     ## 面向数据报代码举例
  ## asbtract path name
  ## uname path name
#lsof 工具简介

Unix domain

面向数据报.png

关于 STREAM 和 DRAM 的 connect

man connect 即可查阅相关区别
If the socket sockfd is of type SOCK_DGRAM then addr is the address to which datagrams are sent by default, and the only address from which datagrams are received. If the socket is of type
SOCK_STREAM or SOCK_SEQPACKET, this call attempts to make a connection to the socket that is bound to the address specified by addr.

pathname 文件路径

面向流的 代码举例:

/*server.c */
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

//定义用于通信的文件名

#define UNIX_DOMAIN "/tmp/UNIX.domain"

int main()
{

    socklen_t clt_addr_len;
    int listen_fd;
    int com_fd;
    int ret;
    int i;
    static char recv_buf[1024];
    int len;
    struct sockaddr_un clt_addr;
    struct sockaddr_un srv_addr;

    //创建用于通信的套接字,通信域为UNIX通信域

    listen_fd=socket(PF_UNIX,SOCK_STREAM,0);
    if(listen_fd<0){
        perror("cannot create listening socket");
        return 1;
    }

    /*
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_prot = htons(SERV_PORT);
    */
    //设置服务器地址参数
    srv_addr.sun_family=AF_UNIX;
    strncpy(srv_addr.sun_path,UNIX_DOMAIN,sizeof(srv_addr.sun_path)-1);

    //绑定套接字与服务器地址信息
    ret=bind(listen_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));
    if(ret==-1){
        perror("cannot bind server socket");
        close(listen_fd);
        unlink(UNIX_DOMAIN);
        return 1;

    }

    //对套接字进行监听,判断是否有连接请求
    ret=listen(listen_fd,1);
    if(ret==-1){
        perror("cannot listen the client connect request");
        close(listen_fd);
        unlink(UNIX_DOMAIN);
        return 1;

    }

    //当有连接请求时,调用accept函数建立服务器与客户机之间的连接
    len=sizeof(clt_addr);
    com_fd=accept(listen_fd,(struct sockaddr*)&clt_addr,&len);
    if(com_fd<0){
        perror("cannot accept client connect request");
        close(listen_fd);
        unlink(UNIX_DOMAIN);

        return 1;
    }

    //读取并输出客户端发送过来的连接信息
    printf("\n=====info=====\n");
    for(i=0;i<4;i++){
        memset(recv_buf,0,1024);
        int num=read(com_fd,recv_buf,sizeof(recv_buf));
        printf("Message from client (%d)) :%s\n",num,recv_buf);
    }

    close(com_fd);
    close(listen_fd);

    unlink(UNIX_DOMAIN);

    return 0;
}

/*client.c*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

//定义用于通信的文件名
#define UNIX_DOMAIN "/tmp/UNIX.domain"
int main(void)
{
    int connect_fd;
    int ret;
    char snd_buf[1024];
    int i;

    static struct sockaddr_un srv_addr;
    //创建用于通信的套接字,通信域为UNIX通信域

    connect_fd=socket(PF_UNIX,SOCK_STREAM,0);
    if(connect_fd<0){
        perror("cannot create communication socket");
        return 1;
    }

    srv_addr.sun_family=AF_UNIX;
    strcpy(srv_addr.sun_path,UNIX_DOMAIN);

    //连接服务器
    ret=connect(connect_fd,(struct sockaddr*)&srv_addr,sizeof(srv_addr));
    if(ret==-1){
        perror("cannot connect to the server");
        close(connect_fd);

        return 1;
    }


    memset(snd_buf,0,1024);
    strcpy(snd_buf,"message from client");

    //给服务器发送消息
    for(i=0;i<4;i++)
        write(connect_fd,snd_buf,sizeof(snd_buf));

    close(connect_fd);
    return 0;
}

面向数据报的代码举例

/*server*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

char * server_filename = "/tmp/socket-server";

int main(void)
{
    int s;
    struct sockaddr_un srv_un = {0};

    if ((s = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
        perror("socket server");
        exit(1);
    }

    srv_un.sun_family = AF_UNIX;
    strncpy(srv_un.sun_path, server_filename, sizeof(srv_un.sun_path));
    /*If you leave the file behind when you're finished, or perhaps crash after binding, the next bind will fail
    / with "address in use". Which just means, the file is already there.*/
    unlink(srv_un.sun_path);

    if (bind(s, (struct sockaddr *)&srv_un, sizeof(srv_un)) == -1) {
        perror("bind server");
        exit(1);
    }

    for(;;) {

        char buf[1024] = {0};
        read(s, buf, sizeof(buf));
        printf("RECEIVED: %s", buf);

    }

    close(s);

    return 0;
}

/*client*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

char * server_filename = "/tmp/socket-server";
char * client_filename = "/tmp/socket-client";

int main(void)
{

    int s;
    char obuf[100];
    struct sockaddr_un srv_un, cli_un = { 0 };
    
    srv_un.sun_family = AF_UNIX;
    strncpy(srv_un.sun_path, server_filename, sizeof(srv_un.sun_path));

    cli_un.sun_family = AF_UNIX;
    strncpy(cli_un.sun_path, client_filename, sizeof(cli_un.sun_path));
    unlink(cli_un.sun_path);

    if ((s = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1) {
        perror("socket server");
        exit(1);
    }

    /* (http://stackoverflow.com/questions/3324619/unix-domain-socket-using-datagram-communication-between-one-server-process-and)
    Here, we bind to our client node, and connect to the server node. As Unix domain sockets need to have endpoints on either end
    of the connection. For more info, visit the URL.*/
    if (bind(s, (struct sockaddr *)&cli_un, sizeof(cli_un)) == -1) {
        perror("bind client");
        exit(1);
    }

    if (connect(s, (struct sockaddr *) &srv_un, sizeof(srv_un)) == -1) {
        perror("connect client");
        exit(1);
    }

    //printf("Connected.\n");

    while(printf("> "), fgets(obuf, 100, stdin), !feof(stdin)) {
        /*send 函数并没指名发给谁,因为上面已经有了 connect 默认的 发送对象*/
        if (send(s, obuf, strlen(obuf), 0) == -1) {
            perror("send");
            exit(1);
        }
        break;
    }

    //printf("Sent successfully.\n");

    close(s);

    return 0;
}

  1. 编译运行 server.c
    可以看到 /tmp/UNIX.domain, 文件类型是socket


    UNIX.domain.png
  2. netstat -anp|grep UNIX


    netstat.png
  3. 编译运行 client.c
    随着 server.c 的关闭,/tmp/UNIX.domain 消失, 文件也没了,netstat 也没了


    图片.png
  4. 查看更多信息
    pidof server => lsof pid


    lsof.png

一个socket 对于 一个 fd来讲,无非也就是一个fd

abstract path 文件路径

C/S 都应该使用抽象的路径
实际上就是把第一个字符修改成 '0' 即可。


lsof.png

从路径来看抽象的path 在路径里换成了@

这条路径是与文件系统没有关系的。所以 在/tmp 路径下也看不到 unix.domain

unnmae 无名 即socketpair

int s[2]; /*pair of sockets*/
z = socketpair(AF_LOCAL, SOCKET_STREAM, o, s);

socket pair 与 pipe 相似,但最大不同是 socketpair 是 双工的,pipe 是单工的。
工程中经常约定,一个进程在s[0] 读写,另一个在s[1] 读写

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

const char* str = "Hello World";

int main(int argc, char* argv[]){
    char buf[128] = {0};
    int fd[2]; 
    pid_t pid; 

    if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) == -1 ) { 
        return EXIT_FAILURE; 
    } 

    pid = fork();
    if(pid < 0) {
        return EXIT_FAILURE;
    } else if(pid > 0) {
        close(fd[1]);

        write(fd[0], str, strlen(str));
        read(fd[0], buf, sizeof(buf));
        printf("parent received: %s\n", buf);
    } else if(pid == 0) {
        close(fd[0]);

        read(fd[1], buf, sizeof(buf));  
        write(fd[1], str, strlen(str));
        printf("child received: %s\n",buf);
    }

    while(1);
} 

父子进程彼此发送了 hello world

进程间的通讯,只要是有血缘关系,通信起来就简单很多

lsof 工具简介

上一篇 下一篇

猜你喜欢

热点阅读