• 网络编程:本地套接字


    一、介绍
    本地套接字是IPC, 即本地进程间通信的一种实现方式。出来本地套接字以外,其他技术,如管道、共享信息队列等也是进程间通信的常用方法。但因为本地套接字开发便捷,接受度高,所以普遍适用于同一台主机上进程间通信的各种场景。
    利用本地套接字可完成可靠字节流和数据报两种协议。
    PS:
    可通过netstat命令查看Linux系统内的本地套接字状况。
    查看所使用的本地套接字描述符:

    套接字是一种特殊类型的套接字,与TCP/UDP套接字不同。TCP/UDP即使在本地地址通信,也要走系统网络协议栈,而本地套接字,严格意义上来说提供了一种单主机跨进程间调用的手段,减少 协议栈实现的复杂度,效率比TCP/UDP套接字高很多。类似的IPC机制还有UNIX管道、共享内存和RPC调用等。
    本地地址就是本地套接字专属的。

    二、本地字节流套接字
    服务端:

    //
    // Created by shengym on 2019-07-14.
    //
    
    #include  "lib/common.h"
    
    
    int main(int argc, char **argv) {
        if (argc != 2) {
            error(1, 0, "usage: unixstreamserver <local_path>");
        }
    
        int listenfd, connfd;
        socklen_t clilen;
        struct sockaddr_un cliaddr, servaddr;
    
        listenfd = socket(AF_LOCAL, SOCK_STREAM, 0);
        if (listenfd < 0) {
            error(1, errno, "socket create failed");
        }
    
        char *local_path = argv[1];
        unlink(local_path);
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sun_family = AF_LOCAL;
        strcpy(servaddr.sun_path, local_path);
    
        if (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) {
            error(1, errno, "bind failed");
        }
    
        if (listen(listenfd, LISTENQ) < 0) {
            error(1, errno, "listen failed");
        }
    
        clilen = sizeof(cliaddr);
        if ((connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen)) < 0) {
            if (errno == EINTR)
                error(1, errno, "accept failed");
            else
                error(1, errno, "accept failed");
        }
    
        char buf[BUFFER_SIZE];
    
        while (1) {
            bzero(buf, sizeof(buf));
            if (read(connfd, buf, BUFFER_SIZE) == 0) {
                printf("client quit");
                break;
            }
            printf("Receive: %s", buf);
    
            char send_line[MAXLINE];
            bzero(send_line, MAXLINE);
            sprintf(send_line, "Hi, %s", buf);
    
            int nbytes = sizeof(send_line);
    
            if (write(connfd, send_line, nbytes) != nbytes)
                error(1, errno, "write error");
        }
    
        close(listenfd);
        close(connfd);
    
        exit(0);
    
    }
    

    客户端:

    //
    // Created by shengym on 2019-07-15.
    //
    #include "lib/common.h"
    
    int main(int argc, char **argv) {
        if (argc != 2) {
            error(1, 0, "usage: unixstreamclient <local_path>");
        }
    
        int sockfd;
        struct sockaddr_un servaddr;
    
        sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
        if (sockfd < 0) {
            error(1, errno, "create socket failed");
        }
    
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sun_family = AF_LOCAL;
        strcpy(servaddr.sun_path, argv[1]);
    
        if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) {
            error(1, errno, "connect failed");
        }
    
        char send_line[MAXLINE];
        bzero(send_line, MAXLINE);
        char recv_line[MAXLINE];
    
        while (fgets(send_line, MAXLINE, stdin) != NULL) {
    
            int nbytes = sizeof(send_line);
            if (write(sockfd, send_line, nbytes) != nbytes)
                error(1, errno, "write error");
    
            if (read(sockfd, recv_line, MAXLINE) == 0)
                error(1, errno, "server terminated prematurely");
    
            fputs(recv_line, stdout);
        }
    
        exit(0);
    }
    

    这里创建的套接字类型,注意是 AF_LOCAL,并且使用字节流格式。
    关于本地文件路径,需要明确一点,它必须是“绝对路径”,这样的话,编写好的程序可以在任何目录里被启动和管理。如果是“相对路径”,为了保持同样的目的,这个程序的启动路径就必须固定,这样一来,对程序的管理反而是一个很大的负担。
    另外还要明确一点,这个本地文件,必须是一个“文件”,不能是一个“目录”。如果文件不存在,后面 bind 操作时会自动创建这个文件
    运行结果:
    服务端:

    客户端:

    三:本地数据报套接字
    服务端:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/socket.h>
    #include <sys/un.h>
    #include <netinet/in.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <signal.h>
    
    #define BUFFER_SIZE 4096
    #define MAX_LINE 4096
    
    
    int main(int argc,char* argv[])
    {
        if(argc != 2)
        {
            perror("usage: unixdataserver <local path>");
            return -1;
        }
    
        int socket_fd;
        socket_fd = socket(AF_LOCAL, SOCK_DGRAM, 0);
        if(socket_fd < 0)
        {
            perror("socket create failed");
            return -1;
        }
    
        struct sockaddr_un servaddr;
        char *local_path = argv[1];
        unlink(local_path);
        bzero(&servaddr,sizeof(servaddr));
        servaddr.sun_family = AF_LOCAL;
        strcpy(servaddr.sun_path,local_path);
    
        if(bind(socket_fd,(struct sockaddr *)&servaddr,sizeof(servaddr)) < 0)
        {
            perror("bind faild\n");
            return -1;
        }
    
        char buf[BUFFER_SIZE];
    
        struct sockaddr_un client_addr;
        socklen_t client_len = sizeof(client_addr);
        while(1)
        {
            bzero(buf,sizeof(buf));
            if(recvfrom(socket_fd, buf, BUFFER_SIZE, 0, (struct sockaddr *)&client_addr, &client_len) == 0)
            { 
                printf("client quit\n");
                break;
            }
            printf("Receive: %s \n",buf);
    
            char send_line[MAX_LINE];
            bzero(send_line,MAX_LINE);
            sprintf(send_line,"Hi, %s",buf);
    
            size_t nybtes = strlen(send_line);
            printf("now sending :%s \n",send_line);
    
            if(sendto(socket_fd,send_line,nybtes, 0, (struct sockaddr *)&client_addr, client_len) != nybtes)
            {
                perror("sendto error\n");
                break;
            }
        }
        close(socket_fd);
    
        exit(0);
    
    }
    

    这里创建的套接字类型,注意是 AF_LOCAL,协议类型为 SOCK_DGRAM。
    收发数据recvfrom、sendto等都与UDP网络程序一致。

    客户端:

    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/socket.h>
    #include <sys/un.h>
    #include <netinet/in.h>
    #include <unistd.h>
    #include <errno.h>
    #include <string.h>
    #include <signal.h>
    
    #define MAXLINE 4096
    
    int main(int argc, char *argv[])
    {
        if (argc != 2)
        {
            perror("usage: unixdataclient <local path>");
            return -1;
        }
    
        int sockfd;
        sockfd = socket(AF_LOCAL, SOCK_DGRAM, 0 );
    
        struct sockaddr_un client_addr, server_addr;
        
        bzero(&client_addr,sizeof(client_addr));
        client_addr.sun_family = AF_LOCAL;
        strcpy(client_addr.sun_path, tmpnam(NULL));
    
        if(bind(sockfd, (struct sockaddr *)&client_addr, sizeof(client_addr)) < 0)
        {
            perror("bind error\n");
            return -1;
        }
    
        bzero(&server_addr,sizeof(server_addr));
        server_addr.sun_family = AF_LOCAL;
        strcpy(server_addr.sun_path, argv[1]);
    
        char send_line[MAXLINE];
        bzero(send_line, sizeof(send_line));
        char recv_line[MAXLINE];
    
        while (fgets(send_line,MAXLINE,stdin) != NULL)
        {
            int i = strlen(send_line);
            if(send_line[i - 1] == '\n')
            {
                send_line[i - 1] = 0;
            }
    
            size_t nbytes = strlen(send_line);
            printf("now sending %s \n",send_line);
    
            if(sendto(sockfd, send_line, nbytes, 0, (struct sockaddr *)&server_addr,sizeof(server_addr)) != nbytes)
            {
                perror("sendto error\n");
                break;
            }
    
            int n = recvfrom(sockfd, recv_line, MAXLINE, 0, NULL, NULL);
            recv_line[n] = 0;
    
            fputs(recv_line, stdout);
            fputs("\n",stdout);
        }
        exit(0);
        
    }
    

    执行结果:

    【记】关于本地文件路径,需要明确一点,它必须是“绝对路径”,这样的话,编写好的程序可以在任何目录里被启动和管理。如果是“相对路径”,为了保持同样的目的,这个程序的启动路径就必须固定,这样一来,对程序的管理反而是一个很大的负担。

    附:
    Unix Domain Socket 使用
    C语言tmpnam()函数
    详解struct sockaddr
    unlink

  • 相关阅读:
    最近学习下,nohup和&的区别
    java 关键字
    iOS之事件穿透
    排序算法——快速排序
    分布式-选举算法
    分布式选举算法
    SolrCloud 分布式集群部署步骤
    linux 启动两个tomcat
    solr安装-tomcat+solrCloud构建稳健solr集群
    JAVA 中BIO,NIO,AIO的理解
  • 原文地址:https://www.cnblogs.com/whiteBear/p/15953191.html
Copyright © 2020-2023  润新知