• 嵌入式养成计划-29-网络编程----TCP与UDP的基础模型


    Linux下,基于TCP与UDP协议,不同进程下单线程通信服务器

    Linux下,基于TCP与UDP协议,不同进程下单线程通信服务器

    六十五、TCP与UDP的基础模型

    1. socket

    1.1 套接字概念

    • 最早的套接字和共享内存,消息队列,管道一样,只能实现一个主机内部的进程间通信。
    • 后期加入了TCP/IP协议,使的套接字能够支持不同主机之间的进程间通信。
    • socket函数,可以在内核空间中创建两块缓冲区,供于发送数据,接收数据。
      在这里插入图片描述

    1.2 socket 函数

    功能:
    	在内核空间中创建两个缓冲区(发送缓冲区,接收缓冲区),返回该缓冲区的文件描述符到用户空间中;
    原型:
           #include           /* See NOTES */
           #include 
    
           int socket(int domain, int type, int protocol);
    参数:
        int domain:地址族,协议族。
           Name                Purpose                          Man page
           AF_UNIX, AF_LOCAL   Local communication              unix(7)
           AF_INET             IPv4 Internet protocols          ip(7)
           AF_INET6            IPv6 Internet protocols          ipv6(7)
        int type:  
            SOCK_STREAM:字节流式套接字,流式套接字,默认使用TCP协议;
            SOCK_DGRAM:  数据报式套接字,报式套接字,默认使用UDP协议;
            SOCK_RAW:    原始套接字,协议需要在第三个参数手动指定.
            
        int protocol:默认协议,填0;
            IPPROTO_TCP  IPPROTO_UDP  IPPROTO_IP
    返回值:
        成功,返回一个新的文件描述符;
        失败,返回-1,更新errno;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2. TCP编程

    2.1 TCP流程图

    在这里插入图片描述

    2.2 TCP 中的函数

    2.2.1 socket

    功能:
    	在内核空间中创建两个缓冲区(发送缓冲区,接收缓冲区),返回该缓冲区的文件描述符到用户空间中;
    原型:
           #include           /* See NOTES */
           #include 
    
           int socket(int domain, int type, int protocol);
    参数:
        int domain:地址族,协议族。
           Name                Purpose                          Man page
           AF_UNIX, AF_LOCAL   Local communication              unix(7)
           AF_INET             IPv4 Internet protocols          ip(7)
           AF_INET6            IPv6 Internet protocols          ipv6(7)
        int type:  
            SOCK_STREAM:字节流式套接字,流式套接字,默认使用TCP协议;
            SOCK_DGRAM:  数据报式套接字,报式套接字,默认使用UDP协议;
            SOCK_RAW:    原始套接字,协议需要在第三个参数手动指定.
            
        int protocol:默认协议,填0;
            IPPROTO_TCP  IPPROTO_UDP  IPPROTO_IP
    返回值:
        成功,返回一个新的文件描述符;
        失败,返回-1,更新errno;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    2.2.2 bind

    功能:
    	绑定地址信息到指定套接字上;
    原型:
           #include           /* See NOTES */
           #include 
    
           int bind(int sockfd, const struct sockaddr *addr,
     socklen_t addrlen);
    参数:
        int sockfd:指定要绑定到哪个套接字上,填对应的文件描述符;
        struct sockaddr *addr:通用地址信息结构体,真实的地址信息结构体根据地址族指定。
                                手动填充上地址信息(例如:IP和端口),给bind函数绑定使用;
            AF_INET: man 7 IP
               struct sockaddr_in {
                   sa_family_t    sin_family; /* address family: AF_INET */     必须填AF_INET
                   in_port_t      sin_port;   /* port in network byte order */  端口号的网络字节序(1024~49151struct in_addr sin_addr;   /* internet address */      IP地址的网络字节序,(本机IP:ifconfig查看)       
               };
    
               /* Internet address. */
               struct in_addr {
                   uint32_t       s_addr;     /* address in network byte order */
               };
           AF_INET6: man 7 ipv6          
        socklen_t addrlen:真实的地址信息结构体的大小,sizeof(struct sockaddr_in);
    返回值:
        成功,返回0;
        失败,返回-1,更新errno;
    
    • 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

    报错解释:bind: Address already in use ---->原因:代码异常退出后,端口号会在30s~3min不等的时间释放。

    2.2.3 listen

    功能:
    	将套接字设置为被动监听状态;
        让内核去维护两个队列:未完成连接的队列,已完成连接的队列
    原型:
           #include           /* See NOTES */
           #include 
    
           int listen(int sockfd, int backlog);
    参数:
        int sockfd:指定要转换成被动监听状态的文件描述符;
        int backlog:指定未完成连接队列的容量,不要填01即可,一般填128;
    返回值:
        成功,返回0;
        失败,返回-1,更新errno;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.2.4 accept(阻塞函数)

    功能:
    	阻塞函数,阻塞等待客户端连接成功,从已完成连接的队列中获取一个客户端信息,生成一个新的文件描述符。
        该文件描述符才是与客户端通信的文件描述符;
    原型:
           #include           /* See NOTES */
           #include 
    
           int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    参数:
        int sockfd:被转换成被动监听状态的文件描述符;
        struct sockaddr *addr:通用地址信息结构体,真实的地址信息结构体根据地址族指定。
                                存储获取到的客户端的地址信息;若不想获取,则填NULL;
            AF_INET: man 7 IP
               struct sockaddr_in {
                   sa_family_t    sin_family; /* address family: AF_INET */     必须填AF_INET
                   in_port_t      sin_port;   /* port in network byte order */  端口号的网络字节序(1024~49151struct in_addr sin_addr;   /* internet address */      IP地址的网络字节序,(本机IP:ifconfig查看)       
               };
    
               /* Internet address. */
               struct in_addr {
                   uint32_t       s_addr;     /* address in network byte order */
               };   
        socklen_t *addrlen:真实的地址信息结构体的大小,注意是指针类型,若第二个参数填NULL,则该参数也填NULL;  
    返回值:
        成功,返回新的通信套接字文件描述符; 该文件描述符才是与客户端通信的文件描述符;
        失败,返回-1,更新errno;
    
    • 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

    2.2.5 recv

    功能:
    	接收数据;
    原型:
           #include 
           #include 
    
           ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    参数:
        int sockfd:指定从哪个套接字维护的缓冲区中读取数据;
        void *buf:存储接收到的数据,void*:可以接收任意类型数据;
        size_t len:指定要接收多少个字节的数据;
        int flags:
                0:阻塞方式,当缓冲区中没有数据的时候,该函数阻塞; 若flags==0, 则recv函数等价于read函数;
                MSG_DONTWAIT:非阻塞方式,当当缓冲区中没有数据的时候,该函数不阻塞;且函数运行失败;
    返回值:
        >0, 成功读取到的字节数;
        =0,在流失套接字中,若对端关闭,则返回0;
        =-1, 函数运行失败,更新errno;
        
    recv(sockfd, buf, len, flags);
     等价于 recvfrom(sockfd, buf, len, flags, NULL, NULL);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • recv函数能否替换成其他函数?
      可以,当recv函数最后一个参数填0,可以替换成read函数,
      当recvfrom后面两个参数填NULL的时候,等价于recv函数,所以可以替换成recvfrom

    2.2.6 send

    功能:
    	发送数据;
    原型:
           #include 
           #include 
    
           ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    参数:
        int sockfd:指定向哪个套接字维护的缓冲区中写入数据;
        void *buf:指定要发送的数据的首地址,void*:可以发送任意类型数据;
        size_t len:指定要发送多少个字节的数据;
        int flags:
                0:阻塞方式,当缓冲区中满的时候,该函数阻塞; 若flags==0, 则send函数等价于write函数;
                MSG_DONTWAIT:非阻塞方式,当缓冲区中满的时候,该函数不阻塞;且函数运行失败;    
    返回值:
        >0, 成功发送的字节数;
        =-1,函数运行失败,更新errno;
    
    send(sockfd, buf, len, flags);
     等价于 sendto(sockfd, buf, len, flags, NULL, 0);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • send函数能否替换成其他函数?
      可以,当send函数最后一个参数填0,可以替换成write函数,
      当sendto后面两个参数填NULL, 0 的时候,等价于send函数,所以可以替换成sendto

    2.2.7 connect

    功能:
    	连接服务器;
    原型:
           #include           /* See NOTES */
           #include 
    
           int connect(int sockfd, const struct sockaddr *addr,
    socklen_t addrlen);
    参数:
        int sockfd:指定要通过哪个文件描述符连接服务器;
        struct sockaddr *addr:通用地址信息结构体,真实的地址信息结构体根据地址族指定。
                                手动填充上服务器的地址信息(例如:IP和端口),给connect函数绑定使用; 
                                想要连接哪个服务器就填哪个服务器的地址信息
            AF_INET: man 7 IP
               struct sockaddr_in {
                   sa_family_t    sin_family; /* address family: AF_INET */     必须填AF_INET
                   in_port_t      sin_port;   /* port in network byte order */  端口号的网络字节序(1024~49151struct in_addr sin_addr;   /* internet address */      IP地址的网络字节序,(本机IP:ifconfig查看)       
               };
    
               /* Internet address. */
               struct in_addr {
                   uint32_t       s_addr;     /* address in network byte order */
               };
        socklen_t addrlen:真实的地址信息结构体的大小,sizeof(struct sockaddr_in);
    返回值:
        成功,返回0;
        失败,返回-1,更新errno;
    
    • 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

    3. UDP编程

    3.1 UDP流程图

    在这里插入图片描述

    3.2 UDP搭建

    3.2.1 socket

    功能:
    	在内核空间中创建两个缓冲区(发送缓冲区,接收缓冲区),返回该缓冲区的文件描述符到用户空间中;
    原型:
           #include           /* See NOTES */
           #include 
    
           int socket(int domain, int type, int protocol);
    参数:
        int domain:地址族,协议族。
           Name                Purpose                          Man page
           AF_UNIX, AF_LOCAL   Local communication              unix(7)
           AF_INET             IPv4 Internet protocols          ip(7)
           AF_INET6            IPv6 Internet protocols          ipv6(7)
        int type:  
            SOCK_STREAM:字节流式套接字,流式套接字,默认使用TCP协议;
            SOCK_DGRAM:  数据报式套接字,报式套接字,默认使用UDP协议;
            SOCK_RAW:    原始套接字,协议需要在第三个参数手动指定.
            
        int protocol:默认协议,填0;
            IPPROTO_TCP  IPPROTO_UDP  IPPROTO_IP
    返回值:
        成功,返回一个新的文件描述符;
        失败,返回-1,更新errno;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3.2.2 bind

    功能:
    	绑定地址信息到指定套接字上;
    原型:
           #include           /* See NOTES */
           #include 
    
           int bind(int sockfd, const struct sockaddr *addr,
     socklen_t addrlen);
    参数:
        int sockfd:指定要绑定到哪个套接字上,填对应的文件描述符;
        struct sockaddr *addr:通用地址信息结构体,真实的地址信息结构体根据地址族指定。
                                手动填充上地址信息(例如:IP和端口),给bind函数绑定使用;
            AF_INET: man 7 IP
               struct sockaddr_in {
                   sa_family_t    sin_family; /* address family: AF_INET */     必须填AF_INET
                   in_port_t      sin_port;   /* port in network byte order */  端口号的网络字节序(1024~49151struct in_addr sin_addr;   /* internet address */      IP地址的网络字节序,(本机IP:ifconfig查看)       
               };
    
               /* Internet address. */
               struct in_addr {
                   uint32_t       s_addr;     /* address in network byte order */
               };
           AF_INET6: man 7 ipv6          
        socklen_t addrlen:真实的地址信息结构体的大小,sizeof(struct sockaddr_in);
    返回值:
        成功,返回0;
        失败,返回-1,更新errno;
    
    • 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

    3.2.3 recvfrom

    功能:
    	接收数据的同时可以获取到该数据包从哪里来,即可以知道发送方的地址信息;
    原型:
           #include 
           #include 
    
           ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    
           ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                            struct sockaddr *src_addr, socklen_t *addrlen);
    参数:
        int sockfd:指定从哪个套接字维护的缓冲区中读取数据;
        void *buf:存储接收到的数据,void*:可以接收任意类型数据;
        size_t len:指定要接收多少个字节的数据;
        int flags:
                0:阻塞方式,当缓冲区中没有数据的时候,该函数阻塞; 若flags==0, 则recv函数等价于read函数;
                MSG_DONTWAIT:非阻塞方式,当当缓冲区中没有数据的时候,该函数不阻塞;且函数运行失败;
        struct sockaddr *src_addr:通用地址信息结构体,真实的地址信息结构体根据地址族指定。
                                    用于存储发送方的地址信息的,即用于存储这个包是从谁那里发送过来的。若不想接收则填NULL;
            AF_INET: man 7 IP
               struct sockaddr_in {
                   sa_family_t    sin_family; /* address family: AF_INET */     必须填AF_INET
                   in_port_t      sin_port;   /* port in network byte order */  端口号的网络字节序(1024~49151struct in_addr sin_addr;   /* internet address */      IP地址的网络字节序,(本机IP:ifconfig查看)       
               };
    
               /* Internet address. */
               struct in_addr {
                   uint32_t       s_addr;     /* address in network byte order */
               };   
        socklen_t *addrlen:真实的地址信息结构体的大小,注意是指针类型,若第二个参数填NULL,则该参数也填NULL;  
    返回值:
        >0, 成功读取到的字节数;
        =0,但是只有在流失套接字中,若对端关闭,则返回0;
        =-1, 函数运行失败,更新errno;
    
    • 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

    recv(sockfd, buf, len, flags);
    等价于 recvfrom(sockfd, buf, len, flags, NULL, NULL);

    3.2.4 sendto

    功能:
    	发送数据给指定该数据包应该发给谁,即指定接收方的地址,必须指定清楚这个包该发给谁
    原型:
           #include 
           #include 
    
           ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    
           ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                          const struct sockaddr *dest_addr, socklen_t addrlen);
    参数:
        int sockfd:指定向哪个套接字维护的缓冲区中写入数据;
        void *buf:指定要发送的数据的首地址,void*:可以发送任意类型数据;
        size_t len:指定要发送多少个字节的数据;
        int flags:
                0:阻塞方式,当缓冲区中满的时候,该函数阻塞; 若flags==0, 则send函数等价于write函数;
                MSG_DONTWAIT:非阻塞方式,当缓冲区中满的时候,该函数不阻塞;且函数运行失败;  
        struct sockaddr *src_addr:通用地址信息结构体,真实的地址信息结构体根据地址族指定。
                                    指定该数据包应该发给谁,即指定接收方的地址信息,想发给谁填谁的地址信息;
            AF_INET: man 7 IP
               struct sockaddr_in {
                   sa_family_t    sin_family; /* address family: AF_INET */     必须填AF_INET
                   in_port_t      sin_port;   /* port in network byte order */  端口号的网络字节序(1024~49151struct in_addr sin_addr;   /* internet address */      IP地址的网络字节序,(本机IP:ifconfig查看)       
               };
    
               /* Internet address. */
               struct in_addr {
                   uint32_t       s_addr;     /* address in network byte order */
               };   
        socklen_t addrlen:真实的地址信息结构体的大小;              
    返回值:
        >0, 成功发送的字节数;
        =-1,函数运行失败,更新errno; 
    
    • 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

    send(sockfd, buf, len, flags);
    等价于 sendto(sockfd, buf, len, flags, NULL, 0);

    网络调试工具— —飞机的用法

    • 注意: 关闭计算机的杀毒软件,电脑管家,防火墙

    在这里插入图片描述
    在这里插入图片描述

  • 相关阅读:
    大型语言模型的推理演算
    一文讲解Vuex实现原理
    C# 结构体转字节数组
    【Kafka二】架构深入
    C语言 - 你一定能看懂的三子棋详解(万字长文,傻瓜式解析)
    Java入门——this()/super()
    柯达伊教学法在中小学音乐课堂中的运用研究课题结题报告
    js实现短信验证码一键登录
    【趣话计算机底层技术】一个故事看懂各种锁
    Jenkins与DevOps持续交付详解
  • 原文地址:https://blog.csdn.net/qq_52625576/article/details/133049352