嵌入式学习第二十六天!(网络传输:TCP编程、HTTP协议)

TCP通信:

1. TCP发端:

socket -> connect -> send -> recv -> close

2. TCP收端:

socket -> bind -> listen -> accept -> recv -> send -> close

3. TCP需要用到的函数:

1. connect:

cpp 复制代码
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

**功能:**发送链接请求

参数:

**sockfd:**套接字文件描述符

**addr:**目的地址存放空间首地址

**addrlen:**IP地址的大小

返回值:

成功返回0

失败返回-1

2. send:

cpp 复制代码
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

**功能:**发送数据

参数:

**sockfd:**文件描述符

**buf:**发送数据空间首地址

**flags:**属性默认为0

返回值:

成功返回实际发送字节数

失败返回-1

3. recv:

cpp 复制代码
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

**功能:**接收数据

参数:

**sockfd:**套接字文件描述符

**buf:**存放数据空间首地址

**len:**最大接收数据的长度

**flags:**属性,默认为0

返回值:

成功返回实际接收字节数

失败返回-1

如果对方退出,返回0

4. listen:

cpp 复制代码
int listen(int sockfd, int backlog);

**功能:**监听客户端发送的连接请求(该函数不会阻塞)

参数:

**sockfd:**套接字文件描述符

**backlog:**允许等待的尚未被处理的三次握手请求的最大个数

返回值:

成功返回0

失败返回-1

5. accept:

cpp 复制代码
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

**功能:**处理等待连接队列中的第一个连接请求,该函数具有阻塞功能(如果没有人发送链接请求,会阻塞等待)

参数:

**socket:**套接字文件描述符

**address:**存放IP地址的空间首地址

**addrlen:**存放IP地址大小空间首地址

返回值:

成功返回一个新的文件描述符

失败返回-1

4. TCP编程练习:

  1. 利用TCP实现跨主机的文件发送:

send.c

cpp 复制代码
#include "head.h"

int main(void)
{
	FILE *fd = NULL;
	int sockfd = 0;
	int ret = 0;
	struct sockaddr_in severaddr;
	ssize_t nsize = 0;
	size_t rret = 0;
	char filename[100] = {0};
	char tmpbuff[1024] = {0};

	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd == -1)
	{
		perror("fail to socket");
		return -1;
	}
	
	severaddr.sin_family = AF_INET;
	severaddr.sin_port = htons(50000);
	severaddr.sin_addr.s_addr = inet_addr("192.168.1.162");
	ret = connect(sockfd,(struct sockaddr *)&severaddr, sizeof(severaddr));
	if(ret == -1)
	{
		perror("fail to connect");
		return -1;
	}
	
	scanf("%s", filename);
	fd = fopen(filename, "r");
	if(fd == NULL)
	{
		perror("fail to fopen");
		return -1;
	}

	nsize = send(sockfd, filename, strlen(filename), 0);
	if(nsize == -1)
	{
		perror("fail to send");
		return -1;
	}

	while(1)
	{
		memset(tmpbuff, 0, sizeof(tmpbuff));
		rret = fread(tmpbuff, 1, sizeof(tmpbuff), fd);
		if(rret == 0)
		{
			break;
		}
		usleep(1000);
		nsize = send(sockfd, tmpbuff, rret, 0);
		if(nsize == -1)
		{
			perror("fail to send");
			return -1;
		}
		
	}
	
	fclose(fd);
	close(sockfd);

	return 0;

}

在这里,nsize = send(sockfd, tmpbuff, rret, 0);中,必须使用rret作为发送的字节大小,不能用strlen(tmpbuff),因为如果是二进制文件,不是ASCII码文件,\0可能成为发送的内容,所以在这里rret作为读到的字节数,可以直接作为send的输入。

其中在进行接收数据的时候,usleep(1000),是防止在TCP传输时出现的粘包现象,如果没有这段代码,那么接收端,接收的文件就会出现:内容和名字共同作为接收方所收到文件的文件名。为了防止这种粘包的情况,我们还可以在传输数据的时候,给数据加上帧头和帧尾,将文件名和数据内容分隔开。

recv.c

cpp 复制代码
#include "head.h"

int main(void)
{
	FILE *fd = NULL;
	int ret = 0;
	int sockfd = 0;
	int contfd = 0;
	ssize_t nsize = 0;
	char *ptmp = NULL;
	char tmpbuff[1024] = {0};
	struct sockaddr_in serveraddr;

	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(50000);
	serveraddr.sin_addr.s_addr = INADDR_ANY;

	sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if(sockfd == -1)
	{
		perror("fail to socket");
		return -1;
	}


	ret = bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
	if(ret == -1)
	{
		perror("fail to ret");
		return -1;
	}
	
	ret = listen(sockfd, 10);
	if(ret == -1)
	{
		perror("fail to listen");
		return -1;
	}

	contfd = accept(sockfd, NULL, NULL);
	if(contfd == -1)
	{
		perror("fail to accept");
		return -1;
	}

	nsize = recv(contfd, tmpbuff, sizeof(tmpbuff), 0);
	if(nsize == -1)
	{
		perror("fail to recv");
		return -1;
	}

	fd = fopen(tmpbuff, "w");
	if(fd == NULL)
	{
		perror("fail to fopen");
		return -1;
	}

	while(1)
	{
		memset(tmpbuff, 0, sizeof(tmpbuff));
		nsize = recv(contfd, tmpbuff, sizeof(tmpbuff), 0);
		if(nsize <= 0)
		{
			break;
		}
		fwrite(tmpbuff, 1, nsize, fd);
	}

	fclose(fd);
	close(contfd);
	close(sockfd);

	return 0;
}

5. TCP包头:

**1. 序号:**发送端发送数据的编号

**2. 确认号:**已经确认接收到的数据的编号(只有当ACK为1时,确认好才有用)

**3. 第四行为首部长度:**数据前面的称之为头部,6位标记位,每一个标记位占一位

URG: 紧急标志位 ACK: 确认标记位 **RST:**断开连接标记位

**SYN:**请求标记位,置1建立连接的过程

**FIN:**结束标记位,置1释放连接的过程

**PSH:**若置为1这一数据段不在缓存区里等待,直接优先处理

**4. 校验和:**目的是保证数据完整性

5. 注意: 不要将确认序号Ack标志位ACK搞混了,确认方Ack=发起方Seq+1,两端配对

6. TCP为什么安全可靠:

  1. 在通信前建立三次握手连接:SYN、SYN+ACK、ACK

  2. 在通信过程中通过序列号和确认号保障数据传输的完整性

本次发送序列号:上次收到的确认号

本次发送确认号:上次接收到的序列号+实际接收的数据长度

  1. 在通信结束时使用四次挥手连接保障数据传输的完整性

7. UDP和TCP的区别:

  1. UDP和TCP都是传输层的协议

  2. UDP实现机制简单,资源开销小,不安全不可靠

  3. TCP实现机制复杂,资源开销大,安全可靠

  4. UDP是无连接、TCP有连接、UDP是以数据包形式传输、TCP是以流的方式传输

HTTP协议:

1.URL:

代表着是统一资源定位符,每个有效的 URL 都指向一个唯一的资源。这个资源可以是一个 HTML 页面等等。

<协议>://<主机>:<端口>/<路径>

**协议 :**HTTP :80 TCP

**协议 :**HTTPS :443 TCP

**主机:**域名 -> 域名解析服务器 -> IP地址

**端口:**可以省,HTTP 80, HTTPS 443

**路径:**想要获得对应的资源

2. HTTP交互过程:

  1. 建立TCP连接

  2. 发送HTTP请求报文

  3. 回复HTTP请求报文

  4. 关闭TCP连接

相关推荐
白帽黑客沐瑶2 天前
【网络安全就业】信息安全专业的就业前景(非常详细)零基础入门到精通,收藏这篇就够了
网络·安全·web安全·计算机·程序员·编程·网络安全就业
树码小子2 天前
Java网络编程:(socket API编程:TCP协议的 socket API -- 回显程序的服务器端程序的编写)
java·网络·tcp/ip
2303_Alpha2 天前
SpringBoot
笔记·学习
萘柰奈2 天前
Unity学习----【进阶】TextMeshPro学习(三)--进阶知识点(TMP基础设置,材质球相关,两个辅助工具类)
学习·unity
沐矢羽2 天前
Tomcat PUT方法任意写文件漏洞学习
学习·tomcat
好奇龙猫2 天前
日语学习-日语知识点小记-进阶-JLPT-N1阶段蓝宝书,共120语法(10):91-100语法+考え方13
学习
向阳花开_miemie2 天前
Android音频学习(十八)——混音流程
学习·音视频
工大一只猿2 天前
51单片机学习
嵌入式硬件·学习·51单片机
绿箭柠檬茶2 天前
Ubuntu 服务器配置转发网络访问
服务器·网络·ubuntu
c0d1ng2 天前
量子计算学习(第十四周周报)
学习·量子计算