【嵌入式Linux】<总览> 网络编程(更新中)

文章目录

前言

一、网络知识概述

1. 网路结构分层

2. socket

3. IP地址

4. 端口号

5. 字节序

二、网络编程常用API

1. socket函数

2. bind函数

3. listen函数

4. accept函数

5. connect函数

6. read和recv函数

7. write和send函数

三、TCP编程

1. TCP介绍

2. TCP通信流程

3. TCP服务端和客户端程序


前言

记录学习嵌入式Linux网络编程的知识重点与难点,若涉及版权问题请联系本人删除!


一、网络知识概述

1. 网路结构分层

①主要存在两种分层模型:OSI七层模型(理论)和TCP/IP四层模型(实际)。

②分层思想:采用分治法,将复杂问题划分为各个层级上的子问题。每一层向上提供服务,同时使用下层提供的服务。

③网络封包和拆包:如下图所示,从主机A传输数据到主机B的流程一般为:主机A的应用层封装好了传输数据后,传输层会给上层数据包添加TCP头部,网络层会给上层数据包添加IP头部,网络接口层会给上层数据包添加对应的头部和尾部(一般为CRC校验);路由器将接收到的帧去掉头部和尾部传输给上一层,传输层将解析IP地址并查表转发,然后继续封装为帧,传输到指定的主机B;主机B逐层拆包,最终在应用层获取接收到的真正数据。

2. socket

①概念:socket是一个特殊的文件描述符,用于网络通信。

②socket分类:

  • 流式套接字(SOCK_STREAM):对应TCP,面向连接、可靠。
  • 数据报套接字(SOCK_DGRAM):对应UDP,无连接、不可靠。
  • 原始套接字(SOCK_RAW):对应多个协议,可以直接访问IP、ICMP,跨过了传输层。

3. IP地址

①概念:IP地址是网络中主机地址的标识。通过IP可以找到对应的主机。

②IP地址分类:

  • IPv4:一个32位(4字节)的整数,每个字节用.来分隔。每个字节的数据范围为0~255。例如: 192.168.5.11  不够用,引入局域网可以解决不够用的问题
  • IPv6:一个128(16字节)的整数,每两个字节为一部分,总共有8部分,每部分用:分隔。例如:2001:0db8:3c4d:0015:0000:0000:1a2f:1a2b   “地球上每一粒沙子都能分配一个”

③特殊的IP地址:

  • 局域网IP:192.XXX.XXX.XXX    10.XXX.XXX.XXX
  • 广播IP:XXX.XXX.XXX.255    255.255.255.255(全网广播)
  • 组播IP:224.XXX.XXX.XXX到239.XXX.XXX.XXX

4. 端口号

①概念:是一个16位的整数,范围:1~65535.

②端口号分类:

  • 众所周知的端口:1~1023. (FTP: 21, SSH: 22, HTTP: 80, HTTPS: 469)
  • 保留端口:1024~5000  不建议使用
  • 可以使用端口:5001~65535

③注意事项:

  • TCP端口和UDP端口是相互独立的。(因为它们在内核中的处理路径不同)
  • 网络通信由IP地址+端口号来决定。IP地址确定主机位置,端口号确定主机中哪个进程来处理。

5. 字节序

①概念:当涉及内存中存取多字节数据时,就会遇到字节序的问题。

②两种字节序:

  • 小端:低字节序内容存储到低地址的内存中。
  • 大端:低字节序内容存储到高地址的内存中。

③注意事项:

  • 一般本地采用小端模式,网络传输采用大端模式。
  • 在发送数据和接收数据前,应该先将本地字节序和网络字节序进行转换。

④主机字节序和网络字节序转换函数:

#include <arpa/inet.h>

// 主要用于网络通信过程中IP和端口的转换
uint16_t htons(uint16_t hostshort);   //短整形,主机字节序->网络字节序

uint32_t htonl(uint32_t hostlong);    //整形,主机字节序->网络字节序	

uint16_t ntohs(uint16_t netshort);    //短整形,网络字节序->主机字节序

uint32_t ntohl(uint32_t netlong);     //整形,网络字节序->主机字节序

⑤IP地址转换函数:

inet_pton函数:将IP地址从主机字节序->网络字节序。

【1】头文件:#include <arpa/inet.h>

【2】函数原型:int inet_pton(int af, const char *src, void *dst);

【3】参数说明:

  • af:地址族,填写AF_INET(IPv4)或者AF_INET6(IPv6)
  • src:传入参数,即要转换的IP地址。例如:192.168.5.11
  • dst:传出参数,存放转换后的IP地址。

【4】返回值:成功返回1,第一个参数无效返回-1,第二个参数无效返回0.

inet_ntop函数:将IP地址从网络字节序->主机字节序。

【1】头文件:#include <arpa/inet.h>

【2】函数原型:const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);

【3】参数说明:

  • af:地址族,填写AF_INET(IPv4)或者AF_INET6(IPv6)
  • src:传入参数,其中存储了网络字节序的IP地址。
  • dst:传出参数,存放转换后的IP地址。
  • size:dst指向的内存中最多可以存储多少个字节。

【4】返回值:成功返回指针指向第三个参数对应的地址,失败返回NULL。


二、网络编程常用API

以下函数除read和write外都需包含头文件#include <sys/types.h>、#include <sys/socket.h>

1. socket函数

【1】功能:创建网络套接字。

【2】函数原型:int socket(int domain, int type, int protocol);

【3】参数说明:

domainAF_INETIPv4协议
AF_INET6IPv6协议
AF_LOCAL本地通信
typeSOCK_STREAM流式套接字,对应TCP
SOCK_DGRAM数据报套接字,对应UDP
SOCK_RAW原始套接字
protocol一般写0即可,使用默认协议。非0一般用于原始套接字。

【4】返回值:成功返回套接字fd,失败返回-1。

【5】代码示例:

int fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd < 0) {
    perror("socket error");
    return -1;
}

2. bind函数

【1】功能:将本地的IP、端口与套接字绑定。

【2】函数原型:int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

【3】参数说明:

sockfd套接字,由socket函数生成
addrstruct sockaddr类型的变量的地址
addrlenaddr指向的内存的地址大小

struct sockaddr结构体:(写数据时一般不用)

struct sockaddr {
	sa_family_t sa_family;       // 地址族协议, ipv4
	char        sa_data[14];     // 端口(2字节) + IP地址(4字节) + 填充(8字节)
}

struct sockaddr_in结构体:(常用)一般将端口和IP地址保存在该类型的变量中,然后强转为struct sockaddr类型。(它们的大小完全相同)

struct sockaddr_in
{
    sa_family_t sin_family;		/* 地址族 */
    in_port_t sin_port;         /* 端口号, 2字节 -> 网路字节序 */
    struct in_addr sin_addr;    /* IP地址, 4字节 -> 网络字节序 */
    /* 填充8字节,初始化为0 */
    unsigned char sin_zero[sizeof (struct sockaddr) - sizeof(sin_family) -
               sizeof (in_port_t) - sizeof (struct in_addr)];
};

struct in_addr
{
    in_addr_t s_addr;
};

【4】返回值:成功返回0,失败返回-1。

【5】代码示例:

#define  SERV_PORT    9999
#define  SERV_IP      "192.168.5.12"

struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;        //IPv4协议
sin.sin_port = htons(SERV_PROT); //端口号,转化为网络字节序
//IP地址,转化为网络字节序
if (inet_pton(AF_INET, SERV_IP, &sin.sin_addr.s_addr) != 1) {
    perror("inet_pton error");
    return -1;
}
if (bind(fd, (struct sockaddr*)&sin, sizeof(sin))) {
    perror("bind error");
    return -1;
}

3. listen函数

【1】功能:设置套接字监听。调用之前需要bind绑定。

【2】函数原型:int listen(int sockfd, int backlog);

【3】参数说明:

sockfd监听的文件描述符,由socket函数生成
backlog同时处理的最大连接数,一般可取5,最大值为128。表示系统允许2*backlog+1个客户端同时进行三次握手。

【4】返回值:成功返回0,失败返回-1。

【5】代码示例:

if (listen(fd, 128)) {
    perror("listen error");
    return -1;
}

4. accept函数

【1】功能:服务器阻塞等待客户端的连接请求,建立新连接,得到通信使用的套接字。

【2】函数原型:int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

【3】参数说明:

sockfd监听的文件描述符,由socket函数生成
addr传出参数,保存客户端的地址信息
addrlenaddr指向的内存的大小

【4】返回值:成功返回与客户端进行通信的套接字,失败返回-1。

【5】代码示例:

struct sockaddr_in clientAddr;
int clientLen = sizeof(clientAddr);
int cfd = accept(fd, (struct sockaddr*)&clientAddr, &clientLen);
if (cfd < 0) {
    perror("accept error");
    return -1;
}

5. connect函数

【1】功能:客户端发起连接请求。成功连接服务器后,客户端会自动随机绑定一个端口。

【2】函数原型:int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

【3】参数说明:

sockfd套接字,由socket函数生成
addr存储要连接的服务端的IP和端口信息(需要网络字节序)
addrlenaddr指向的内存的大小

【4】返回值:成功返回0,失败返回-1。

【5】代码示例:

#define  SERV_PORT    9999
#define  SERV_IP      "192.168.5.12"

struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;//IPv4协议
sin.sin_port = htons(SERV_PORT);//目标服务端的端口号,网络字节序
//目标服务端的IP地址,网络字节序
if (inet_pton(AF_INET, SERV_IP, &sin.sin_addr.s_addr) != 1) {
    perror("inet_pton error");
    return -1;
}
//调用connect函数
if (connect(fd, (struct sockaddr*)&sin, sizeof(sin)) == -1) {
    perror("connect error");
    return -1;
}

6. read和recv函数

【1】功能:接收数据。

【2】函数原型:

  • ssize_t read(int sockfd, void *buf, size_t len);
  • ssize_t recv(int sockfd, void *buf, size_t len, int flags);

【3】参数说明:

sockfd用于通信的文件描述符
buf存储接收的数据
lenbuf指向的内存的容量
flags特殊属性,一般不使用,指定为0

【4】返回值:>0表示实际接收的字节数,==0表示对方断开了连接,-1表示失败。

7. write和send函数

【1】功能:接收数据。

【2】函数原型:

  • ssize_t write(int sockfd, const void *buf, size_t len);
  • ssize_t send(int sockfd, const void *buf, size_t len, int flags);

【3】参数说明:

sockfd用于通信的文件描述符
buf发送的数据
lenbuf的长度
flags特殊属性,一般不使用,指定为0

【4】返回值:>0表示实际发送的字节数(与len相等),-1表示失败。


三、TCP编程

1. TCP介绍

TCP(Transmission Control Protocol)是面向连接的、可靠的、基于字节流的传输层通信协议。

  • 面向连接:建立连接需要三次握手,断开连接需要四次挥手。
  • 可靠安全:在TCP通信过程中,对每个发送的数据包都会进行校验,若数据丢失则重传。
  • 流式传输:发送端和接收端的处理速度、数据量都可以不一致。

2. TCP通信流程

参考爱编程的大丙:

3. TCP服务端和客户端程序

服务端程序:接收客户端数据并打印,同时将收到的数据重新发回客户端。

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>

#define SERV_PORT   9999

int main(int argc, char **argv)
{
    /* 1.创建TCP的套接字 */
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1) {
        perror("server socket error");
        return -1;
    }

    /* 2.绑定IP和端口号 */
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(SERV_PORT);
    addr.sin_addr.s_addr = INADDR_ANY;//INADDR_ANY宏表示本机所有IP
    int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
    if (ret == -1) {
        perror("server bind error");
        return -1;
    }

    /* 3.设置监听 */
    ret = listen(fd, 128);
    if (ret == -1) {
        perror("server listen error");
        return -1;
    }

    /* 4.等待客户端连接 */
    struct sockaddr_in clientAddr;
    int clientLen = sizeof(clientAddr);
    int cfd = accept(fd, (struct sockaddr*)&clientAddr, &clientLen);
    if (cfd == -1) {
        perror("server accept error");
        return -1;
    }
    //打印客户端信息
    char ip[24] = {0};
    printf("客户端IP: %s, 端口: %d\n",
            inet_ntop(AF_INET, &clientAddr.sin_addr.s_addr, ip, sizeof(ip)),
            ntohs(clientAddr.sin_port));

    /* 5.与客户端通信 */
    while (1) {
        //接收数据
        char buf[1024];
        memset(buf, 0, sizeof(buf));
        ret = read(cfd, buf, sizeof(buf));
        if (ret == 0) {
            printf("服务端:与客户端断开连接\n");
            break;
        } else if (ret < 0) {
            perror("server read error");
            break;
        } else {
            //打印接收数据,并重新发回接收的数据
            printf("服务端接收: %s\n", buf);
            write(cfd, buf, sizeof(buf));
        }
    }

    /* 6.关闭文件描述符 */
    close(cfd);
    close(fd);
    return 0;
}

客户端程序:每1秒发送指定数据,同时接收来自服务端的数据。

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <unistd.h>

#define SERV_PORT   9999
#define SERV_IP     "192.168.124.6"

int main(int argc, char **argv)
{
    /* 1.创建TCP的套接字 */
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd == -1) {
        perror("client socket error");
        return -1;
    }

    /* 2.与服务端建立连接 */
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(SERV_PORT);
    inet_pton(AF_INET, SERV_IP, &addr.sin_addr.s_addr);
    int ret = connect(fd, (struct sockaddr*)&addr, sizeof(addr));
    if (ret == -1) {
        perror("client connect error");
        return -1;
    }
    //打印连接的服务端信息
    printf("连接的服务端IP: %s, 端口: %d\n", SERV_IP, SERV_PORT);

    /* 3.与服务端通信 */
    int number = 0;
    while (1) {
        //发送数据
        char buf[1024] = {0};
        sprintf(buf, "客户端: number = %d\n", number++);
        write(fd, buf, sizeof(buf));

        //接收数据
        memset(buf, 0, sizeof(buf));
        ret = read(fd, buf, sizeof(buf));
        if (ret == 0) {
            printf("客户端:与服务端断开连接\n");
            break;
        } else if (ret < 0) {
            perror("client read error");
            break;
        } else {
            printf("客户端接收: %s\n", buf);
        }
        sleep(1);//发送数据慢一些
    }

    /* 4.关闭文件描述符 */
    close(fd);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/801058.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

新版本WPS不登录无法编辑的解决办法

原因分析&#xff1a;新版本的WPS因加入多种在线功能&#xff0c;建议登录账号获得更加体验 解决办法&#xff1a;首选第一种修改注册表后重启WPS&#xff0c;第二种仅作为临时满足工作需求&#xff0c;过一段时间会自动失效 方法一&#xff1a;键盘同时按下WINR键&#xff0c;…

【Python】基础语法(函数、列表和元组、字典、文件)

。一、函数 1、函数是什么 编程中的函数和数学中的函数有一定的相似之处。 数学上的函数&#xff0c;比如 y sin x&#xff0c;x 取不同的值&#xff0c;y 就会得到不同的结果。 编程中的函数是一段可以被重复使用的代码片段。 &#xff08;1&#xff09;求数列的和&…

MySQL索引特性(上)

目录 索引的重要 案例 认识磁盘 MySQL与存储 先来研究一下磁盘 扇区 定位扇区 结论 磁盘随机访问与连续访问 MySQL与磁盘交互基本单位 建立共识 索引的理解 建立测试表 插入多条记录 局部性原理 所有的MySQL的操作(增删查改)全部都是在MySQL当中的内存中进行的&am…

网友提问:HTML CSS JS很低级吗?

这话听起来就像有人在说披萨只是面团加奶酪&#xff0c;完全忽略了上面的美味配料和烘烤的艺术啊&#xff01;HTML、CSS、JS这三位可是前端开发的铁三角&#xff0c;它们一点都不“低级”&#xff0c;反而相当关键。 HTML就像是房子的骨架&#xff0c;没有它&#xff0c;网页就…

【iOS】——MRC

一、引用计数 内存管理的核心是引用计数器&#xff0c;用一个整数来表示对象被引用的次数&#xff0c;系统需要根据引用计数器来判断对象是否需要被回收。 在每次 RunLoop 迭代结束后&#xff0c;都会检查对象的引用计数器&#xff0c;如果引用计数器等于 0&#xff0c;则说明…

单链表算法 - 链表分割

链表分割_牛客题霸_牛客网现有一链表的头指针 ListNode* pHead&#xff0c;给一定值x&#xff0c;编写一段代码将所有小于x的。题目来自【牛客题霸】https://www.nowcoder.com/practice/0e27e0b064de4eacac178676ef9c9d70思路: 代码: /* struct ListNode {int val;struct List…

烧掉一个亿就为造好一把椅子,西昊如何策划人体工学椅极致产品力

2011年到2015年这4年期间&#xff0c;功能单一&#xff0c;价值雷同的椅子市场趋于饱和&#xff0c;竞争的白热化和同质化带来了全行业的价格战&#xff0c;彼时西昊的经营陷入巨大的困境&#xff0c;所有的产品和钱都变成成库存&#xff0c;现金流几乎枯竭。因此&#xff0c;西…

文心一言《使用手册》,文心一言怎么用?

一、认识文心一言 &#xff08;一&#xff09;什么是文心一言 文心一言是百度研发的 人工智能大语言模型产品&#xff0c;能够通过上一句话&#xff0c;预测生成下一段话。 任何人都可以通过输入【指令】和文心一言进行对话互动、提出问题或要求&#xff0c;让文心一言高效地…

智能家居产品公司网站源码,自适应布局设计,带完整演示数据

适合各类智能家居电子产品使用的网站源码&#xff0c;深色大气设计&#xff0c;自适应布局设计&#xff0c;pc手机均可完美适配&#xff0c;带完整演示数据。 独家原创资源。源码是asp开发的&#xff0c;数据库是access&#xff0c;主流的虚拟主机空间都支持asp&#xff0c;直…

<Rust><GUI>rust语言GUI库tauri体验:前、后端结合创建一个窗口并修改其样式

前言 本文是rust语言下的GUI库&#xff1a;tauri来创建一个窗口的简单演示&#xff0c;主要说明一下&#xff0c;使用tauri这个库如何创建GUI以及如何添加部件、如何编写逻辑、如何修改风格等&#xff0c;所以&#xff0c;这也是一个专栏&#xff0c;将包括tauri库的多个方面。…

面对人工智能发展的伦理挑战:应对策略与未来方向

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

硬件开发笔记(二十六):AD21导入电感原理图库、封装库和3D模型

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/140437290 长沙红胖子Qt&#xff08;长沙创微智科&#xff09;博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV…

SAP ERP与金蝶云星空的集成案例(新能源光伏行业)

一、项目环境 阳光新能源开发股份有限公司&#xff08;简称“阳光电源”&#xff09;是一家专注于新能源开发利用的国家高新技术企业。作为阳光电源&#xff08;股票代码&#xff1a;300274&#xff09;旗下的新能源开发投资平台&#xff0c;阳光新能源聚焦光伏、风电、风光…

暴雨让服务器不怕热҈热҈热҈热҈

在AI算力呈几何倍数增长的趋势下&#xff0c;算力逐渐朝着“高性能、高密度、高耗能“发展。在高耗能的算力下&#xff0c;AI服务器功率已逐步逼近风冷散热极限&#xff0c;而液冷作为更加高效、低能耗的制冷技术&#xff0c;逐渐成为了高密度算力散热场景的首选方案。 液冷的…

数据结构-java中链表的存储原理及使用方式

目录 链表&#xff08;线性表的链式存储&#xff09; 代码实例&#xff1a;&#xff08;链表构建&#xff0c;头插尾插&#xff09; LinkedList LinkedList的使用&#xff1a; 1、构造方法 2、操作方法 LinkedList 和 ArrayList 的区别 链表&#xff08;线性表的链式存储…

Minio搭建文件服务器的学习

MinIO是一个高性能的开源对象存储服务器&#xff0c;与Amazon S3兼容。它使用Go语言编写&#xff0c;可以在多种操作系统上运行&#xff0c;如Linux、MacOS和Windows等。MinIO的分布式特性使其能够轻松扩展存储容量和处理能力&#xff0c;满足大规模数据存储的需求。 使用Docke…

你也想做一个Element-ui吧!!!——Blueの前端路(一)

目录 前言&#xff1a; 父子组件 button组件 使用vue脚手架初始化一个项目 如何封装&#xff0c;注册和使用一个组件 main.js中将组件设置为全局 使用 此组件我们所需实现的内容 type 父组件组件传递type属性 子组件接收负组件传递的数据 通过绑定类名的方法动态控制…

北汽蓝谷:预期能否兑现

天天提热搜&#xff0c;公司却一路亏损&#xff0c;股价却一路走高&#xff0c;今天说说——北汽蓝谷 问最近车圈谁最热&#xff0c;北汽蓝谷少不了。先是享界S9上市定档&#xff0c;华为余承东“在线带货”。 后有百度无人驾驶萝卜快跑引发全网热议&#xff0c;所用车型便来自…

TF/SD卡开发驱动(SPI)

TF与SD卡本质上来说都是flash类型的存储器 可以理解为TF卡是SD卡的升级版&#xff0c;体积小功能强大&#xff0c;SD卡是传统意义上的存储卡&#xff0c;适用范围比较广&#xff0c;而SD卡的驱动方式有两种 SDIO 和 SPI&#xff0c;同理TF卡也是一样 &#xff08;在资源足够…

2024黑马AI+若依框架项目开发 个人心得、踩坑和bug记录 全网最快最全 基础功能认识篇

2024黑马AI若依框架项目开发 个人心得、踩坑和bug记录 全网最快最全 基础功能认识篇 你好,我是Qiuner. 为帮助别人少走弯路和记录自己编程学习过程而写博客 这是我的 github https://github.com/Qiuner ⭐️ ​ gitee https://gitee.com/Qiuner &#x1f339; 如果本篇文章帮到…