2024/03/15(网络编程·day3)

一、思维导图

二、模拟面试题

什么是IP地址?

IP地址是主机在网络中的唯一标识。

分为IPv4和IPv6,

IPv4由4字节32位二进制数组成,通常使用点分十进制表示,例如192.168.117.85 ,其中每个十进制数的范围都在0-255.

IPv6由16字节128位二进制数组成,通常使用8组6进制的数字用冒号隔开表示。

IPv6主要是为了解决IPv4分配网络地址不足的问题。

IP地址和MAC地址的区别

1、IP地址是在网络中用来标识设备的,使得数据正确接收和发送,提供了逻辑地址,使得数据包在网络中能够正确的路由;MAC地址用于在局域网内唯一标识设备的物理地址,是网络适配器(网卡)的固定地址,用于局域网内数据帧的传输。

2、IP地址主要应用于网络层,负责数据包在互联网到局域网之间的路由;

MAC地址主要应用于数据链路层,是局域网内进行数据帧传输和识别接收者发送者的依据。

3、IP地址可以根据需求重新分配;

MAC地址通常在设备生产中分配,固定不可改变。

当电脑从一个网络切换到另一个网络时,哪个地址变,哪个地址不变?IP地址会改变,MAC地址不变。(具体如上述IP地址和MAC地址区别)
什么是端口号?

端口号本质上是一个两字节无符号整数,用于标识网络中特殊的服务或者进程,在网络通信中,每个通过网络传输的数据包都包含源端口号和目标端口号。

端口号的范围从0到65535,其中0到1023端口号被系统保留,用于一些知名的网络服务,如HTTP(80端口)、FTP(21端口)、SSH(22端口)、SMTP(25端口)等。1024到49151的端口号是注册端口,用于常见的应用程序或服务,49152到65535的端口号是动态或私有端口,用于临时分配给客户端程序。

TCP通信过程中服务器端实现流程

1、创建套接字:服务器端首先需要通过socket函数创建一个套接字文件用于接收客户端的连接请求。创建套接字时需要指定通信的协议族(如IPv4或IPv6)和通信类型(如TCP或UDP)

2、绑定地址和端口:服务器端需要通过系统函数 bind 将套接字绑定到一个特定的IP地址和端口号上,以便客户端能够通过这个地址和端口与服务器建立连接通信。

3、监听连接请求:服务器端通过调用 listen 函数开始监听来自客户端的连接请求。在监听状态下服务器可以接收客户端发送的连接请求并进行处理。

4、接收连接请求:当服务器接收到客户端的连接请求时,通过调用 accept 函数接受该连接请求,并创建一个新的套接字用于与客户端进行通信,通常称为连接套接字。

5、接收和发送数据:通过 accept 函数创建的连接套接字,服务器可以接收来自客户端的数据,同时服务器也可以通过连接套接字向客户端发送数据。(read/write  send/recv  sendto/recvfrom )

6、关闭连接

当通信完成或客户端主动断开连接时, 服务器端需要通过调用 close 函数关闭套接字文件,释放资源并终止与客户端的通信。

TCP通信的客户端流程

1、创建套接字:客户端首先需要通过socket函数创建一个套接字文件用于接收客户端的连接请求。创建套接字时需要指定通信的协议族(如IPv4或IPv6)和通信类型(如TCP或UDP)

2、绑定地址和端口:客户端需要知道要连接的服务器的IP地址和端口号,以便客户端能够通过这个地址和端口与服务器建立连接通信。

3、发起连接请求:客户端使用创建的套接字调用连接(connect)函数,指定服务器的地址和端口号,以发起连接请求。连接请求发送到服务器后,服务器会响应确认连接,建立客户端与服务器之间的通信通道。

4、接收和发送数据:一旦连接建立成功,客户端可以通过套接字进行数据的发送和接收。同时服务器也可以通过连接套接字向客户端发送数据(read/write  send/recv  sendto/recvfrom )

5、关闭连接:当客户端与服务器的通信结束时,需要关闭连接以释放资源。客户端可以调用关闭(close)函数来关闭套接字连接。

TCP通信和UDP通信的区别

1、连接or无连接:

TCP是面向连接的协议,通信前需要先建立连接,然后才能进行数据传输,确保数据的可靠性和顺序性。

UDP是无连接的协议,通信不需要事先建立连接,可以直接发送数据报,但不保证数据的可靠性和顺序性。

2、可靠性:

TCP提供可靠的数据传输,通过确认、重传、超时等机制来确保数据的可靠性,保证数据的完整性和顺序性。

UDP不提供数据传输的可靠性保证,数据报可能丢失、重复或乱序,需要应用层自行处理。

3、传输方式:

TCP提供面向流的传输,数据以字节流的形式进行传输,保证数据的顺序和完整性,适用于需要可靠传输的场景,如文件传输、网页浏览等。

UDP提供数据报传输,数据被封装成数据报并独立传输,不保证数据的顺序和完整性,适用于实时性要求高、容忍数据丢失的场景,如音频、视频传输、实时游戏等。

4、连接管理:

TCP通过三次握手建立连接,四次挥手释放连接,并维护连接状态信息,包括拥塞控制、流量控制等。

UDP不维护连接状态,每个数据报都是独立的,没有连接建立和关闭的过程。

5、效率:

TCP的可靠性和连接管理带来了一定的开销,包括握手延迟、拥塞控制、流量控制等,适用于对数据完整性和顺序性要求高的场景。

UDP的无连接特性和简单性使其开销较小,适用于实时性要求高、数据丢失可以容忍的场景。

TCP通信中的三次握手

1、客户端向服务器发送同步序列编号(SYN)的数据包,表示请求建立连接,并选择一个初始序列号(ISN)。

2、服务器收到客户端的SYN请求后,会发送一个带有SYN和ACK标志的数据包作为应答,确认客户端的SYN,并选择自己的初始序列号,并对客户端的初始序列号进行确认。

3、客户端收到服务器的确认后,会发送一个带有ACK标志的数据包作为确认,表示连接建立成功。


TCP通信中的四次挥手

1、客户端已经发送完所有数据,想要关闭连接,于是发送一个带有FIN标志的数据包给服务器,表示不再发送数据。

2、服务器收到客户端的FIN后,会发送一个带有ACK标志的数据包作为确认,表示已收到关闭请求,但仍可以发送数据。

3、服务器发送完所有数据后,也想要关闭连接,于是发送一个带有FIN标志的数据包给客户端,表示不再发送数据。

4、客户端收到服务器的FIN后,会发送一个带有ACK标志的数据包作为确认,表示已收到关闭请求,连接终止。

UDP中是否可以使用connect函数进行连接

1、UDP通信中可以使用connect函数,将服务器与某个客户端建立一个唯一通道。

2、优点:传输效率高,稳定性高,数据干扰较小。

3、在服务器端使用connect与某个特定的客户端建立连接后,服务器就不再接收其他客户端的消息了。

4、如果想要断开,需要再使用一次connect函数,但是需要将地址信息结构体中的sin_addr改成AF_UNSPEC。

5、在UDP中可以多次使用connect函数与其他客户端建立连接,但是在TCP中只能进行一次连接。

6、当UDP中使用了connect与某个特定的客户端建立连接后,就可以正常使用read/write、send/recv函数完成通信。

三、

1、tcpforkSer

#include<myhead.h>

#define SER_PORT 8888 	//服务器端口号
#define SER_IP "192.168.241.128" 	//服务器IP

//定义信号处理函数
void handler(int signo)
{
	if(signo == SIGCHLD)
	{
		//以非阻塞的形式回收僵尸进程
		while(waitpid(-1,NULL,WNOHANG) > 0);
	}
}

/******************主程序*********************/
int main(int argc, const char *argv[])
{
	//将子进程退出时发射的SIGCHLD信号绑定到信号处理函数中
	if(signal(SIGCHLD,handler) == SIG_ERR)
	{
		perror("signal error");
		return -1;
	}

	//1、创建一个套接字
	int sfd = -1;
	sfd = socket(AF_INET,SOCK_STREAM,0);
	//参数1:表示创建的是网络通信的套接字
	//参数2:表示使用的是TCP通信协议
	//参数3:参数2指定了协议,参数3填0即可
	if(sfd == -1)
	{
		perror("socket error");
		return -1;
	}
	printf("%d success sfd = %d\n",__LINE__,sfd);  //3

	//设置端口号快速重用
	int reuse = 1;
	if(setsockopt(sfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) == -1)
	{
		perror("setsockopt error");
		return -1;
	}
	printf("端口号快速重用成功\n");

	//2、绑定IP和端口号
	//2.1填充地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family = AF_INET; 	//地址族
	sin.sin_port = htons(SER_PORT); 	//端口号
	sin.sin_addr.s_addr = inet_addr(SER_IP); 	//IP地址

	//2.2绑定
	if(bind(sfd,(struct sockaddr *)&sin,sizeof(sin))==-1)
	{
		perror("bind error");
		return -1;
	}
	printf("%d bind success\n",__LINE__);

	//3、将套接字设置成被监听状态
	if(listen(sfd,128) == -1)
	{
		perror("listen error");
		return -1;
	}

	//4、阻塞等待客户端的连接请求
	int newfd = -1;
	//定义结构体变量接受客户端地址信息结构体
	struct sockaddr_in cin;//接收客户端信息结构体
	socklen_t addrlen = sizeof(cin);//接收客户端结构体大小

	//定义进程号变量
	pid_t pid = -1;

	while(1)
	{
		//父进程执行连接操作
		if((newfd = accept(sfd,(struct sockaddr*)&cin,&addrlen)) == -1)
		{
			perror("accept error");
			return -1;
		}
		printf("[%s  %d]请求连接 newfd = %d\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),newfd);

		//创建子进程
		pid = fork();
		if(pid > 0)
		{
			//关闭与客户端通信的套接字
			close(newfd);
		}
		else if(pid == 0)
		{
			//关闭sfd
			close(sfd);

			//5、收发数据
			char rbuf[128] = "";//接收客户端发送的信息
			while(1)
			{
				//将容器清空
				bzero(rbuf,sizeof(rbuf));//memset(rbuf,0,sizeof(rbuf));

				//从套接字中读取数据
				int res = recv(newfd,rbuf,sizeof(rbuf)-1,0);
				if(res == 0)
				{
					printf("客户端已下线\n");
					break;
				}
				printf("[%s  %d]:%s\n",inet_ntoa(cin.sin_addr),ntohs(cin.sin_port),rbuf);

				//加个收到再回回去
				strcat(rbuf," <Got it>!");

				send(newfd,rbuf,strlen(rbuf),0);
				printf("回复成功\n");		 

			}
			//关闭与客户端通信的套接字
			close(newfd);

			//退出子进程
			exit(EXIT_SUCCESS);
		}

	}
	//6、关闭服务器
	close(sfd);


	return 0;
}

2、udpSer

#include<myhead.h>
#define SER_PORT 6666
#define SER_IP "192.168.117.103"
#define CLI_PORT 7777
#define CLI_IP "192.168.117.103"
int main(int argc, const char *argv[])
{
	//1、创建用于通信的套接字文件描述符
	int cfd = socket(AF_INET,SOCK_DGRAM,0);
	if(cfd == -1)
	{
		perror("socket error");
		return -1;
	}
	printf("socket success cfd = %d\n",cfd);

	//2、绑定IP地址和端口号
	//2.1填充客户端地址信息结构体
	struct sockaddr_in cin;
	cin.sin_family = AF_INET;
	cin.sin_port = htons(CLI_PORT);
	cin.sin_addr.s_addr = inet_addr(CLI_IP);
	//2.2、绑定
	if(bind(cfd,(struct sockaddr*)&cin,sizeof(cin)) == -1)
	{
		perror("bind error");
		return -1;
	}
	printf("bind success\n");

	//3、数据收发
	char wbuf[128] = "";
	//填充服务器端地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(SER_PORT);
	sin.sin_addr.s_addr = inet_addr(SER_IP);

	while(1)
	{
		printf("请输入>>>");
		fgets(wbuf,sizeof(wbuf),stdin); //从终端获取一个字符串
		wbuf[strlen(wbuf)-1] = 0; //将换行换成'\0'

		//判断输入的是否为退出
		if(strcmp(wbuf,"quit") ==0)
		{
			break;
		}

		//将数据发送给服务器
		sendto(cfd,wbuf,strlen(wbuf),0,(struct sockaddr*)&sin,sizeof(sin));
		printf("发送成功\n");

		//接收服务器发送来的消息
		bzero(wbuf,sizeof(wbuf)); //清零

		read(cfd,wbuf,sizeof(wbuf)-1);
		printf("收到服务器的消息为:%s\n",wbuf);

	}

	//4、关闭套接字
	close(cfd);

	return 0;
}

四、项目

1、TCP机械臂测试

通过w(红色臂角度增大)s(红色臂角度减小)d(蓝色臂角度增大)a(蓝色臂角度减小)按键控制机械臂

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

1)基于TCP服务器的机械臂,端口号是8888, ip是Windows的ip;

查看Windows的IP:按住Windows+r 按键,输入cmd , 输入ipconfig

2)点击软件中的开启监听;

3)机械臂需要发送16进制数,共5个字节,协议如下

0xff 0x02 x y 0xff       0xff:起始结束协议,固定的; 0x02:控制机械手臂协议,固定的; x:指定要操作的机械臂 0x00 红色摆臂 0x01 蓝色摆臂 y:指定角度

#include<myhead.h>
#define SER_PORT 8888
#define SER_IP "192.168.117.85"
#define CLI_PORT 9999
#define CLI_IP "192.168.241.129"

int main(int argc, const char *argv[])
{
	//1、创建用于连接的客户端套接字
	int cfd = socket(AF_INET,SOCK_STREAM,0);
	if(cfd == -1)
	{
		perror("socket error");
		return -1;
	}
	printf("socket sucess cfd = %d\n",cfd); //3

	//设置端口号快速重用
	int reuse = 1;
	if(setsockopt(cfd,SOL_SOCKET,SO_REUSEADDR,&reuse,sizeof(reuse)) == -1)
	{
		perror("socket error");
		return -1;
	}
	printf("端口号快速重用成功\n");
	 
	//2、绑定端口号和IP地址(非必须)
	//2.1填充客户端地址信息结构体
	struct sockaddr_in cin;
	cin.sin_family = AF_INET;
	cin.sin_port = htons(CLI_PORT);
	cin.sin_addr.s_addr = inet_addr(CLI_IP);

	//2.2、绑定端口号和IP
	if(bind(cfd,(struct sockaddr*)&cin,sizeof(cin)) == -1)
	{
		perror("bind error");
		return -1;
	}
	printf("bind success\n");

	//3、连接服务器
	//3.1、填充要连接服务器的地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family = AF_INET; 	//地址族
	sin.sin_port = htons(SER_PORT); 	//服务器端口号
	sin.sin_addr.s_addr = inet_addr(SER_IP); //服务器IP地址

	//3.2、连接服务器
	if(connect(cfd,(struct sockaddr*)&sin,sizeof(sin)) == -1)
	{
		perror("connect error");
		return -1;
	}
	printf("连接成功\n");

	//定义控制机械臂的容器
	char buf_red[5] = {0xff,0x02,0x00,0x00,0xff}; //红色臂容器
	unsigned char buf_bule[5] = {0xff,0x02,0x01,0x00,0xff}; //蓝色臂容器
	
	//将数据发送给服务器初始化
	send(cfd,buf_red,sizeof(buf_red),0);
	sleep(1); 	//防止沾包
	send(cfd,buf_bule,sizeof(buf_bule),0);

	//定义一个读取数据的结构体变量
	struct input_event ie;
	//打开 /dev/input/event1文件
	int fd = open("/dev/input/event1",O_RDONLY);
	if(fd == -1)
	{
		perror("open error");
		return -1;
	}

	//收发数据
	char ctrl = 0; //用户输入的控制键
	while(1)
	{
	//	scanf("%c",&ctrl); //用户输入键
	//	getchar();

		//从文件中读取当前文件中的数据
		read(fd,&ie,sizeof(ie));

		//输出从键盘事件文件中读取的数据
		//printf("ie.type = %d,ie.code = %d,ie.value =%d\n",ie.type,ie.code,ie.value);


		//对输入字符进行多分支选择
		switch(ie.code*ie.value)
		{
		case 17:
			{
				//红色臂角度增大
				buf_red[3] +=2;
				if(buf_red[3] >= 90)
				{
					buf_red[3] = 90;
				}
				//将数据发送给服务器
				send(cfd,buf_red,sizeof(buf_red),0);
			}
			break;
		//红色臂角度减小
		case 31:
			{
				//红色臂角度减小
				buf_red[3] -=2;
				if(buf_red[3] <= -90)
				{
					buf_red[3] = -90;
				}
				//将数据发送给服务器
				send(cfd,buf_red,sizeof(buf_red),0);
			}
			break;
		case 30:
			{
				//蓝色臂角度增大
				buf_bule[3] +=2;
				if(buf_bule[3] >= 180)
				{
					buf_bule[3] =180;
				}
				//将数据发送给服务器
				send(cfd,buf_bule,sizeof(buf_bule),0);
			}
			break;
		case 32:
			{
				//蓝色臂角度减小
				buf_bule[3] -=2;
				if(buf_bule[3] <= 0)
				{
					buf_bule[3] = 0;
				}
				//将数据发送给服务器
				send(cfd,buf_bule,sizeof(buf_bule),0);
			}
			break;
		case 16:goto END;

		}

	}
END:

	//关闭套接字文件
	close(cfd);

	return 0;
}

2、基于UDP的TFTP文件传输

1)tftp协议概述

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

特点:

是应用层协议

基于UDP协议实现

数据传输模式

octet:二进制模式(常用)

mail:已经不再支持

2)tftp下载模型

TFTP通信过程总结

  1. 服务器在69号端口等待客户端的请求
  2. 服务器若批准此请求,则使用 临时端口 与客户端进行通信。
  3. 每个数据包的编号都有变化(从1开始)
  4. 每个数据包都要得到ACK的确认,如果出现超时,则需要重新发送最后的数据包或ACK包
  5. 数据长度以512Byte传输的,小于512Byte的数据意味着数据传输结束。

3)tftp协议分析

差错码:

0 未定义,差错错误信息

1 File not found.

2 Access violation.

3 Disk full or allocation exceeded.

4 illegal TFTP operation.

5 Unknown transfer ID.

6 File already exists.

7 No such user.

8 Unsupported option(s) requested.

  • 38
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值