• Linux的socket通信


    关于套接字通信定义如下:
    套接字对应程序猿来说就是一套网络通信的接口,使用这套接口就可以完成网络通信。网络通信的主体主要分为两部分:客户端和服务器端。在客户端和服务器通信的时候需要频繁提到三个概念:IP、端口、通信数据,下面介绍一下需要注意的一些细节问题。

    引用来源于爱编程的大丙

    在这里插入图片描述

    服务器端的套接字通信流程:
    (1)建立套接字,这个套接字就是一个文件描述符。


    • int lfd = socket();
    • 函数原型如下:
    • // 创建一个套接字
      int socket(int domain, int type, int protocol);

    参数:
    domain: 使用的地址族协议

    • AF_INET: 使用IPv4格式的ip地址
    • AF_INET6: 使用IPv4格式的ip地址
      type:
    • SOCK_STREAM: 使用流式的传输协议
    • SOCK_DGRAM: 使用报式(报文)的传输协议
    • protocol: 一般写0即可, 使用默认的协议
    • SOCK_STREAM: 流式传输默认使用的是tcp
    • SOCK_DGRAM: 报式传输默认使用的udp
      返回值:
    • 成功: 可用于套接字通信的文件描述符
    • 失败: -1
      函数的返回值是一个文件描述符,通过这个文件描述符可以操作内核中的某一块内存,网络通信是基于这个文件描述符来完成的。
        // 1. 创建监听的套接字
        int lfd = socket(AF_INET, SOCK_STREAM, 0);
        if(lfd == -1)
        {
            perror("socket");
            exit(0);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    (2)绑定:将得到的监听的文件描述符和本地的IP 端口进行绑定


    // 将文件描述符和本地的IP与端口进行绑定
    int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);


    • 返回值:成功返回0,失败返回-1
        // 2. 将socket()返回值和本地的IP端口绑定到一起
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
         //系统存储的是小端端口,所以需要转换一下
        addr.sin_port = htons(10000);   // 大端端口,这里因为网络通信是使用的大端端口,
       
        // INADDR_ANY代表本机的所有IP, 假设有三个网卡就有三个IP地址
        // 这个宏可以代表任意一个IP地址
        // 这个宏一般用于本地的绑定操作
        addr.sin_addr.s_addr = INADDR_ANY;  // 这个宏的值为0 == 0.0.0.0,因为这个都是0000,不需要大小端转换
        int ret = bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
        if(ret == -1)
        {
            perror("bind");
            exit(0);
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    (3) 设置监听


    // 给监听的套接字设置监听
    int listen(int sockfd, int backlog);


    参数:

    • sockfd: 文件描述符, 可以通过调用socket()得到,在监听之前必须要绑定 bind()
    • backlog: 同时能处理的最大连接要求,最大值为128
      返回值:
    • 函数调用成功返回0,调用失败返回 -1
       // 3. 设置监听
        ret = listen(lfd, 128);
        if(ret == -1)
        {
            perror("listen");
            exit(0);
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    (4)阻塞并等待连接
    会得到一个新的文件描述符(通信的)


    // 等待并接受客户端的连接请求, 建立新的连接,
    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);


    返回值:函数调用成功,得到一个文件描述符, 用于和建立连接的这个客户端通信,调用失败返回 -1

        // 4. 阻塞等待并接受客户端连接
        struct sockaddr_in cliaddr;
        int clilen = sizeof(cliaddr);
        int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &clilen);
        if(cfd == -1)
        {
            perror("accept");
            exit(0);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    (5)如果成功之后打印一条消息(可有可无)

        // 打印客户端的地址信息
        char ip[24] = {0};
        printf("客户端的IP地址: %s, 端口: %d\n",
               inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ip, sizeof(ip)),
               ntohs(cliaddr.sin_port));
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (6)开始通信

        // 5. 和客户端通信
        while(1)
        {
            // 接收数据
            char buf[1024];
            memset(buf, 0, sizeof(buf));
            int len = read(cfd, buf, sizeof(buf));
            if(len > 0)
            {
                printf("客户端say: %s\n", buf);
                write(cfd, buf, len);
            }
            else if(len  == 0)
            {
                printf("客户端断开了连接...\n");
                break;
            }
            else
            {
                perror("read");
                break;
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    server.c

    // server.c
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
        // 1. 创建监听的套接字
        int lfd = socket(AF_INET, SOCK_STREAM, 0);
        if(lfd == -1)
        {
            perror("socket");
            exit(0);
        }
    
        // 2. 将socket()返回值和本地的IP端口绑定到一起
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(10000);   // 大端端口
        // INADDR_ANY代表本机的所有IP, 假设有三个网卡就有三个IP地址
        // 这个宏可以代表任意一个IP地址
        // 这个宏一般用于本地的绑定操作
        addr.sin_addr.s_addr = INADDR_ANY;  // 这个宏的值为0 == 0.0.0.0
        int ret = bind(lfd, (struct sockaddr*)&addr, sizeof(addr));
        if(ret == -1)
        {
            perror("bind");
            exit(0);
        }
    
        // 3. 设置监听
        ret = listen(lfd, 128);
        if(ret == -1)
        {
            perror("listen");
            exit(0);
        }
    
        // 4. 阻塞等待并接受客户端连接
        struct sockaddr_in cliaddr;
        int clilen = sizeof(cliaddr);
        int cfd = accept(lfd, (struct sockaddr*)&cliaddr, &clilen);
        if(cfd == -1)
        {
            perror("accept");
            exit(0);
        }
        // 打印客户端的地址信息
        char ip[24] = {0};
        printf("客户端的IP地址: %s, 端口: %d\n",
               inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ip, sizeof(ip)),
               ntohs(cliaddr.sin_port));
    
        // 5. 和客户端通信
        while(1)
        {
            // 接收数据
            char buf[1024];
            memset(buf, 0, sizeof(buf));
            int len = read(cfd, buf, sizeof(buf));
            if(len > 0)
            {
                printf("客户端say: %s\n", buf);
                write(cfd, buf, len);
            }
            else if(len  == 0)
            {
                printf("客户端断开了连接...\n");
                break;
            }
            else
            {
                perror("read");
                break;
            }
        }
    
        close(cfd);
        close(lfd);
    
        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

    client.c

    // client.c
    #include 
    #include 
    #include 
    #include 
    #include 
    
    int main()
    {
        // 1. 创建通信的套接字
        int fd = socket(AF_INET, SOCK_STREAM, 0);
        if(fd == -1)
        {
            perror("socket");
            exit(0);
        }
    
        // 2. 连接服务器
        struct sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(10000);   // 大端端口
        inet_pton(AF_INET, "你的地址", &addr.sin_addr.s_addr);
    
        int ret = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
        if(ret == -1)
        {
            perror("connect");
            exit(0);
        }
    
        // 3. 和服务器端通信
        int number = 0;
        while(1)
        {
            // 发送数据
            char buf[1024];
            sprintf(buf, "你好, 服务器...%d\n", number++);
            write(fd, buf, strlen(buf)+1);
            
            // 接收数据
            memset(buf, 0, sizeof(buf));
            int len = read(fd, buf, sizeof(buf));
            if(len > 0)
            {
                printf("服务器say: %s\n", buf);
            }
            else if(len  == 0)
            {
                printf("服务器断开了连接...\n");
                break;
            }
            else
            {
                perror("read");
                break;
            }
            sleep(1);   // 每隔1s发送一条数据
        }
    
        close(fd);
    
        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

    在这里插入图片描述

  • 相关阅读:
    C#在Pdf画统计图表之【雷达图】(以五边形为例)
    github Copilot的使用总结
    工业物联网网关为边缘计算带来更多的价值
    记一次神奇的 pipe 错误
    ZigBee 3.0实战教程-Silicon Labs EFR32+EmberZnet-1-01:目录
    linux centos yum 安装lamp
    GZ038 物联网应用开发赛题第8套
    元宇宙-虚拟世界的安全风险如何应对
    2023.11.22使用flask做一个简单的图片浏览器
    【算法速查】一篇文章带你快速入门八大排序(上)
  • 原文地址:https://blog.csdn.net/m0_49586319/article/details/133244238