嵌入式学习DAY30 --- 文件的上传、Udp的服务器和客户端、IO模型_嵌入式 c 语言 上传文件(1)

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

	return -1;
}
printf("socket ok!\n");
//请求连接
struct sockaddr_in servAddr = {0};
servAddr.sin_family = PF_INET;
servAddr.sin_port = htons(8888);
servAddr.sin_addr.s_addr = inet\_addr("192.168.0.150");
int ret = connect(sockFd, (struct sockaddr \*)&servAddr, sizeof(servAddr));
if(ret < 0)
{
	perror("connect error!");
	close(sockFd);
	return -1;
}
printf("connect ok!\n");

char fileName[20] = {0};
int fileSize = 0;
char fileHead[50] = {0};
char buf[1024] = {0};
//选择要上传的文件
printf("请输入要上传的文件名;");
gets(fileName);

struct stat stBuf = {0};
//获得文件头
stat(fileName, &stBuf);
fileSize = stBuf.st_size;

sprintf(fileHead, "%s#%d", fileName, fileSize);

//发送文件头
ret = send(sockFd, fileHead, sizeof(fileHead), 0);
if(ret < 0)
{
	perror("send error!");
	close(sockFd);
	return -1;
}

//以只读方式打开文件
int fd = open(fileName, O_RDONLY);
if(fd < 0)
{
	perror("open error!");
	close(sockFd);
	return -1;
}
//循环读文件,并将读到的内容发送给服务器
while(1)
{
	memset(buf, 0, sizeof(buf));
	ret = read(fd, buf, sizeof(buf));
	if(ret < 0)
	{
		perror("read error!");
		close(sockFd);
		close(fd);
		return -1;
	}
	else if(0 == ret)
	{
		break;
	}
	//读多少,发多少
	int retSend = send(sockFd, buf, ret, 0);
	if(retSend < 0)
	{
		perror("send error!");
		close(sockFd);
		close(fd);
		return -1;
	}
}
//关闭文件
close(fd);
//关闭套接字
close(sockFd);
return 0;

}


##### 2> tcpserver



> 
> 2.>tcpserver-----------------------------------------------------------代码如下所示:
> 
> 
> 



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

void getFileInfo(char *fileHead, char *fileName, int *pFileSize)
{
int i = 0;
while(fileHead[i] != ‘#’)
{
fileName[i] = fileHead[i];
i++;
}

fileName[i] = '\0';
i++;
char buf[20] = {0};
strcpy(buf, &fileHead[i]);

\*pFileSize = atoi(buf);

}

int main()
{
//创建套接字
int sockFd = socket(PF_INET, SOCK_STREAM, 0);
if(sockFd < 0)
{
perror(“socket error!”);
return -1;
}
printf(“socket ok!\n”);
//绑定地址信息(ip+port)
struct sockaddr_in servAddr = {0};
servAddr.sin_family = PF_INET;
servAddr.sin_port = htons(8888);
servAddr.sin_addr.s_addr = inet_addr(“192.168.0.150”);
int ret = bind(sockFd, (struct sockaddr *)&servAddr, sizeof(servAddr));
if(ret < 0)
{
perror(“bind error!”);
close(sockFd);
return -1;
}
printf(“bind ok!\n”);
//监听
//将主动的套接字变成被动等待连接的套接字
ret = listen(sockFd, 1);
if(ret < 0)
{
perror(“listen error!”);
close(sockFd);
return -1;
}
printf(“listening\n”);
//建立连接
int connFd = accept(sockFd, NULL, NULL);
if(connFd < 0)
{
perror(“accept error!”);
close(sockFd);
return -1;
}
printf(“accept ok!\n”);

char fileHead[50] = {0};
char fileName[20] = {0};
int fileSize = 0;
char buf[1024] = {0};

//接收文件头
ret = recv(connFd, fileHead, sizeof(fileHead), 0);
if(ret < 0)
{
	perror("recv error!");
	close(sockFd);
	close(connFd);
	return -1;
}

//解析文件头
getFileInfo(fileHead, fileName, &fileSize);

printf("fileName = %s, fileSize = %d\n", fileName, fileSize);
//以只写的方式创建并打开文件
int fd = open(fileName, O_WRONLY | O_CREAT, 0666);
if(fd < 0)
{
	perror("open error!");
	close(sockFd);
	close(connFd);
	return -1;
}
//按照文件大小循环接收文件内容并将接收到的内容写入文件
while(fileSize > 0)
{
	memset(buf, 0, sizeof(buf));
	ret = recv(connFd, buf, sizeof(buf), 0);
	if(ret < 0)
	{
		perror("recv error!");
		close(sockFd);
		close(connFd);
		close(fd);
		return -1;
	}
	int wrRet = write(fd, buf, ret);
	if(wrRet < 0)
	{
		perror("write error!");
		close(sockFd);
		close(connFd);
		close(fd);
		return -1;
	}
	fileSize -= ret;
}
//关闭文件
close(fd);
//关闭套接字
close(sockFd);
close(connFd);
return 0;

}




---


### 二、Udp的服务器和客户端


#### 1、Udp的服务器:


1、创建套接字 socket  
 2、绑定IP地址和端口号 bind  
 3、接收 recvfrom  
 4、发送 sendto  
 5、关闭套接字 close


#### 2、Udp客户端:


1、创建套接字 socket  
 2、发送 sendto  
 3、接收 recvfrom  
 关闭套接字 close


#### 3、sendto(),recvfrom()


ssize\_ t sendto(int socket, void \*message, size\_ t length, intflags, struct sockaddr \*dest\_ addr, socklen *t dest* len);


ssize\_ t recvfrom(int socket, void \*buffer, size\_ *t length, intflags, struct sockaddr \*address, socklen* t \*address\_ len);


这两个函数一般在使用UDP协议时使用


#### 4、代码展示:


##### 1> udpclient



> 
> 1.>udpclient-----------------------------------------------------------代码如下所示:
> 
> 
> 



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

int main(int argc, char **argv)
{
if(argc < 2)
{
printf(“请输入ip\n”);
return -1;
}
//创建套接字
int sockFd = socket(PF_INET, SOCK_DGRAM, 0);
if(sockFd < 0)
{
perror(“socket error!”);
return -1;
}
printf(“socket ok!\n”);
//发送消息
char buf[1024] = {0};

struct sockaddr_in servAddr = {0};
servAddr.sin_family = PF_INET;
servAddr.sin_port = htons(9999);
servAddr.sin_addr.s_addr = inet\_addr(argv[1]);

while(1)
{
	memset(buf, 0, sizeof(buf));
	printf("请输入:");
	gets(buf);
	if(0 == strcmp(buf, "quit"))
	{
		break;
	}
	int ret = sendto(sockFd, buf, sizeof(buf), 0, (struct sockaddr \*)&servAddr, sizeof(servAddr));
	if(ret < 0)
	{
		perror("sendto error!");
		close(sockFd);
		return -1;
	}

	memset(buf, 0, sizeof(buf));
	ret = recvfrom(sockFd, buf, sizeof(buf), 0, NULL, NULL);
	if(ret < 0)
	{
		perror("recvfrom error!");
		close(sockFd);
		return -1;
	}
	printf("recv:%s\n", buf);
}
//关闭套接字
close(sockFd);
return 0;

}


##### 2> udpserver



> 
> 2.>udpserver-----------------------------------------------------------代码如下所示:
> 
> 
> 



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

int main(int argc, char **argv)
{
if(argc < 2)
{
printf(“请输入ip地址!\n”);
return -1;
}
//创建套接字
int sockFd = socket(PF_INET, SOCK_DGRAM, 0);
if(sockFd < 0)
{
perror(“socket error!”);
return -1;
}
printf(“socket ok!\n”);
//绑定地址信息
struct sockaddr_in servAddr = {0};
servAddr.sin_family = PF_INET;
servAddr.sin_port = htons(9999);
servAddr.sin_addr.s_addr = inet_addr(argv[1]);

int ret = bind(sockFd, (struct sockaddr \*)&servAddr, sizeof(servAddr));
if(ret < 0)
{
	perror("bind error!");
	close(sockFd);
	return -1;
}
printf("bind ok!\n");
//接收消息
char buf[1024] = {0};
struct sockaddr_in cliAddr = {0};
socklen_t len = sizeof(cliAddr); //必须赋值
while(1)
{
	memset(buf, 0, sizeof(buf));
	ret = recvfrom(sockFd, buf, sizeof(buf), 0,(struct sockaddr \*)&cliAddr, &len);
	if(ret < 0)
	{
		perror("recvfrom error!");
		close(sockFd);
		return -1;
	}
	printf("recv:%s\n", buf);

	memset(buf, 0, sizeof(buf));
	printf("input:");
	gets(buf);
	ret = sendto(sockFd, buf,sizeof(buf), 0, (struct sockaddr \*)&cliAddr, len);
    if(ret < 0)
	{
		perror("sendto error!");
		close(sockFd);
		return -1;
	}
}
//关闭套接字
close(sockFd);
return 0;

}




---


### 三、IO模型


#### 1、在UNIX/Linux下主要有4种I/O模型:


阻塞I/O:  
 最常用、最简单、效率最低


非阻塞I/O:  
 可防止进程阻塞在I/O操作上,需要轮询


I/O多路复用:  
 允许同时对多个I/O进行控制


信号驱动I/O:  
 一种异步通信模型


目标:理解并掌握IO多路复用的原理(非常重要)  
 IO多路:同时有多个输入输出  
 复用:一个东西可以被用作多个功能,但是同一时刻只能用其中的一个功能


IO多路复用:可以处理多路IO,有IO操作需要,就操作哪路IO



Int main()
{
Gets(buf); //等待用户从终端输入
Accept(); //等待客户端连接
}


上面的代码面临的问题是有客户端来连接,但是终端没有输入,所以就会一直阻塞在gets,导致不能和客户端连接成功,有问题


如何解决这个问题?



Int main()
{
//一般select在阻塞,但是只要有客户端来连接或者终端有输入,select就会返回
Select()
If(终端输入)
{
Gets(buf); //等待用户从终端输入
}
Else if(客户端来连接了)
{
Accept(); //等待客户端连接
}
}


#### 2、Select的工作原理:


1、创建一个文件描述符的集合,这个集合的大小是1024个位,128个字节



Typedef Struct
{
Long long arr[16];
} fd_set;


1024个位:因为有1024个文件描述符,一个位对应一个文件描述符,刚好一一对应  
 也就是是第几位就是哪个文件描述符


2、将集合清空  
 1024个位全都是0 00000000000000000000000000000000000000


3、将要操作的文件描述符加入集合  
 将0和sockFd 3加入集合  
 100100000000000000000000000000000


4、将集合交给select  
 Select是一个系统调用函数,将这个从用户空间先拷贝到内核空间,内核会去对集合进行轮询(遍历的次数最大的文件描述符的值+1次),如果0和3都没有任何反应(没有终端输入也没有客户端来连接),那么集合还是保持不变,而且select阻塞  
 但是只要0和3其中有一个有反应了,比如终端有输入了,或者有客户端来连接了,那么内核会将其中没有反应的文件描述符的位清0,有反应的文件描述符保持不变,select返回,并且将修改后的集合从内核空间拷贝给用户空间  
 所以,如果是0准备好了,此时用户空间得到的集合就是:  
 100000000000000000000000000000000


5、遍历集合,看哪一位还是1((遍历的次数最大的文件描述符的值+1次))


6、看为1的这一位到底是谁,然后进行对应的IO操作


为了设置文件描述符我们要使用几个宏:



> 


![img](https://img-blog.csdnimg.cn/img_convert/69ed782287bbab4c7e19fb3b9a496732.png)
![img](https://img-blog.csdnimg.cn/img_convert/7d63719c14af12fbb48b8a24f735523d.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

终端有输入了,或者有客户端来连接了,那么内核会将其中没有反应的文件描述符的位清0,有反应的文件描述符保持不变,select返回,并且将修改后的集合从内核空间拷贝给用户空间  
 所以,如果是0准备好了,此时用户空间得到的集合就是:  
 100000000000000000000000000000000


5、遍历集合,看哪一位还是1((遍历的次数最大的文件描述符的值+1次))


6、看为1的这一位到底是谁,然后进行对应的IO操作


为了设置文件描述符我们要使用几个宏:



> 


[外链图片转存中...(img-0Rr289Dg-1715806320053)]
[外链图片转存中...(img-DDBsikHs-1715806320054)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上C C++开发知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618668825)**

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值