Linux网络编程【6】(TCP和UDP本地通信)

263 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

本地通信

1 相关概念

套接字通信最开始也和前六种进程间通信方式一样,也是只能实现一台主机的多个进程间通信,称之为本地通信,UNIX域套接字

  • socket同样可以用于本地通信
  • 创建套接字时使用本地协议AF_UNIX(或AF_LOCAL)。
  • 分为流式套接字和用户数据报套接字
  • 和其他进程间通信方式相比使用方便、效率更高
  • 常用于前后台进程通信

2 本地信息结构体

​ #include <sys/un.h> struct sockaddr_un { sa_family_t sun_family; 地址族,AF_UNIX char sun_path[108]; 文件名,最终会创建一个套接字文件 };

1 TCP本地通信

1.1 流程

服务器:

  1. 创建套接字 socket( )
  2. 填充服务器本地信息结构体 sockaddr_in
  3. 将套接字与服务器本地信息结构体绑定 bind( )
  4. 将套接字设置为被动监听状态 listen( )
  5. 阻塞等待客户端的连接请求 accept( )
  6. 进行通信 recv( )/send( )

客户端:

  1. 创建套接字 socket( )
  2. 填充服务器本地信息结构体 sockaddr_in
  3. 发送客户端的连接请求 connect( )
  4. 进行通信 send( )/recv( )

1.2 服务器

//TCP本地通信之服务器

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h> 
#include <string.h>
#include <sys/un.h>

#define N 128
#define ERRLOG(errmsg) do{\
                            perror(errmsg);\
                            printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
                            exit(1);\
                            }while(0)

int main(int argc, char const *argv[])
{
    if(argc < 2)
    {
        fprintf(stderr, "Usage: %s <socket file>\n", argv[0]);
        exit(1);
    }

    int sockfd, acceptfd;
    struct sockaddr_un serveraddr, clientaddr;
    socklen_t addrlen = sizeof(serveraddr);
    char buf[N] = {0};
    ssize_t bytes;

    //第一步:创建套接字
    if((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
    {
        ERRLOG("socket error");
    }

    //第二步:填充服务器本地信息结构体
    serveraddr.sun_family = AF_UNIX;
    strcpy(serveraddr.sun_path, argv[1]);

    //第三步:将套接字与服务器网络信息结构体绑定
    if(bind(sockfd, (struct sockaddr *)&serveraddr, addrlen) == -1)
    {
        ERRLOG("bind error");
    }

    //第四步:将套接字设置为被动监听状态
    if(listen(sockfd, 5) == -1)
    {
        ERRLOG("listen error");
    }

NEXT:
    //第五步:阻塞等待客户端的连接
    if((acceptfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen)) == -1)
    {
        ERRLOG("accept error");
    }

    //进行通信
    while(1)
    {
        if((bytes = recv(acceptfd, buf, N, 0)) == -1)
        {
            ERRLOG("recv error");
        }
        else if(bytes == 0)
        {
            printf("客户端退出了\n");
            goto NEXT;
        }

        if(strcmp(buf, "quit") == 0)
        {
            printf("客户端退出了\n");
            goto NEXT;
        }

        printf("客户端:%s\n", buf);
    
        strcat(buf, "^_^");

        if(send(acceptfd, buf, N, 0) == -1)
        {
            ERRLOG("send error");
        }
    }

    return 0;
}

1.3 客户端

//TCP本地通信之客户端

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h> 
#include <string.h>
#include <sys/un.h>

#define N 128
#define ERRLOG(errmsg) do{\
                            perror(errmsg);\
                            printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
                            exit(1);\
                            }while(0)

int main(int argc, char const *argv[])
{
    if(argc < 2)
    {
        fprintf(stderr, "Usage: %s <socket file>\n", argv[0]);
        exit(1);
    }

    int sockfd;
    struct sockaddr_un serveraddr;
    socklen_t addrlen = sizeof(serveraddr);

    //第一步:创建套接字
    if((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
    {
        ERRLOG("socket error");
    }

    //第二步:填充服务器本地信息结构体
    serveraddr.sun_family = AF_UNIX;
    strcpy(serveraddr.sun_path, argv[1]);

    //第三步:给服务器发送客户端的连接请求
    if(connect(sockfd, (struct sockaddr *)&serveraddr, addrlen) == -1)
    {
        ERRLOG("connect error");
    }

    //进行通信
    char buf[N] = {0};
    while(1)
    {
        fgets(buf, N, stdin);
        buf[strlen(buf) - 1] = '\0';

        if(send(sockfd, buf, N, 0) == -1)
        {
            ERRLOG("send error");
        }

        if(strcmp(buf, "quit") == 0)
        {
            printf("客户端退出了\n");
            exit(0);
        }

        memset(buf, 0, N);

        if(recv(sockfd, buf, N, 0) == -1)
        {
            ERRLOG("recv error");
        }

        printf("服务器:%s\n", buf);
    }

    return 0;
}

2 UDP本地通信

1.1 流程

服务器:

  1. 创建套接字 socket( )
  2. 填充服务器本地信息结构体 sockaddr_un
  3. 将套接字与服务器本地信息结构体绑定 bind( )
  4. 进行通信 recvfrom( ) / sendto( )

客户端:

  1. 创建套接字 socket( )
  2. 填充客户端本地信息结构体 sockaddr_un
  3. 将套接字与客户端本地信息结构体绑定
  4. 填充服务器本地信息结构体 sockaddr_un
  5. 进行通信 sendto( ) / recvfrom( )

2.2 服务器

//UDP本地通信之服务器

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h> 
#include <string.h>
#include <sys/un.h>

#define N 128
#define ERRLOG(errmsg) do{\
                            perror(errmsg);\
                            printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
                            exit(1);\
                            }while(0)

int main(int argc, char const *argv[])
{
    if(argc < 2)
    {
        fprintf(stderr, "Usage: %s <socket file>\n", argv[0]);
        exit(1);
    }

    int sockfd;
    struct sockaddr_un serveraddr, clientaddr;
    socklen_t addrlen = sizeof(serveraddr);
    char buf[N] = {0};

    //第一步:创建套接字
    if((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1)
    {
        ERRLOG("socket error");
    }

    //第二步:填充服务器本地信息结构体
    serveraddr.sun_family = AF_UNIX;
    strcpy(serveraddr.sun_path, argv[1]);

    //第三步:将套接字与服务器网络信息结构体绑定
    if(bind(sockfd, (struct sockaddr *)&serveraddr, addrlen) == -1)
    {
        ERRLOG("bind error");
    }

    //进行通信
    while(1)
    {
NEXT:
        if(recvfrom(sockfd, buf, N, 0, (struct sockaddr *)&clientaddr, &addrlen) == -1)
        {
            ERRLOG("recvfrom error");
        }

        if(strcmp(buf, "quit") == 0)
        {
            printf("客户端退出了\n");
            goto NEXT;
        }

        printf("客户端[%s]:%s\n", clientaddr.sun_path, buf);
    
        strcat(buf, "^_^");

        if(sendto(sockfd, buf, N, 0, (struct sockaddr *)&clientaddr, addrlen) == -1)
        {
            ERRLOG("sendto error");
        }
    }

    return 0;
}

2.3 客户端

//UDP本地通信之客户端

#include <stdio.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h> 
#include <string.h>
#include <sys/un.h>

#define N 128
#define ERRLOG(errmsg) do{\
                            perror(errmsg);\
                            printf("%s - %s - %d\n", __FILE__, __func__, __LINE__);\
                            exit(1);\
                            }while(0)

int main(int argc, char const *argv[])
{
    if(argc < 2)
    {
        fprintf(stderr, "Usage: %s <serversocket file> <clientsocket file>\n", argv[0]);
        exit(1);
    }

    int sockfd;
    struct sockaddr_un serveraddr;
    socklen_t addrlen = sizeof(serveraddr);

    //第一步:创建套接字
    if((sockfd = socket(AF_UNIX, SOCK_DGRAM, 0)) == -1)
    {
        ERRLOG("socket error");
    }

    //UDP本地通信里面,客户端必须绑定自己的信息,否则服务器无法
    //获取客户端的信息,所以无法给客户端发送数据
    struct sockaddr_un clientaddr;
    clientaddr.sun_family = AF_UNIX;
    strcpy(clientaddr.sun_path, argv[2]);
    if(bind(sockfd, (struct sockaddr *)&clientaddr, addrlen) == -1)
    {
        ERRLOG("bind error");
    }

    //第二步:填充服务器本地信息结构体
    serveraddr.sun_family = AF_UNIX;
    strcpy(serveraddr.sun_path, argv[1]);

    //进行通信
    char buf[N] = {0};
    while(1)
    {
        fgets(buf, N, stdin);
        buf[strlen(buf) - 1] = '\0';

        if(sendto(sockfd, buf, N, 0, (struct sockaddr *)&serveraddr, addrlen) == -1)
        {
            ERRLOG("sendto error");
        }

        if(strcmp(buf, "quit") == 0)
        {
            printf("客户端退出了\n");
            exit(0);
        }

        memset(buf, 0, N);

        if(recvfrom(sockfd, buf, N, 0, (struct sockaddr *)&serveraddr, &addrlen) == -1)
        {
            ERRLOG("recvfrom error");
        }

        printf("服务器:%s\n", buf);
    }

    return 0;
}