• linux网络编程大杂烩==Linux应用编程7


    一、Linux 网络编程框架

    1、网络是分层的

    • (1)OSI 七层模型:应用层、表示层、会话层、传输层、网络层、数据链路层、物理层。
    • (2)网络为什么要分层:互联网及其复杂,需要分层以便更好地实现网络通信。

    2.TCP/IP 协议引入

    • (1)TCP/IP 协议是用得最多的网络实现协议。
    • (2)TCP/IP 协议分为四层,对应着 OSI 的七层:应用层(包括 OSI 的表示层和会话层)、传输层、网络层、链路层(包括 OSI 的物理层)。
    • (3)网络编程时最关注应用层,了解传输层,网络层和链路层不用管。

    3、BS 和 CS

    • (1)BS 架构:broswer-server,浏览器-服务器框架。
    • (2)CS 架构:client-server,客户端-服务器框架

    二、TCP 协议的学习 1

    1.关于 TCP 理解的重点

    • (1)TCP 协议工作在传输层,对上服务 socket 接口,对下调用 IP 层
    • (2)TCP 协议面向连接,通信前必须三次握手建立连接关系。
    • (3)TCP 协议提供可靠传输,不怕丢包、乱序等问题。

    2、TCP 如何保证可靠传输

    • (1)TCP 在传输有效信息前要求通信双方必须先握手建立连接。
    • (2)TCP 接收方收到数据包后会发 ack 给发送方,若发送方未收到 ack 则丢包重传。
    • (3)TCP 有效内容会附带校验信息,以防止内容在传输过程中出现错误。
    • (4)发送方会给各个报文编号,接收方收到报文后会校验编号,一旦顺序错误则重传。
    • (5)TCP 会根据网络情况来调节发送速率(滑动窗口协议)

    三、TCP 协议的学习 2

    1、TCP 的三次握手

    • (1)建立连接需要三次握手:客户端向服务器发送 SYN 报文,服务器回复 SYN + ACK 报文, 客户端回复 ACK 报文。三次握手的目的是为了防止已经失效的连接请求报文段突然又传送到了服务端,产生错误。
      • ①第一次握手:客户端发送一个 TCP 标志位 SYN=1,ACK=0 的数据包给服务端,并随机会产 生一个 Seq=J。当服务端接收到这个数据后,服务端由 SYN=1 可知客户端是 想要建立连接。
      • ②第二次握手:服务端要对客户端的联机请求进行确认,向客户端发送应答号 ACK=1、SYN=1 , 确认号 Ack=J+1,此值是客户端的序列号加 1,还会产生一个随机的序列号 Seq=K,这样就告诉客户端可以进行连接。
      • ③第三次握手:客户端收到数据后检查 Ack 是否为 J+1,以及标志位 ACK 的值是否为 1,若 为 1,则会发送 ACK=1、确认号码 Ack=K+1,告诉服务端,你的请求连接被确 认,连接可以建立,Client 和 Server 进入 ESTABLISHED 状态,完成三次握手, 随后 Client 与 Server 之间可以开始传输数据了。
    • (2)建立连接的条件:服务器 accept 时客户端主动发起 connect。

    2、TCP 的四次挥手

    • (1)关闭连接需要四次挥手:客户端向服务器发送FIN报文,服务器回复ACK报文;服务器向客户端发送FIN+ACK报文,客户端回复ACK报文。(客户端与服务器可互换)
    • (2)客户端和服务器都可以主动发起关闭。
    • 注:这些握手协议已经封装在 TCP 协议内部,socket 编程接口平时不用管

    3、基于 TCP 通信的服务模式

    • (1)具有公网 IP 地址的服务器(或者使用动态 IP 地址映射技术)。
    • (2)服务器端 socket、bind、listen、accept 后处于监听状态。
    • (3)客户端 socket 后,直接 connect 去发起连接。
    • (4)服务器收到申请并同意客户端接入后会建立 TCP 连接,然后双方开始收发数据。
    • (5)双方均可发起关闭连接。

    4、常见的使用来 TCP 协议的网络应用

    • (1)http、ftp
    • (2)QQ 服务器
    • (3)mail 服务器

    四、socket 编程接口介绍

    在这里插入图片描述
    1、建立连接

    • (1)socket 函数:建立一个套接口,类似于 open,用来打开一个网络连接,如果成功则返 回一个网络文件描述符(int),之后我们操作这个网络连接都通过这个网络文件描述符。
    • (2)bind 函数:将 socket 建立的套接口与一个本地地址捆绑(主机地址/端口号)。
    • (3)listen 函数:把一个未连接的套接字转换成一个被动套接字,指示内核应该接受指向该 套接字的连接请求。
    • (4)accept 函数:开始接收从客户端发来的请求信息。
    • (5)connect 函数:发起对服务器的连接请求,三次握手在此时开始。

    2、发送和接收

    • (1)send 和 write:都是用来发送信息。
    • (2)recv 和 read:都是用来接收信息。

    3、辅助性函数

    • (1) inet_aton(将字符串转换成网络地址)、inet_addr(构建网络地址)、inet_ntoa(将网络地址转换成字符串)。
    • (2)inet_ntop(将网络地址转换成字符串)、inet_pton(将字符串转换成网络地址)。两者都兼容 IPv4 和 IPv6。

    4、表示 IP 地址相关数据结构

    • (1)都定义在 netinet/in.h
    • (2)truct sockaddr:这个结构体是网络编程中用来表示一个 IP 地址的,注意这个 IP 地址 是 兼 容 IPv4 和 IPv6 的 , 在 实 际 编 程 中 这 个 结 构 体 会 被 struct sockaddr_in 或者 struct sockaddr_in6。
    • (3)typedef uint32_t in_addr_t:网络内部用来表示 IP 地址的类型。
    struct sockaddr_in{
    	short int sin_family;//地址族。一般来说是AF_INET和PF_INET
    	unsigned short int sin_port;//端口号(使用网络字节顺序)。在linux下,端口号的范围是0~65535,0~1024范围的端口号已经被系统使用或者保留
    	struct in_addr sin_addr;//存储IP地址,使用in_addr这个数据结构。
    	unsigned char sin_zero[8];//未来将sockaddr_in结构与sockaddr结构对齐
    };
    typedef uint32_t in_addr_t;
    struct in_addr{
    	in_addr_t s_addr;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    五、IP 地址转换函数实践

    1、inet_addr、inet_ntop、inet_pton:

    #include 
    #include 
    #include 
    #include 
    #define IPADDR	"192.168.1.103"
    //0x66  01  a8   c0
    //103   1   168  192
    //网络字节序,其实就是大端模式,低字节在高地址
    int main(void)
    {
    	//使用inet_addr函数
    	in_addr_t addr=0;
    	addr=inet_addr(IPADDR);//构建网络地址
    	printf("addr=0x%x.\n",addr);//0x6601a8c0
    	return 0;
    /*
    	//使用 inet_ntop 函数
    	struct in_addr addr={0};
    	char buf[50]={0};
    	addr.s_addr=0x6703a8c0;
    	inet_ntop(AF_INET,&addr,buf,sizeof(buf));//将网络地址转换成字符串
    	printf("ip addr = %s.\n", buf);
    	
    	//使用 inet_pton 函数
    	int ret=0;
    	struct in_addr addr={0};
    	ret=inet_pton(AF_INET,IPADDR,&addr);//将字符串转换成网络地址
    	if(ret!=1)	return -1;
    	printf("addr=0x%x.\n",addr.s_addr);
    */
    }
    
    • 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

    六、socket 编程实践 1

    1、服务器端程序编写

    • (1)socket。 (sockfd)创立套接字伪文件
    • (2)bind。 往套接字上面绑定port和ip(本身)
    • (3)listen。允许同时有多少客户端跟我建立连接
    • (4)accept:返回值是一个 fd(chilfd),accept 正确返回表示已经与客户端建立 TCP 连接,之后需 要通过这个连接对应的 fd 来和客户端进行读写操作。
    • 注意:socket 返回的 fd 叫监听 fd,是用来监听客户端的,不能读写;accept 返回的 fd 叫连 接 fd,用来读写。
    #include 
    #include 
    #include 
    #include 
    #include 
    #define	SERPORT	9003
    #define SERADDR	"127.0.0.1"//本地地址
    #define BACKLOG	100
    int main(void)
    {
    	int sockfd=-1,ret=-1,chilfd=-1;
    	socklen_t len=0;
    	struct sockaddr_in seraddr={0};
    	struct sockaddr_in cliaddr={0};
    
    	//第一步:先socket建立一个套接口,得到监听sockfd
    	sockfd=socket(AF_INET,SOCK_STREAM,0);
    	if(-1==sockfd){
    		perror(socket);exit(-1);
    	}
    	printf("socketfd = %d.\n", sockfd);
    
    	//第二步:bind绑定sockfd和当前电脑的ip地址&端口号
    	seraddr.sin_family=AF_INET;//设置地址族为IPV4
    	seraddr.sin_port=htons(SERPORT);//设置地址的端口号信息
    	seraddr.sin_addr.s_addr=inet_addr(SERADDR);//设置IP地址
    	ret=bind(sockfd,(const struct sockaddr*)&seraddr,sizeof(seraddr));
    	if(ret<0)	return -1;
    	printf("bind success.\n");
    
    	//第三步:listen监听端口
    	ret=listen(sockfd,BACKLOG);//阻塞等待客户端来连接服务器
    	if(ret<0){
    		perror("listen");exit(-1);
    	}
    	printf("listen success.\n");
    
    	//第四步:accept阻塞等待客户端接入
    	clifd=accept(sockfd,(struct sockaddr*)&cliaddr,&len);
    	printf("连接已经建立,client fd=%d.\n",clifd);
    	
    	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

    2、客户端程序的编写

    • (1)socket。
    • (2)connect。connect和bind参数一致,前者是对方的地址后者是自己的地址
    • 概念:端口号,实质就是一个数字编号,用来在一台主机的操作系统中唯一地标识一个能上网的进程。网络上传输的每一个数据包都包含了发送方和接收方的 IP 地址和端口号。
    #include 
    #include 
    #include 
    #include 
    #include 
    #define	SERADDR	"127.0.0.1"//服务器开放给我们的IP地址和端口号 
    #define	SERPORT	9003
    int main(void)
    {
    	int sockfd=-1,ret=-1;
    	struct sockaddr_in seraddr={0};	
    
    	//第一步:先socket建立一个套接口
    	sockfd=socket(AF_INET,SOCK_STREAM,0);
    	if(-1==sockfd){
    		perror("socket");return -1;
    	}
    	printf("socketfd=%d.\n",sockfd);
    
    	//第二步:connect连接服务器
    	seraddr.sin_family=AF_INET;//IPV4
    	seraddr.sin_port=htons(SERPORT);//端口号
    	seraddr.sin_addr.s_addr=inet_addr(SERADDR);//设置IP地址
    	ret=connect(sockfd,(const struct sockaddr*)&seraddr,sizeof(seraddr));
    	if(ret<0){
    		perror("listen");exit(-1);
    	}
    	printf("connect successfully.\n");
    	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

    七、socket 编程实践 2

    1、客户端发送&服务器接收

    //客户端反复发送
    while(1){
    	pritnf("输入发送的内容\n");
    	scanf("s%",sendbuf);
    	ret=send(sockfd,sendbuf,strlen(sendbuf),0);
    	printf("发送了%d个字符\n",ret);
    }
    //服务器反复接收
    while(1){
    	ret=recv(clifd,recvbuf,sizeof(recvbuf),0);
    	printf("client发送过来的内容是:%s.\n",recvbuf);
    	memset(recvbuf,0,sizeof(recvbuf));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2、服务器发送&客户端接收

    //服务器给客户端发
    strcpy(sendbuf,"hello world.");
    ret=send(clifd,sendbuf,strlen(sendbuf),0);
    printf("发送了%d个字符\n",ret);
    
    //客户端接收
    ret=recv(sockfd,recvbuf,sizeof(recvbuf),0);
    printf("server发送过来的内容是:%s\n",recvbuf);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3、探讨:如何让客户端和服务器好好沟通

    • (1)客户端和服务器原则上都可以任意收和发,但实际上双方必须配合
    • (2)必须了解到的一点:client 和 server 之间的通信是异步的,这就是问题的根源。
    • (3)解决方案:依靠应用层协议来解决,就是 server 和 client 事先做好一系列通信约定。

    八、socket 编程实践 3==important

    1、自定义应用层协议第一步:规定发送和接收方法。

    • (1)规定连接建立后由客户端主动向服务器发送一个请求数据包,服务器收到后回复一个 应答数据包,这就是一个通信回合。
    • (2)整个连接的通信就是由 N 多个回合组成的。
    //=======client.c文件
    while(1){
    	//回合中第一步:客户端给服务器发送信息
    	printf("请输入要发送的内容");
    	scanf("%s",sendbuf);
    	ret=send(sockfd,sendbuf,strlen(sendbuf),0);
    	printf("向server发送了%d个字符.\n",ret);
    	
    	//回合中第二步:客户端接收服务器的回复
    	memset(recvbuf,0,sizeof(recvbuf));
    	ret=recv(sockfd,recvbuf,sizeof(recvbuf)0);
    	printf("server发送过来的内容是:%s\n",recvbuf);
    	
    	//回合中第三步:客户端解析服务器的回复,再做下一步定夺
    }
    
    //=======server.c文件
    while(1){
    	//回合中第1步:服务器接收客户端消息
    	memset(recvbuf,0,sizeof(recvbuf));
    	ret=recv(clifd,recvbuf,sizeof(recvbuf),0);
    	printf("client发送过来的内容是:%s\n",recvbuf);
    
    	//回合中第二步:服务器回复客户端消息
    	printf("请输入要发送的内容\n");
    	scanf("%s",sendbuf);
    	ret=send(clifd,sendbuf,strlen(sendbuf),0);
    	printf("向 client 发送了%d 个字符\n", ret);
    
    	// 回合中第 3 步:服务器解析客户端的回复,再做下一步定夺
    }
    
    • 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

    2、自定义应用层协议第二步:定义数据包格式

    //=======client.c文件
    #define CMD_REGISTER 1001 // 注册学生信息 
    #define CMD_CHECK 1002 // 检验学生信息 
    #define CMD_GETINFO 1003 // 获取学生信息 
    #define STAT_OK 30 // 回复 ok 
    #define STAT_ERR 31 // 回复出错了
    
    typedef struct commu
    {
    	char name[20];//学生姓名
    	int age;// 学生年龄
    	int cmd;// 命令码
    	int stat;//状态信息,用来回复
    }info;
    while(1){
    	//回合中第一步:客户端给服务器发送信息
    	info st1;
    	printf("请输入学生姓名\n");
    	scanf("%s",&st1.name);
    	printf("请输入学生年龄");
    	scanf("%d",&st1.age);
    	st1.cmd=CMD_REGISTER;
    	ret=send(sockfd,&st1,sizeof(st1),0);
    	printf("发送了 1 个学生信息\n");
    	
    	//回合中第二步:客户端接收服务器信息
    	memset(&st1,0,sizeof(st1));
    	ret=recv(sockfd,&st1,sizeof(st1),0);
    	
    	//回合中第三步:客户端解析服务器的回复,再做下一步定夺
    	if(st1.stat==STAT_OK)
    		printf("注册学生信息成功\n");
    	else
    		printf("注册学生信息失败\n");	
    }
    
    //=======server.c文件
    #define CMD_REGISTER 1001 // 注册学生信息 
    #define CMD_CHECK 1002 // 检验学生信息 
    #define CMD_GETINFO 1003 // 获取学生信息 
    #define STAT_OK 30 // 回复 ok 
    #define STAT_ERR 31 // 回复出错了
    
    typedef struct commu
    {
    	char name[20];//学生姓名
    	int age;// 学生年龄
    	int cmd;// 命令码
    	int stat;//状态信息,用来回复
    }info;
    while1(1){
    	info st;
    	//回合第一步:服务器接收客户端信息
    	memset(recvbuf,0,sizeof(recvbuf));
    	ret=recv(clifd,&st,sizeof(st),0)
    	printf("client发送过来的内容是:%s\n",recvbuf);
    	
    	//回合第二步:服务器解析客户端数据包,然后干活
    	if(st.cmd==CMD_REGISTER){
    		printf("用户要注册学生信息.\n");
    		printf("学生姓名:%s,学生年龄:%d.\n",st.name,st.age);
    		/*此处服务器要进行真正的注册动作,一般是插入数据库一条信息
    		*************************************			
    		*************************************/
    		
    		//回合第三步:服务器发送客户端,回复
    		st.stat=STAT_OK;
    		ret = send(clifd, &st, sizeof(info), 0);
    		printf("注册完成\n");
    	}
    	if (st.cmd == CMD_CHECK) {}
    	if (st.cmd == CMD_GETINFO) {}
    }
    
    • 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

    3、常用应用层协议:http、ftp……

    九、TCP与UDP

    1 、TCP、UDP的区别

    • TCP—传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。
    • UDP—用户数据报协议,是一个简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据报发送出去,但是并不能保证它们能到达目的地。
    • 区别:
      • 1)TCP是面向连接的,UDP是面向无连接的
      • 2)UDP程序结构较简单
      • 3)TCP是面向字节流的,UDP是基于数据包的
      • 4)TCP保证数据正确性,UDP可能丢包
      • 5)TCP保证数据顺序到达,UDP不保证

    2 、TCP、UDP的优缺点

    • TCP优点:可靠稳定。TCP的可靠体现在TCP在传输数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完之后,还会断开来连接用来节约系统资源。TCP缺点:慢,效率低,占用系统资源高,易被攻击在传递数据之前要先建立连接,这会消耗时间,而且在数据传递时,确认机制、重传机制、拥塞机制等都会消耗大量时间,而且要在每台设备上维护所有的传输连接。然而,每个连接都会占用系统的CPU,内存等硬件资源。因为TCP有确认机制、三次握手机制,这些也导致TCP容易被利用,实现DOS、DDOS、CC等攻击。
    • UDP优点:快,比TCP稍安全UDP没有TCP拥有的各种机制,是一种无状态的传输协议,所以传输数据非常快,没有TCP的这些机制,被攻击利用的机会就少一些,但是也无法避免被攻击
    • UDP缺点:不可靠,不稳定因为没有TCP的这些机制,UDP在传输数据时,如果网络质量不好,就会很容易丢包,造成数据的缺失。

    3、TCP UDP适用场景

    • TCP:传输一些对信号完整性,信号质量有要求的信息。
    • UDP:对网络通讯质量要求不高时,要求网络通讯速度要快的场景。

    4、 TCP为什么是可靠连接?

    • 因为tcp传输的数据满足3大条件,不丢失,不重复,按顺序到达。

    5、OSI典型网络模型,简单说说有哪些
    在这里插入图片描述

    • ICMP协议是一个网络层协议。
      一个新搭建好的网络,往往需要先进行一个简单的测试,来验证网络是否畅通;但是IP协议并不提供可靠传输。如果丢包了,IP协议并不能通知传输层是否丢包以及丢包的原因。所以我们就需要一种协议来完成这样的功能–ICMP协议。
    • ARP:地址解析协议(Address Resolution Protocol)
      • 基本功能:知道目标设备的IP地址,查询目标设备的MAC地址,以保证通信的顺利进行。
      • 它是IPv4中网络层必不可少的协议,不过在IPv6中已不再适用,并被邻居发现协议(NDP)所替代RARP:是将MAC物理地址转换成IP地址。
  • 相关阅读:
    学习记忆——宫殿篇——记忆宫殿——数字编码——扑克牌记忆
    【解题报告】CF练一下题 | 难度CF2500左右
    《QT从基础到进阶·三十》QVariant的基础用法
    22.原型模式
    GitModel|Task04|随机模拟
    Day02-事件绑定
    最新Adobe2024全家桶下载,PS/PR/AE/AI/AU/LR/ID详细安装教程
    vue3父子组件传值,子组件暴漏方法
    强制删除文件?正确操作方法分享!
    macOS 查验国家税务总局发票
  • 原文地址:https://blog.csdn.net/weixin_47397155/article/details/126175603