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

2024-03-12 1073阅读

温馨提示:这篇文章已超过374天没有更新,请注意相关的内容是否还可用!

TCP通信:

    1. TCP发端:

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

    2. TCP收端:

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

    3. TCP需要用到的函数:

        1. connect:

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

              功能:发送链接请求

              参数:

                  sockfd:套接字文件描述符

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

                  addrlen:IP地址的大小

              返回值:

                   成功返回0

                   失败返回-1

        2. send:

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

              功能:发送数据

              参数:

                  sockfd:文件描述符

                  buf:发送数据空间首地址

                  flags:属性默认为0

              返回值:

                  成功返回实际发送字节数

                  失败返回-1

        3. recv:

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

                功能:接收数据

                参数:

                    sockfd:套接字文件描述符

                    buf:存放数据空间首地址

                    len:最大接收数据的长度

                    flags:属性,默认为0

                返回值:

                    成功返回实际接收字节数

                    失败返回-1 

                    如果对方退出,返回0 

        4. listen:

int listen(int sockfd, int backlog);

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

                参数:

                    sockfd:套接字文件描述符

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

                返回值:

                    成功返回0 

                    失败返回-1 

        5. accept:

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

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

                参数:

                    socket:套接字文件描述符

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

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

                返回值:

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

                    失败返回-1

        4. TCP编程练习:

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

send.c

#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

#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   域名解析服务器  ->  IP地址 

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

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

    2. HTTP交互过程:

        1. 建立TCP连接

        2. 发送HTTP请求报文

        3. 回复HTTP请求报文

        4. 关闭TCP连接

VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]