• 第三天:实现网络编程基于tcp/udp协议在Ubuntu与gec6818开发板之间双向通信


    互联网地址

    每一台设备接入互联网后,都会举报一个唯一的地址编号

    IP地址 INTERNET地址

    internet地址 :它是协议上的一个逻辑地址

    目前来说,我们主要的IP地址有两类 IPV4 IPV6

    IPV4 其实就是使用一个32bit整数作为IP

    IPV6 其实就是使用一个128bit整数作为IP

    ipv4

    1010 1100 0000 0010 0000 0001 0000 0001 人类是看不到的

    172.2.1.1 给人看的"点分式"

    每8bit组成一个十进制数,以 ‘.’ 隔开

    2^32 个地址

    这么多地址,怎么去管理?

    类似电话号码 =区号+主机号

    +86 0731 12345678

    +86 中国大陆

    0731 长沙

    12345678 具体那个电话的号码

    同理,ip地址也分为两个部分

    ip地址=网络号+主机号

    网络号:用于标识网络中的某个子网,占ip地址中的高x位

    主机号:用于标识同一个子网内的不同主机,占地址中低的(32-x)位

    也叫做 子网号 子网主机号

    所占的bit位x又怎么确定呢?

    netmask 子网掩码,用来确定一个ip地址中,网络号与主机号的占比

    IP地址的bit位在子网掩码中对应的bit为1,就是网络号

    在子网掩码中为0,就是主机号

    比如:

    ip 172.2.1.1

    netmask 255.255.255.0

    网络号 ip&netmask 172.2.1.0

    主机号的范围 172.2.1.0 ~ 172.2.1.255

    目前ip地址主要分为5类

    在这里插入图片描述

    我们常用的是C类网络

    端口号

    在网络中,可以提供ip地址来区分不同的网络设备

    如果两个设备的IP相同 —》ip冲突,只有一台能够接入网络

    问题;一台设备上,或许会运行多个网络应用

    比如:我打开QQ给你们传文件,同时还在直播上课

    那么网络中,怎么知道传递的数据,是由哪个网络应用发起的呢?

    ip只能区分是那一台主机,不能区分是那一个应用

    为了区分哪个网络应用传输的数据,需要用到端口号

    TCP和UDP都是采用16bit无符号整数来作为端口号标识网络应用

    IP —>标识网络中的某个设备

    端口 —》标识设备上的某个网络应用

    一台主机上的网络应用由:

    ip地址+传输层协议(TCP/UDP)+端口号来确定

    端口号 由 IANS 来管理

    1-1023 众所周知的端口

    比如 HTTP应用 80

    FTP服务 20

    1024-49151 注册端口,提供注册端口

    49151-65536 动态或者私有端口,可以随便用

    字节序

    大端模式

    高地址存储低字节 低地址存储高字节

    小端模式

    低地址存储低字节 高地址存储高字节

    比如: int a = 0x12345678

    &a = 0x4000

    内存地址大端模式存储内容小端模式存储内容
    0x40000x120x78
    0x40010x340x56
    0x40020x560x34
    0x40030x780x12

    不同的系统对于内存的管理方法不同,可能采用大端模式,可能采用小端模式

    练习:尝试写一个程序,判断自己的Linux系统是大端模式还是小端模式,并且输出结果

    int a = 1; //0x00 00 00 01
    char *p = &a
    if(*p == 1)
    {
    	printf("小端模式\n");
    }
    else
    {
    	printf("大端模式\n");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    网络字节序采用的是大端模式

    网络接口 socket

    网络通信接口 socket是一个简单易上手,功能强大的接口

    有以下特点:

    1.socket是一个编程接口(网络编程接口)

    所有的网络程序,都需要基于socket

    2.socket也是一个特殊的文件描述符

    特殊:可以利用系统文件接口

    比如 read write close

    但是不能用lseek

    它的内容不在文件系统中,也不在内核中,而是在"网络"中

    3.socket 是TCP/IP协议的一种代码实现

    但是它不仅限于TCP/IP协议,它还支持蓝牙协议,wifi协议,unix域协议

    socket独立于具体协议的网络程序接口,位于应用层与传输层之间,负责衔接应用层与传输层

    网络通信

    网络通信的实现—》 API(应用程序编程接口)

    1.socket 创建一个套接字

    2.bind 不动,将一个地址(网络应用地址:IP+传输协议+端口)与套接字绑定

    3.listen 监听,用于设定服务器监听套接字(监听后就可以获得客户端的请求)

    4.accpet 等待连接请求的到来

    5.connect 用于发起连接请求

    6.通信 数据收发

    ​ read/write

    ​ recv/send

    ​ recefrom/send_to

    7.关闭套接字

    close 套接字也就是一个文件描述符罢了

    思考 在网络通信在,服务器和客户端 双方的通信流程图

    在这里插入图片描述

    1 socket

    NAME
           socket - create an endpoint for communication
    
    SYNOPSIS
           #include           /* See NOTES */
           #include 
    		socket用来创建一个通信接口
           int socket(int domain, int type, int protocol);
    			@domain:指定域,协议族
                AF_UNIX, AF_LOCAL   Local communication              unix(7)
                    unix域协议/本地协议栈
                AF_INET             IPv4 Internet protocols          ip(7)
                    IPV4协议族
                AF_INET6            IPv6 Internet protocols          ipv6(7)
                    IPV6协议族
                @type:指定套接字类型(选择传输层协议)
    				SOCK_STREAM 流式套接字,面向TCP传输层协议
                    SOCK_DGRAM  数据报套接字,面向UDP传输层协议
                    SOCK_RAW	原始套接字
    			@protocol:协议,指定具体那个协议
                    一般为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

    2 bind

    用于绑定服务器地址(IP+传输层协议+端口号)

    NAME
           bind - bind a name to a socket
    
    SYNOPSIS
           #include           /* See NOTES */
           #include 
    		bind 绑定一个"名字”给socket(套接字描述符)
           int bind(int sockfd, const struct sockaddr *addr,
                    socklen_t addrlen);
    			@sockfd:指定套接字描述符,就是socket的返回值
                @addr:指向struct sockaddr 结构体指针,保存的是要绑定的地址
                @addrlen:地址长度,即参数addr的长度,sizeof (struct sockaddr)
    		返回值:成功返回0,失败返回-1,并且errno被设置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    关于网络应用的地址 =IP+传输层协议+端口号

    在 头文件中,定义了一个通用的网络应用地址结构体

    struct sockaddr {
                   sa_family_t sa_family;//指定协议栈,和socket函数的参数domain相同
        
                   char        sa_data[14];
        		//数组保存网络应用地址(IP+传输层协议+端口号)
               }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    上面这个结构体,并不是很好赋值和使用,一般在网络编程中,网络应用地址赋值可以采用定义在

    /usr/include/netinet/in.h中的结构体 struct sockaddr_in

    Structure describing an Internet socket address

    以太网地址结构体

    struct sockaddr_in
      {
        __SOCKADDR_COMMON (sin_);//协议栈   sin_family
        in_port_t sin_port;			/* 端口号,网络上一般是大端模式,计算机一般是小端模式  */
        struct in_addr sin_addr;		/* 指定ip地址. 
                            typedef uint32_t in_addr_t;
                            struct in_addr
                              {
                                in_addr_t s_addr;
                              };
        */
    
        /* Pad to size of `struct sockaddr'.  */
        unsigned char sin_zero[sizeof (struct sockaddr) -
    			   __SOCKADDR_COMMON_SIZE -
    			   sizeof (in_port_t) -
    			   sizeof (struct in_addr)];
      };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    eg:某个网络应用

    ip 172.2.1.11

    端口 6666

    struct sockaddr_in addr;//定义一个网络应用地址结构体addr
    addr.sin_family=AF_INET;//协议族
    addr.sin_port = htons(6666);//端口号是网络字节序,短整型
    addr.sin_addr.s_addr = inet_addr(172.2.1.11);//点分式IP转为32bit整数
    int ret = 0;//返回值
    ret = bind(sockfd,(struct sockaddr)&addr,sizeof(addr));
    if(ret == -1)
    {
        perror("bind error");
        return -1;
    }
    //bind 之后,进程就有了一个网络应用地址,能够进行网络通信
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    端口本地字节序转网络字节序

    h host 主机

    to 转换成

    n network 网络

    s short 16bit

    l long 32bit

    NAME
           htonl, htons, ntohl, ntohs - convert values between host and network byte order
    
    SYNOPSIS
           #include 
    
           uint32_t htonl(uint32_t hostlong);
    		将本地字节序转换成网络长整型
           uint16_t htons(uint16_t hostshort);
    		将本地字节序转换成网络短整型
           uint32_t ntohl(uint32_t netlong);
    		将网络字节序转换成本地长整型
           uint16_t ntohs(uint16_t netshort);
    		将网络字节序转换成本地短整型
                
            参数就是需要转换的数据
            返回值就是转换之后的数据
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    点分式ip与32bit ip的转换

    NAME
           inet_aton, inet_addr, inet_network, inet_ntoa, inet_makeaddr, inet_lnaof, inet_netof - Internet address manipulation routines
    
    SYNOPSIS
           #include 
           #include 
           #include 
    
    		inet_addr是用来将cp指定的点分式ip转换成32bit整数ip返回
           in_addr_t inet_addr(const char *cp);
    			@cp 指针,指向要转换的点分式字符串ip
                    成功返回转换后的32bit整数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    网络通信

    网络通信的实现—》 API(应用程序编程接口)

    1.socket 创建一个套接字

    2.bind 不动,将一个地址(网络应用地址:IP+传输协议+端口)与套接字绑定

    3.listen 监听,用于设定服务器监听套接字(监听后就可以获得客户端的请求)

    4.accpet 等待连接请求的到来

    5.connect 用于发起连接请求

    6.通信 数据收发

    ​ read/write

    ​ recv/send

    ​ recefrom/send_to

    7.关闭套接字

    close 套接字也就是一个文件描述符罢了

    思考 在网络通信在,服务器和客户端 双方的通信流程图

    在这里插入图片描述

    1 socket

    NAME
           socket - create an endpoint for communication
    
    SYNOPSIS
           #include           /* See NOTES */
           #include 
    		socket用来创建一个通信接口
           int socket(int domain, int type, int protocol);
    			@domain:指定域,协议族
                AF_UNIX, AF_LOCAL   Local communication              unix(7)
                    unix域协议/本地协议栈
                AF_INET             IPv4 Internet protocols          ip(7)
                    IPV4协议族
                AF_INET6            IPv6 Internet protocols          ipv6(7)
                    IPV6协议族
                @type:指定套接字类型(选择传输层协议)
    				SOCK_STREAM 流式套接字,面向TCP传输层协议
                    SOCK_DGRAM  数据报套接字,面向UDP传输层协议
                    SOCK_RAW	原始套接字
    			@protocol:协议,指定具体那个协议
                    一般为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

    2 bind

    用于绑定服务器地址(IP+传输层协议+端口号)

    NAME
           bind - bind a name to a socket
    
    SYNOPSIS
           #include           /* See NOTES */
           #include 
    		bind 绑定一个"名字”给socket(套接字描述符)
           int bind(int sockfd, const struct sockaddr *addr,
                    socklen_t addrlen);
    			@sockfd:指定套接字描述符,就是socket的返回值
                @addr:指向struct sockaddr 结构体指针,保存的是要绑定的地址
                @addrlen:地址长度,即参数addr的长度,sizeof (struct sockaddr)
    		返回值:成功返回0,失败返回-1,并且errno被设置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    关于网络应用的地址 =IP+传输层协议+端口号

    在 头文件中,定义了一个通用的网络应用地址结构体

    struct sockaddr {
                   sa_family_t sa_family;//指定协议栈,和socket函数的参数domain相同
        
                   char        sa_data[14];
        		//数组保存网络应用地址(IP+传输层协议+端口号)
               }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    上面这个结构体,并不是很好赋值和使用,一般在网络编程中,网络应用地址赋值可以采用定义在

    /usr/include/netinet/in.h中的结构体 struct sockaddr_in

    Structure describing an Internet socket address

    以太网地址结构体

    struct sockaddr_in
      {
        __SOCKADDR_COMMON (sin_);//协议栈   sin_family
        in_port_t sin_port;			/* 端口号,网络上一般是大端模式,计算机一般是小端模式  */
        struct in_addr sin_addr;		/* 指定ip地址. 
                            typedef uint32_t in_addr_t;
                            struct in_addr
                              {
                                in_addr_t s_addr;
                              };
        */
    
        /* Pad to size of `struct sockaddr'.  */
        unsigned char sin_zero[sizeof (struct sockaddr) -
    			   __SOCKADDR_COMMON_SIZE -
    			   sizeof (in_port_t) -
    			   sizeof (struct in_addr)];
      };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    eg:某个网络应用

    ip 172.2.1.11

    端口 6666

    struct sockaddr_in addr;//定义一个网络应用地址结构体addr
    addr.sin_family=AF_INET;//协议族
    addr.sin_port = htons(6666);//端口号是网络字节序,短整型
    addr.sin_addr.s_addr = inet_addr(172.2.1.11);//点分式IP转为32bit整数
    int ret = 0;//返回值
    ret = bind(sockfd,(struct sockaddr)&addr,sizeof(addr));
    if(ret == -1)
    {
        perror("bind error");
        return -1;
    }
    //bind 之后,进程就有了一个网络应用地址,能够进行网络通信
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    端口本地字节序转网络字节序

    h host 主机

    to 转换成

    n network 网络

    s short 16bit

    l long 32bit

    NAME
           htonl, htons, ntohl, ntohs - convert values between host and network byte order
    
    SYNOPSIS
           #include 
    
           uint32_t htonl(uint32_t hostlong);
    		将本地字节序转换成网络长整型
           uint16_t htons(uint16_t hostshort);
    		将本地字节序转换成网络短整型
           uint32_t ntohl(uint32_t netlong);
    		将网络字节序转换成本地长整型
           uint16_t ntohs(uint16_t netshort);
    		将网络字节序转换成本地短整型
                
            参数就是需要转换的数据
            返回值就是转换之后的数据
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    点分式ip与32bit ip的转换

    NAME
           inet_aton, inet_addr, inet_network, inet_ntoa, inet_makeaddr, inet_lnaof, inet_netof - Internet address manipulation routines
    
    SYNOPSIS
           #include 
           #include 
           #include 
    
    		inet_addr是用来将cp指定的点分式ip转换成32bit整数ip返回
           in_addr_t inet_addr(const char *cp);
    			@cp 指针,指向要转换的点分式字符串ip
                    成功返回转换后的32bit整数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    3 listen

    设置监听,用于监听网络上是否有客户端向“我”(服务器)发起链接请求

    NAME
           listen - listen for connections on a socket
    
    SYNOPSIS
           #include           /* See NOTES */
           #include 
    		
           int listen(int sockfd, int backlog);
    			@sockfd:指定要监听的套接字描述符
                @backlog:监听数量,大于0就行了
                返回值:成功返回0  
                    失败返回-1,并且errno被设置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    listen设置去监听套接字描述符,以便知道是否有人(客户端)向我(服务器)发起链接请求

    但是listen只监听,不处理链接

    Eg:

    int ret = 0;
    ret = listen(sockfd,10);
    if (ret == -1)
    {
    	perror("listen error");
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    4 accept

    用于等待链接请求,并且处理链接请求

    accpet会阻塞进程的运行,直到客户端向服务器发起了链接请求(客户端调用了connect)

    NAME
           accept, accept4 - accept a connection on a socket
    
    SYNOPSIS
           #include           /* See NOTES */
           #include 
    
           int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
    			@sockfd:指定套接字描述符
                @addr:指向struct sockaddr 结构体空间地址
                 		用于存储客户端的地址
                @addrlen:socklen_t空间地址
                    addrlen参数在使用前,需要先赋值 参数addr的长度
                    因为在函数内部,会先利用addrlen的值来确保捕获导致内存溢出
                    
                返回值:成功返回一个非负数,该值是一个文件描述符===》链接套接字描述符
                    用于 服务器与链接的客户端的通信接口
                    也就是说服务器是提供accept的返回值 来收发客户端的消息
                    失败 返回-1,并且errno被设置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    eg:

    struct sockaddr_in caddr;//用于保存客户端的地址
    socklen_t caddr_len = sizeof(caddr);//保存客户端地址长度,使用前保存该地址空间的长度
    int connfd = 0;//用于保存 链接套接字描述符
    connfd = accept(sockfd,(struct sockaddr*)&caddr,$caddr_len);
    if(connfd == -1)
    {
        perror("accept error");
        return -1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    如果accept指向成功,则connfd就是服务器与客户端之间的通信接口

    如果我们是多次调用accept,那么九年允许多个客户端连接,并且每个客户端连接上来后

    服务器上都有一一对应的connfd

    5 connect

    用于客户端向服务器发起链接请求

    NAME
           connect - initiate a connection on a socket
    
    SYNOPSIS
           #include           /* See NOTES */
           #include 
    
           int connect(int sockfd, const struct sockaddr *addr,
                       socklen_t addrlen);
    			@sockfd:指定套接字描述符
                @addr:指定要连接的服务器的地址
                @addrlen:addr的长度
                返回值:成功返回0
                    失败返回-1,并且errno被设置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    客户端调用 connect发起连接请求后,服务器的acceot接收到连接请求,并返回一个connfd用于服务器与客户端的通信接口

    此时,客户端写入/发送数据给sockfd的数据就会通过网络传递给服务器的connfd

    服务器就可以从connfd中读取/接收 这些数据

    eg:

    int ret = 0;
    struct sockaddr_in saddr;
    saddr.sin_family=AF_INET;//IPV4协议族
    saddr.sin_port = htons(6666);//端口号是网络字节序,短整型
    saddr.sin_addr.s_addr = inet_addr(172.2.1.110);//客户端的ip
    ret = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
    if (ret == -1)
    {
        perror("connect error");
        return -1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    6 数据收发

    1 read/write

    socket的网络通信,其利用的是文件描述符(符合Linux的设计哲学)

    2 recv/send 接收/发送

    recv 结束

    NAME
           recv, recvfrom, recvmsg - receive a message from a socket
    
    SYNOPSIS
           #include 
           #include 
    			recv用于从指定的套接字描述符中接收数据(同read)
           ssize_t recv(int sockfd, void *buf, size_t len, int flags);
    			@sockfd:指定套接字描述符,即从那个接收输入
                @buf:指向一块缓存区,用于存储接收到的数据
                @len:指定要接收的字节数
                @flags:接收标志位
                    0 默认接收,带阻塞接收
                    如果能接收到数据就接收数据
                    如果不能接收到数据,就一直等待,直到接收到数据
                    MSG_DONTWAIT  不阻塞接收
                    如果能够接收到数据,则接收数据
                    如果不能接收到数据,则直接返回
                    
                    返回值:
                    >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

    send 发送

    NAME
           send, sendto, sendmsg - send a message on a socket
    
    SYNOPSIS
           #include 
           #include 
    		send 用于向套接字描述符中发送数据
           ssize_t send(int sockfd, const void *buf, size_t len, int flags);
    				@sockfd:指定套接字描述符,即发送到那里去
                    @buf:指向要发送的数据
                    @len:要发送的数据大小,单位字节
                    @flags:发送标志位
                        0 阻塞发送
                        MSG_DONTWAIT 非阻塞发送
                        
                    返回值 成功返回实际发送的字节数(>=0)
    						失败返回-1 并且errno被设置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3 recvfrom/sendto

    从哪里接收/发送给谁

    特点

    recvfrom 可以保存是谁发送过来的数据(保存发送者的地址)

    sendto 可以指定发送的对象(地址),定向发送

    一般用于UDP通信,很少使用在TCP通信中?WHY?

    因为TCP面向连接的通信,在通信前就已经要求通信双方建立连接

    UDP是非连接的通信,所以他需要指定发送给谁,要保留发送者的地址

    recvfrom

    NAME
           recv, recvfrom, recvmsg - receive a message from a socket
    
    SYNOPSIS
           #include 
           #include 
     
           ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                            struct sockaddr *src_addr, socklen_t *addrlen);
    		@前面四个参数 同recv
            @src_addr:指向一个 struct sockaddr缓存区(地址),用于存储对方的地址
            @addrlen:指向一个 socklen_t缓存区(地址)用于存储对方地址的长度
                src_addr和addrlen的使用,参考accept函数
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    sendto

    NAME
           send, sendto, sendmsg - send a message on a socket
    
    SYNOPSIS
           #include 
           #include 
    
           ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                          const struct sockaddr *dest_addr, socklen_t addrlen);
    		@前四个参数 同send
            @dest_addr:指向一个 struct sockaddr缓存区(地址),表示发送给谁
            @addrlen:即dest_addr的长度
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4 recvmsg/sendmsg

    以结构体的形式 发送数据/接收数据

    7 关闭套接字

    套接字: socket 套接字描述符

    服务器:用于监听,看是否有客户端发起连接请求

    客户端:用于客户端与服务器的通信接口

    close 关闭套接字

    shutdown

    NAME
           shutdown - shut down part of a full-duplex connection
    
    SYNOPSIS
           #include 
    		shutdown用于关闭一个链接通道
           int shutdown(int sockfd, int how);
    		@sockfd:指定套关闭那个链接通道
            @how:怎样关闭
                SHUT_RD 关闭读
                SHUT_WR 关闭写
                SHUT_RDWR 关闭读写
    		
                返回值:成功返回0
                	失败返回-1,并且errno被设置
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    eg:我们搭建的服务器,只获取客户端的信息,而不用给客户端发送信息

    则可以关闭链接套接字的写功能

    shutdown(connfd,SHUT_WR); //connfd 就变成了只读
    
    • 1

    结合已有函数和示例代码,查看流程图

    尝试搭建一个TCP服务器

    create_tcpserver 创建TCP服务器

    1 创建socket套接字 网络通信接口

    2 bind 绑定网络应用地址(便于其他应用找到本程序的通信接口)

    3 listen 设置监听(内核监听是否有客户端向服务器发起连接请求)

    mian

    1 创建服务器 create_tcpserver

    2 等待客户端的连接请求,并于客户端建立连接 accept的返回值connfd就是一个用来与客户端通信的描述符

    (链接套接字描述符)

    3 根据需求,编写与客户端的通信

    案例:实现Ubuntu与开发板互相通信

    点此跳转查看案例源码

  • 相关阅读:
    单片机电子元器件-按键
    域名的命名规则有哪些?
    js:判断文本溢出隐藏生效text-overflow: ellipsis
    golang通过gorm操作sqlite设置主键自增
    11、利用大津算法完成一张图片的前景分割
    C# 创建标签PDF文件
    自我博弈1
    1288v3安装win10系统卡顿
    苍穹外卖——项目搭建
    关于云主机root无法从VNC登录处理
  • 原文地址:https://blog.csdn.net/xqmids99/article/details/133123341