嵌入式学习——3——网络编程,ip,TCP,UDP

1、网络编程

1.1 进程间的通信方式

linux内核的五大功能

        内存管理:内存的分配和回收

        进程管理:时间片轮转,上下文切换

        文件管理:将一堆0、1转化为人类识别的字符

        设备管理:linux下一切皆文件

        网络管理:网络协议栈的管理

协议:发送方和接收方都遵循的,数据发送及数据解析的一组准则)

内核提供三种:无名管道pipie、有名管道fifo、信号sem

systemV提供三种:消息队列msgset、共享内存shm、信号灯集semset

1.2 上述通信的弊端

上述六种只是实现了同一个主机,多个进程之间的数据通信,并不能完成跨主机的通信方式

所有,出现了  socket套接字通信,既能处理同一主机,多进程之间,还能完成跨主机的进程间通信

1.3跨主机通信方式

1.3.1 基于CS模型

        客户端服务器模型,要求客户端有一个客户端软件,客户端将数据传输给服务器,由服务器转发给其他客户端

1.3.2 基于BS模型

        浏览器服务器模型,由网页上的相关组件跟服务器进行交互,无需安装客户端软件

2、网络体系结构

2.1 TCP/IP协议分成了两个不同的协议

        检测网络传输中差错的传输控制协议TCP

        专门负责对不同网络进行互联的互联网协议IP

2.2 网络体系结构及OSI开放系统系统互联模型

        2.2.1 网络体系结构概念

        每一层都有自己独立的功能

        通常把功能相近的协议组织在一起放在一层,协议栈。所以每层其实有多个协议

        分层的好处:

        1、各层之间独立,每层不需要知道下一层如何实现,只需要知道,层与层之间的接口所提供的服务

        2、稳定,灵活性好,当任何一层发生变化时,只需要层间接口关系保持不变,而这层上,下的层级不受影响

        3、易于实现和维护(针对功能查找特定层)

        4、促进标准化工作,每层功能和所提供的服务都有了明确说明

        5、结构上不可分割开,每层都可以采用最合适的技术来实现

       

        2.3.2 OSI开放系统互联模型

    

         2.3.3 TCP/IP协议族(簇)的体系结

        TCP/IP协议簇是Internet事实上的工业标准

        分为四层:应用层-----传输层-----网络层-----链路层(网络接口和物理层)

       

         TCP/ip四层体系结构和OSI七层互联模型的对应关系

        2.3.4 数据封包和拆包的过程

一帧数据:

        大小:64--1518字节

        如果数据大于MTU(最大传输单元,linux最大默认是1500),需分成多次传输

        2.3.5 TCP/IP四层结构中常见的 协议

应用层:

        HTTP (Hypertext Transfer Protocol) 超文本传输协议 

                万维网的数据通信的基础

        FTP (File Transfer Protocol) 文件传输协议 

                用于在网络上进行文件传输的一套标准协议,使用TCP传输

        TFTP (Trivial File Transfer Protocol) 简单文件传输协议

                 用于在网络上进行文件传输的一套标准协议,使用UDP传输

        SMTP (Simple Mail Transfer Protocol) 简单邮件传输协议 

                一种提供可靠且有效的电子邮件传输的协议

传输层:

        TCP(Transport Control Protocol) 传输控制协议

                是一种面向连接的、可靠的、基于字节流的传输层通信协议

        UDP(User Datagram Protocol) 用户数据报协议

                是一种无连接、不可靠、快速传输的传输层通信协议

网络层:

        IP(Internetworking Protocol) 网际互连协议

                是指能够在多个不同网络间实现信息传输的协议

        ICMP(Internet Control Message Protocol) 互联网控制信息协议

                用于在IP主机、路由器之间传递控制消息、ping命令使用的协议

        IGMP(Internet Group Management Protocol) 互联网组管理

                是一个组播协议,用于主机和组播路由器之间通信

链路层:

        ARP(Address Resolution Protocol) 地址解析协议

                通过IP地址获取对方mac地址

        RARP(Reverse Address Resolution Protocol) 逆向地址解析协议

                通过mac地址获取ip地址

每层使用的协议,是由下层决定,并不能乱用

        

3、TCP和UDP

共同点:同属于传输层协议

区别:

        TCP:

        1、面向连接的,可靠的字节流服务

        2、传输过程中,数据无误、数据无丢失、数据无失序、数据无重复

                2.1 TCP会给每个数据包编上编号,该编号称之为序列号

                2.2 每个序列号都需要应答包应答,如果没有应答,则会将上面的包重复发送直到正确为止

        3、数据传输效率低,耗费资源多

        4、数据收发是不同步的

                4.1 为了提高效率,TCP会将多个较小,并且发送间隔短的数据包,沾成一个包发送,该现象称为沾包现象

                4.2 该沾包算法称之为Nagle算法

        5、TCP的使用场景:对传输质量比较高的以及传输大量数据的通信,在需要可靠通信的传输场合,一般使用TCP协议

        

        三次握手和四次挥手原因

        在TCP连接中,服务器端的SYN和ACK向客户端发送是一次性发送的

        在断开连接的过程中, B端向A端发送的ACK和FIN是分两次发送的。因为在B端接收到A端的FIN后, B端可能还有数据要传输,所以先发送ACK,等B端处理完自己的事情后就可以发送FIN断开连接了

        

三次握手:

        握手过程中使用了 TCP 的标志(flag) —— SYN(synchronize) 和ACK(acknowledgement)。大致流程如下:

SYN(同步序号,表示此报文是一个连接请求或者连接接收报文),ACK(确认位,对接收到报文的确认),FIN(表示发送方发送完数据,用来释放一个连接)

第一次握手:客户端向服务器端发送一个SYN J,表示客户端向服务器端发送一个连接请求报文,该报文的初始序列号为J。客户端进入SYN_SENT状态,等待服务器端确认。
第二次握手:服务器端向客户端响应一个SYN K, 表示服务器端向客户端发送一个连接请求报文,该报文的初始序列号为K。并对SYN J进行确认ACK J+1,服务器端进入SYN_REVD状态。
第三次握手:客户端再向服务器端发送一个确认ACK K+1。客户端和服务器端进入ESTABLISHED状态,完成三次握手,随后客户端和服务器端就可以开始传送数据了。

四次挥手:

        由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。先进行关闭的一方将执行主动关闭,而另一方被动关闭。

第一次挥手:客户端A发送一个FIN,用来关闭客户A到服务器B的数据传送。
第二次挥手:服务器B收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。
第三次挥手:服务器B关闭与客户端A的连接,发送一个FIN给客户端A。
第四次挥手:客户端A发回ACK报文确认,并将确认序号设置为收到序号加1。

TCP粘包问题如何解决

tcp粘包问题处理的方法:

1、定长发送法,发送端在发送数据时都以len为长度进行分包;

2、尾部标记序列法,在每个要发送的数据包的尾部设置一个特殊的字节序列;

3、头部标记分步接收法,定义一个用户报头,在报头中注明每次发送的数据包大小。

UDP

        1、面向无连接的,不保证数据可靠的,尽最大努力传输的协议

        2、数据传输过程中,可能出现数据丢失、重复、失序现象

        3、数据传输效率高,实时性高

        4、限制每次传输的数据大小,多出部分直接忽略删除

        5、收发是同步的,不会沾包

        6、适用场景:发送小尺寸的,在接收到数据给出应答比较困难的情况下

总结

1) TCP提供面向连接的传输,通信前要先建立连接(三次握手机制); UDP提供无连接的传输,通信前不需要建立连接。
2) TCP提供可靠的传输(有序,无差错,不丢失,不重复); UDP提供不可靠的传输。
3) TCP面向字节流的传输,因此它能将信息分割成组,并在接收端将其重组; UDP是面向数据报的传输,没有分组开销。
4) TCP提供拥塞控制和流量控制机制; UDP不提供拥塞控制和流量控制机制。

(参考链接:https://blog.csdn.net/striveb/article/details/84063712)

练习:简易TCP和UDP链接

TCP:练习

/*
 * @Description: TCP 服务器端
 */
#include "../header.h"

#define SER_PORT 8888
#define SER_IP "192.168.xxx.xxx"

int main(int argc, char const *argv[]) {
    // 1 创建套接字
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    printf("sfd: %d\n", sfd);

    // 2 绑定ip和端口号
    struct sockaddr_in addr;
    addr.sin_family = AF_INET;                                     // 通信域
    addr.sin_port = htons(SER_PORT);                               // 端口号
    addr.sin_addr.s_addr = inet_addr(SER_IP);                      // ip地址
    if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { // 绑定
        perror("bind");
        return -1;
    }
    printf("bind success\n");

    // 3 监听
    if (listen(sfd, 128) == -1) {
        perror("listen");
        return -1;
    }
    printf("listen success\n");

    // 4 等待客户端连接
    struct sockaddr_in naddr_in = {0};
    socklen_t naddr_len = sizeof(naddr_in);
    int new_fd = -1;
    if ((new_fd = accept(sfd, (struct sockaddr *)&naddr_in, &naddr_len)) ==
        -1) {
        perror("accept");
        return -1;
    }
    // 如果没有连接进入,会阻塞在这里
    printf("对方ip地址:%s, 端口号:%d\n", inet_ntoa(naddr_in.sin_addr),
           ntohs(naddr_in.sin_port));
    printf("accept success\n");

    char addr_buff[1024] = {0};
    while (1) {
        memset(addr_buff, 0, sizeof(addr_buff));
        int r_num = recv(new_fd, addr_buff, sizeof(addr_buff), 0);
        if (r_num == 0) {
            printf("对方已经下线");
            break;
        }
        printf("%s:%d--%s\n", inet_ntoa(naddr_in.sin_addr), naddr_in.sin_port,
               addr_buff);
        strcat(addr_buff, "*-* ");
        send(new_fd, addr_buff, sizeof(addr_buff), 0);
    }

    // 5 关闭套接字
    close(new_fd);
    close(sfd);
    printf("close success\n");

    return 0;
}
/*
 * @Description: TCP客户端
 */
#include "../header.h"
#define S_PORT 8888
#define S_IP "192.168.xxx.xxx"
#define C_PORT 7772
#define C_IP "192.168.xxx.xxx"

int main(int argc, char const *argv[]) {
    int sfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sfd == -1) {
        perror("socket");
        return -1;
    }

    struct sockaddr_in bind_address = {0};
    bind_address.sin_family = AF_INET;
    bind_address.sin_port = htons(C_PORT);
    bind_address.sin_addr.s_addr = inet_addr(C_IP);
    if (bind(sfd, (struct sockaddr *)&bind_address, sizeof(bind_address)) ==
        -1) {
        perror("bind");
        return -1;
    }
    printf("bind success\n");

    struct sockaddr_in connect_address = {0};
    connect_address.sin_family = AF_INET;
    connect_address.sin_port = htons(S_PORT);
    connect_address.sin_addr.s_addr = inet_addr(S_IP);
    if (connect(sfd, (struct sockaddr *)&connect_address,
                sizeof(connect_address)) == -1) {
        perror("connect");
        return -1;
    }
    printf("connect success\n");

    char send_buf[1024] = {0};
    char recv_buf[1024] = {0};
    while (1) {
        printf("请输入内容:");
        bzero(send_buf, sizeof(send_buf));
        fgets(send_buf, sizeof(send_buf), stdin);
        if (strlen(send_buf) > 1) {
            send_buf[strlen(send_buf) - 1] = 0;
        }
        if (send(sfd, send_buf, sizeof(send_buf), 0) == -1) {
            perror("send");
            printf("发送失败");
        }

        bzero(recv_buf, sizeof(recv_buf));
        recv(sfd, recv_buf, sizeof(recv_buf), 0);
        printf("返回值:%s\n", recv_buf);
    }
    close(sfd);

    return 0;
}

UDP练习

/*
 * @Description: UDP 服务器端
 */
#include "../header.h"

#define S_IP "192.168.xxx.xxx"
#define S_PORT 8888

int main(int argc, char const *argv[]) {
    int server_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (server_fd == -1) {
        perror("socket");
        return -1;
    }

    struct sockaddr_in ser_address = {0};
    ser_address.sin_family = AF_INET;
    ser_address.sin_port = htons(S_PORT);
    ser_address.sin_addr.s_addr = inet_addr(S_IP);
    if (bind(server_fd, (struct sockaddr *)&ser_address, sizeof(ser_address)) ==
        -1) {
        perror("bind");
        return -1;
    }

    struct sockaddr_in rev_address = {0};
    socklen_t rev_len = sizeof(rev_address);
    char recv_buf[1024] = {0};
    while (1) {
        bzero(recv_buf, sizeof(recv_buf));
        recvfrom(server_fd, recv_buf, sizeof(recv_buf), 0,
                 (struct sockaddr *)&rev_address, &rev_len);
        printf("收到消息:%s\n", recv_buf);

        strcat(recv_buf, "--------");
        if (sendto(server_fd, recv_buf, strlen(recv_buf), 0,
                   (struct sockaddr *)&rev_address,
                   sizeof(rev_address)) == -1) {
            perror("sendto");
            return -1;
        }
        printf("回复消息发送成功\n");
    }

    close(server_fd);
    return 0;
}
/*
 * @Description: UDP 客户端
 */
#include "../header.h"

#define S_IP "192.168.xxx.xxx"
#define S_PORT 8888
#define C_IP "192.168.xxx.xxx"
#define C_PORT 6666

int main(int argc, char const *argv[]) {
    int cli_fd = socket(AF_INET, SOCK_DGRAM, 0);
    if (cli_fd == -1) {
        perror("socket");
        return -1;
    }

    struct sockaddr_in cli_address = {0};
    cli_address.sin_family = AF_INET;
    cli_address.sin_port = htons(C_PORT);
    cli_address.sin_addr.s_addr = inet_addr(C_IP);
    if (bind(cli_fd, (struct sockaddr *)&cli_address, sizeof(cli_address)) ==
        -1) {
        perror("bind");
        return -1;
    }

    struct sockaddr_in send_address = {0};
    send_address.sin_family = AF_INET;
    send_address.sin_port = htons(S_PORT);
    send_address.sin_addr.s_addr = inet_addr(S_IP);

    char send_buf[1024] = {0};
    int send_res = 0;
    while (1) {
        printf("请输入内容:");
        bzero(send_buf, sizeof(send_buf));
        fgets(send_buf, sizeof(send_buf), stdin);
        if (strlen(send_buf) > 1) {
            send_buf[strlen(send_buf) - 1] = 0;
        }
        send_res =
            sendto(cli_fd, send_buf, sizeof(send_buf), 0,
                   (struct sockaddr *)&send_address, sizeof(send_address));
        if (send_res == -1) {
            perror("sendto");
            return -1;
        }
        bzero(send_buf, sizeof(send_buf));
        recvfrom(cli_fd, send_buf, sizeof(send_buf), 0, NULL, NULL);
        printf("返回值:%s\n", send_buf);
    }

    close(cli_fd);
    return 0;
}
  • 30
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
全志T3开发板是一块能够帮助嵌入式初学者进行学习和测试的开发板。该开发板采用了全志公司推出的T3芯片作为主控芯片,拥有强大的处理性能和丰富的硬件接口,非常适合初学者进行嵌入式开发。 在使用全志T3开发板进行学习和测试时,首先需要熟悉该开发板的硬件接口和功能。该开发板提供了丰富的硬件接口,包括GPIO口、SPI口、I2C口、UART口等,这些接口可以用于连接外部硬件模块,实现各种功能。同时,该开发板还具备WiFi和蓝牙功能,方便进行网络通信。 接下来,可以选择一个简单的嵌入式项目进行学习和测试。例如,可以通过GPIO口控制LED灯的亮灭,通过SPI口读取传感器数据等。在这个过程中,需要学习如何使用开发板的各种硬件接口和相应的编程语言,例如C语言或Python。可以查阅开发板的相关文档和教程,以及互联网上的资源,来学习如何编写代码、调试和测试。 此外,全志T3开发板还支持Linux和Android系统,可以学习如何在这些系统上进行嵌入式开发。通过学习和测试,可以深入理解嵌入式系统的原理和开发方法,提升自己的嵌入式开发能力。 总之,全志T3开发板是一块非常适合嵌入式初学者进行学习和测试的开发板。通过学习和测试,可以掌握嵌入式开发的基础知识和技能,并在实践中逐渐提升自己的嵌入式开发水平。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值