【0815作业】搭建select的TCP客户端、poll客户端、tftp文件上传

IO多路复用(重点!!!)

  1. 进程中如果同时需要处理多路输入输出流,在使用单进程单线程的情况下,同时处理多个输入输出请求。
  2. 在无法用多进程多线程,可以选择用IO多路复用;
  3. 由于不需要创建新的进程和线程,减少系统的资源开销,减少上下文切换的次数。
  4. 允许同时对多个IO进行操作,内核一旦发现进程执行一个或多个IO事件,会通知该进程。

 

1 搭建select的TCP客户端

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <netinet/in.h>
#include <poll.h>
#include <head.h>#define PORT 6666               //1024-49151
#define IP "192.168.1.103"    //ifconfig查看本机ip    int main(int argc, const char *argv[])
{//创建流式套接字int cfd = socket(AF_INET,SOCK_STREAM,0);if(cfd < 0){ERR_MSG("socket");return -1;}                                            printf("cfd= %d\n",cfd);//绑定客户端的IP和端口--->非必须绑定//若不绑定,则操作系统会给客户端绑定客户端所在的主机IP,以及随机端口号(49152~65535)//填充地址信息结构体,真实的地址信息结构体根据地址族制定//AF_INET: man 7 ip//要连接哪个服务器就填对应的IP和端口struct sockaddr_in sin;sin.sin_family         = AF_INET;       //必须填AF_INETsin.sin_port           = htons(PORT);   //端口号:填服务器绑定的端口号sin.sin_addr.s_addr    = inet_addr(IP); //IP地址:服务器绑定的IP地址//连接服务器if(connect(cfd, (struct sockaddr*)&sin,sizeof(sin)) < 0){ERR_MSG("connect");return -1;}//为select做准备fd_set readfd;fd_set tmpfd;FD_ZERO(&readfd);FD_ZERO(&tmpfd);FD_SET(0,&readfd);FD_SET(cfd,&readfd);int se_res =-1;ssize_t res = 0;char buf[128]="";socklen_t addrlen = sizeof(sin);while(1){tmpfd = readfd;//使用select判断阻塞se_res = select(cfd+1,&tmpfd,NULL,NULL,NULL);if(0 == se_res){printf("time out...\n");break;}if(se_res < 0){ERR_MSG("select");return -1;}printf("select succuss\n");for(int i=0;i<=cfd;i++){//判断接触阻塞文件描述符符号if(FD_ISSET(i,&tmpfd)==0)continue;//判断是否为0号文件描述符if(0 == i){printf("i == 0\n");//从终端读取数据bzero(buf,sizeof(buf));fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1]=0;//把读取的数据发送到服务器中if(send(cfd,buf,strlen(buf),0) < 0){perror("send");return -1;}printf("send succuss\n");continue;}//判断是否为cfd文件描述符else if(cfd == i){printf("i == %d\n",i);//接收客户端数据bzero(buf,sizeof(buf));res = recv(cfd,buf,sizeof(buf),0);if(res < 0){perror("recv");return -1;}//输出接收的信息在终端上printf("recv succuss\n");printf("[%s : %d : %d]%s\n",inet_ntoa(sin.sin_addr),\ntohs(sin.sin_port),cfd,buf);continue;}}}//关闭文件描述符close(cfd);return 0;
}

1.1 测试结果

1)服务器

ubuntu@ubuntu:06_IO模型$ gcc 02_selectTcpSer.c -o ser
ubuntu@ubuntu:06_IO模型$ ./ser
sfd= 3
允许端口快速重用成功
bind success
listen success
__96__
触发客户端连接事件[192.168.1.103 : 45662]newfd=4 客户端连接成功
__96__
触发客户端交互事件
[192.168.1.103 : 45662]newfd=4 : 123
send success
__96__
触发客户端交互事件
[192.168.1.103 : 45662]newfd=4 : 213
send success
1111
__96__
触发键盘输入事件
: 1111
1111
__96__
触发键盘输入事件
: 1111
1111
__96__
触发键盘输入事件
: 1111
__96__
触发客户端交互事件
[192.168.1.103 : 45662]newfd=4 客户端已下线
[192.168.1.103 : 45662]newfd=4 : 
__188__send: Bad file descriptor
ubuntu@ubuntu:06_IO模型$ 

2)客户端

ubuntu@ubuntu:06_IO模型$ gcc 04_selectTcpCli.c -o cli
ubuntu@ubuntu:06_IO模型$ ./cli
cfd= 3
123
select succuss
i == 0
send succuss
select succuss
i == 3
recv succuss
[192.168.1.103 : 6666 : 3]123*_*
213
select succuss
i == 0
send succuss
select succuss
i == 3
recv succuss
[192.168.1.103 : 6666 : 3]213*_*
^C
ubuntu@ubuntu:06_IO模型$ 


2 搭建poll客户端

#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <string.h>
#include <netinet/in.h>
#include <poll.h>
#include <head.h>#define PORT 6666               //1024-49151
#define IP "192.168.1.103"    //ifconfig查看本机ip    int main(int argc, const char *argv[])
{//创建流式套接字int cfd = socket(AF_INET,SOCK_STREAM,0);if(cfd < 0){ERR_MSG("socket");return -1;}                                            printf("cfd= %d\n",cfd);//绑定客户端的IP和端口--->非必须绑定//若不绑定,则操作系统会给客户端绑定客户端所在的主机IP,以及随机端口号(49152~65535)//填充地址信息结构体,真实的地址信息结构体根据地址族制定//AF_INET: man 7 ip//要连接哪个服务器就填对应的IP和端口struct sockaddr_in sin;sin.sin_family         = AF_INET;       //必须填AF_INETsin.sin_port           = htons(PORT);   //端口号:填服务器绑定的端口号sin.sin_addr.s_addr    = inet_addr(IP); //IP地址:服务器绑定的IP地址//连接服务器if(connect(cfd, (struct sockaddr*)&sin,sizeof(sin)) < 0){ERR_MSG("connect");return -1;}//定义集合struct pollfd fds[2];//将需要监测的文件描述符添加到集合中fds[0].fd = 0;fds[0].events = POLLIN;fds[1].fd = cfd;fds[1].events = POLLIN;char buf[128]="";ssize_t res = 0;int p_res = -1;while(1){p_res = poll(fds,2,-1);if(p_res < 0){ERR_MSG("poll");return -1;}else if(0 == p_res){printf("time out...");break;}//能运行到当前位置,则代表集合中有文件描述符准备就绪//判断是哪个文件描述符准备就绪,则对应处理函数即可//判断集合中的每个文件描述符的revents中是否有POLLIN//从revents中将代表POLLIN的那一位单独提取出来,判断是1还是0if((fds[0].revents & POLLIN) != 0){printf("触发键盘输入事件\n");bzero(buf,sizeof(buf));fgets(buf,sizeof(buf),stdin);buf[strlen(buf)-1] = 0;//发送数据if(send(cfd,buf,sizeof(buf),0) < 0){ERR_MSG("send");return -1;}printf("send succuss\n");}if(fds[1].revents & POLLIN){printf("触发服务器交互事件\n");//接收数据bzero(buf,sizeof(buf));res=recv(cfd,buf,sizeof(buf),0);if(res < 0){ERR_MSG("res");return -1;}else if(0 == res){printf("[%s : %d]cfd=%d 服务器掉线\n",IP,PORT,cfd);break;                                                                  }printf("[%s : %d]cfd=%d : %s\n",IP,PORT,cfd, buf);}}//关闭文件描述符close(cfd);return 0;
}

2.1 测试结果

1)客户端cli

ubuntu@ubuntu:06_IO模型$ ./a.out 
cfd= 3
111
select succuss
i == 0
send succuss
select succuss
i == 3
recv succuss
[192.168.1.103 : 6666 : 3]111*_*
2222
select succuss
i == 0
send succuss
select succuss
i == 3
recv succuss
[192.168.1.103 : 6666 : 3]2222*_*
3333
select succuss
i == 0
send succuss
select succuss
i == 3
recv succuss
[192.168.1.103 : 6666 : 3]3333*_*
^C
ubuntu@ubuntu:06_IO模型$ 

2)服务器ser

3 tftp上传的代码完成

#include <stdio.h>
#include <head.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>#define PORT 69              //服务器绑定的端口号
#define IP "192.168.1.107" //服务器的IP地址int do_download(int cfd,struct sockaddr_in sin);
int do_upload(int cfd, struct sockaddr_in sin);int main(int argc, const char *argv[])
{//创建报式套接字int cfd = socket(AF_INET, SOCK_DGRAM, 0);if(cfd < 0){ERR_MSG("socket");return -1;}printf("cfd = %d\n",cfd);//绑定客户端的地址信息结构体到套接字上--->非必须绑定//若不绑定,则操作系统会给客户端绑定运行主机的IP和随机的端口号//填充要连接的服务器地址信息结构体,真实的地址信息结构体根据地址族制定//要发给谁,就填谁的地址信息//AF_INET : man 7 ipstruct sockaddr_in sin;socklen_t addrlen=sizeof(sin);sin.sin_family         = AF_INET;       //必须填AF_INETsin.sin_port           = htons(PORT);   //端口号:服务器绑定的端口号sin.sin_addr.s_addr    = inet_addr(IP); //服务器绑定的IPchar choose =0;while(1){printf("------------------------\n");printf("---------1. 下载--------\n");printf("---------2. 上传--------\n");printf("---------3. 退出--------\n");printf("------------------------\n");printf("------------------------\n");printf("请输入>>> ");choose = getchar();while(getchar() != 10);   //循环获取字符,直到遇到\n结束循环switch(choose){case '1':do_download(cfd,sin);break;case '2':do_upload(cfd,sin);break;case '3':goto END;break;default:printf("输入错误!请重新输入\n");}}
END://关闭文件描述符close(cfd);return 0;
}int do_download(int cfd, struct sockaddr_in sin)
{//组包准备发送下载请求char buf[516]="";char name[20]="";printf("请输入要下载的文件名>>> ");scanf("%s",name);while(getchar()!=10);unsigned short *p1=(short*)buf;   //操作码*p1=htons(1);char *p2=buf+2;          //文件名strcpy(p2,name);char *p3=p2+strlen(p2); //第一个0 *p3=0;char *p4=p3+1;   //模式  strcpy(p4,"octet");size_t size=2+strlen(p2)+1+strlen(p4)+1; //操作码+文件名+0+模式+0//发送下载请求if(sendto(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0){ERR_MSG("sendto");return -1;}//创建下载文件并清空int fd = -1;   //必须初始化成一个无效的文件描述符socklen_t addrlen = sizeof(sin);ssize_t res = 0;unsigned short num = 0;  //记录本地的块编号//发送下载请求while(1){//接收数据bzero(buf,sizeof(buf));res = recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen);if(res < 0){ERR_MSG("recvfrom");return -1;}if(3 == buf[1])  //数据包{//判断服务器返回的数据包的块编号与本地记录的块编号是否一致if(*(unsigned short*)(buf+2) == htons((num+1))){num++;  //更新本地记录的块编号if(-1 == fd){fd=open(name,O_WRONLY|O_CREAT|O_TRUNC,0664);if(fd < 0){perror("open");return -1;}}//将数据写到文件中if(write(fd,buf+4,res-4) < 0 ){perror("write");return -1;}//发送ACKbuf[1] = 4;//*p1=htons(4);if(sendto(cfd,buf,4,0,(struct sockaddr*)&sin,sizeof(sin)) < 0){ERR_MSG("sendto");return -1;}//若接收到的数据小于512跳出循环,结束下载if(res-4 < 512){printf("%s 文件下载完毕\n",name);break;}}}else if(5 == buf[1])    //错误包{printf("错误: %d %s\n",ntohs(*(short*)(buf+2)),buf+4);close(fd);return -1;}}close(fd);return 0;
}int do_upload(int cfd, struct sockaddr_in sin)
{//组包准备发送上传请求char buf[516]="";char name[20]="";printf("请输入要上传的文件名>>> ");scanf("%s",name);while(getchar()!=10);int fd = open(name,O_RDONLY);if(fd < 0){if( errno == ENOENT){printf(">>>文件不存在,请重新输入<<<\n");return -2;}else{ERR_MSG("open");return -1;}}//组包准备发送上传请求unsigned short *p1=(short*)buf;   //操作码*p1=htons(2);char *p2=buf+2;          //文件名strcpy(p2,name);char *p3=p2+strlen(p2); //第一个0 *p3=0;char *p4=p3+1;   //模式  strcpy(p4,"octet");size_t size=2+strlen(p2)+1+strlen(p4)+1; //操作码+文件名+0+模式+0//发送上传请求if(sendto(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0){ERR_MSG("sendto");return -1;}//循环接收发送数据包ssize_t res;unsigned short num = 0;socklen_t addrlen = sizeof(sin);while(1){//将数据从文件中读取到buf中bzero(buf,sizeof(buf));res = recvfrom(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,&addrlen);if(res < 0){ERR_MSG("recvfrom");return -1;}//操作码的范围是1-5,因为是网络字节序//所以有效操作码存储在高位,即buf[1]的位置//printf("buf[1] = %d\n",buf[1]);   //4 服务器返回应答包if(4 == buf[1])  //数据包{//判断当前数据包的编号是否等于应答包的编号//防止数据包在传送过程丢包或重复收包if(num == ntohs(*(unsigned short*)(buf+2))){//修改操作码为数据包buf[1] = 3;//填充块编号num++;*(unsigned short*)(buf+2) = htons(num);//读取数据res = read(fd,buf+4,sizeof(buf)-4);if(res < 0){ERR_MSG("read");return -1;}else if(0 == res){printf("%s 文件上传完毕!\n",name);break;}//发送数据包//发送的数据包大小为,读取到的字节数res+操作码2byte+块编号2bytesif(sendto(cfd,buf,sizeof(buf),0,(struct sockaddr*)&sin,sizeof(sin)) < 0){ERR_MSG("sendto");return -1;}}else{printf("文件上传失败,请检查网络环境\n");break;}}else if(5 == buf[1])    //错误包{printf("错误: %d %s\n",ntohs(*(short*)(buf+2)),buf+4);close(fd);return -1;}}close(fd);return 0;
}

3.1 测试结果

1)下载

2)上传

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/97659.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DTC 19服务学习1

在UDS&#xff08;统一诊断服务&#xff09;协议中&#xff0c;0x19是用于DTC&#xff08;诊断故障代码&#xff09;信息的服务。以下是你提到的子服务的功能和作用&#xff1a; 0x01 - 报告DTC按状态掩码。这个子服务用于获取当前存储在ECU中的DTC列表。状态掩码用于过滤DTC&a…

通过爬虫抓取上市企业利润表并在睿思BI中展示

睿思BI从v5.3开始支持网络爬虫&#xff0c;可以从指定URL抓取表格数据&#xff0c;本示例实现从网络上抓取上市企业招商银行的利润表数据&#xff0c;并在睿思BI中进行展现。 功能演示URL&#xff1a;https://www.ruisitech.com/rsbi-ultimate/#/dashboard/ShareView?token31…

【C语言】动态通讯录 -- 详解

⚪前言 前面详细介绍了静态版通讯录【C语言】静态通讯录 -- 详解_炫酷的伊莉娜的博客-CSDN博客&#xff0c;但是静态版通讯录的空间是无法被改变的&#xff0c;而且空间利用率也不高。为了解决静态通讯录这一缺点&#xff0c;这时就要有一个能够随着存入联系人数量的增加而增大…

记录hutool http通过代理模式proxy访问外面的链接

效果&#xff1a; 代码&#xff1a; public class TestMain {public static void main(String[] args){HttpRequest httpRequest HttpRequest.get("https://www.youtube.com").timeout(30000);httpRequest.setProxy(new Proxy(Proxy.Type.HTTP,new InetSocketAddre…

简单理解Linux中的一切皆文件

一款操作系统要管理各种各样不同的硬件&#xff0c;因为硬件的不同所以它们使用的文件系统也不同。但是按道理来说&#xff0c;文件系统的不同对于用户来说可不是一件好事&#xff0c;操作不同的硬件就要使用不同的方法。 但是Linux有一切皆文件。 简单来说&#xff0c;Linux…

【数据分析入门】Matplotlib

目录 零、图形解析与工作流0.1 图形解析0.2 工作流 一、准备数据1.1 一维数据1.2 二维数据或图片 二、绘制图形2.1 画布2.2 坐标轴 三、绘图例程3.1 一维数据3.2 向量场3.3 数据分布3.4 二维数据或图片 四、自定义图形4.1 颜色、色条与色彩表4.2 标记4.3 线型4.4 文本与标注4.5…

(排序) 剑指 Offer 21. 调整数组顺序使奇数位于偶数前面 ——【Leetcode每日一题】

❓剑指 Offer 21. 调整数组顺序使奇数位于偶数前面 难度&#xff1a;简单 输入一个整数数组&#xff0c;实现一个函数来调整该数组中数字的顺序&#xff0c;使得所有奇数在数组的前半部分&#xff0c;所有偶数在数组的后半部分。 示例&#xff1a; 输入&#xff1a;nums [1…

python+django+mysql高校校园外卖点餐系统--计算机毕设项目

本文的研究目标是以高校校园外卖点餐为对象&#xff0c;使其高校校园外卖点餐为目标&#xff0c;使得高校校园外卖点餐的信息化体系发展水平提高。论文的研究内容包括对个人中心、美食分类管理、用户管理、商家管理、美食信息管理、工作人员管理、安全检查管理、系统管理、订单…

nginx(七十七)nginx与包体的探究

一 nginx与body体 说明&#xff1a;本文不具有生产意义,只是为了nginx知识的闭环,可以跳过即可 --> "数据脱敏"题外话&#xff1a; 对body的CURD,nginx和openresty处理方式不同强调&#xff1a; 本文是基于http演示的,如果是https加密我们是看不到的 ① core模…

postgresql的在windows下的安装

postgresql的在windows下的安装 下载安装步骤超级用户设置密码本地化设置安装信息安装完成 查看postgresql服务pgAdmin的使用打开命令 行工具查询数据库版本 创建数据库 下载 官网地址 https://www.postgresql.org/ 下载页面 https://www.postgresql.org/download/ windows下…

泛型编程 学习笔记

#include "iostream"using namespace std;template<typename T> void Print(T a) {cout << a << endl; }int main() {int a 5;double b 2.3;char c e;string d "sdfasd";Print(a);Print(b);Print(c);Print(d);return 0; } 它可以不用…

“SRP模型+”多技术融合在生态环境脆弱性评价模型构建、时空格局演变分析与RSEI 指数的生态质量评价

近年来&#xff0c;国内外学者在生态系统的敏感性、适应能力和潜在影响等方面开展了大量的生态脆弱性研究&#xff0c;他们普遍将生态脆弱性概念与农牧交错带、喀斯特地区、黄土高原区、流域、城市等相结合&#xff0c;评价不同类型研究区的生态脆弱特征&#xff0c;其研究内容…

k8s服务注册发现

Service 是 将运行在一个或一组pod上的网络应用程序公开为网络服务的方法。 定义service前端为service名称、ip、端口等不变的部分&#xff0c;后端为符合标签选择的pod集合 注册 通过api server提交注册service请求到DNSservice随后得到clusterIP&#xff08;虚拟ip地址&am…

如何解决使用npm出现Cannot find module ‘XXX\node_modules\npm\bin\npm-cli.js’错误

遇到问题&#xff1a;用npm下载组件时出现Cannot find module ‘D&#xff1a;software\node_modules\npm\bin\npm-cli.js’ 问题&#xff0c;导致下载组件不能完成。 解决方法&#xff1a;下载缺少的npm文件即可解决放到指定node_modules目录下即可解决。 分析问题&#xff1…

机器学习知识点总结:什么是GBDT(梯度提升树)

什么是GBDT(梯度提升树) 虽然GBDT同样由许多决策树组成&#xff0c;但它与随机森林由许多不同。 其中之一是GBDT中的树都是回归树&#xff0c;树有分类有回归&#xff0c;区分它们的方法很简单。将苹果单纯分为好与坏的是分类树&#xff0c;如果能为苹果的好坏程度打个分&…

GPT系列总结

1.GPT1 无监督预训练有监督的子任务finetuning https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf 1.1 Unsupervised pre-training &#xff08;1&#xff09;基于一个transformer decoder&#xff0c;通过一个窗口的输入得…

STM32 定时器复习

12MHz晶振的机器周期是1us&#xff0c;因为单片机的一个机器周期由6个状态周期组成&#xff0c;1个机器周期6个状态周期12个时钟周期&#xff0c;因此机器周期为1us。 51单片机常用 for(){__nop(); //执行一个机器周期&#xff0c;若想循环n us&#xff0c;则循环n次。 }软件…

C语言入门_Day7 逻辑运算

目录&#xff1a; 前言 1.逻辑运算 2.优先级 3.易错点 4.思维导图 前言 算术运算用来进行数据的计算和处理&#xff1b;比较运算是用来比较不同的数据&#xff0c;进而来决定下一步怎么做&#xff1b;除此以外还有一种运算叫做逻辑运算&#xff0c;它的应用场景也是用来影…

快速提高写作生产力——使用PicGo+Github搭建免费图床,并结合Typora

文章目录 简述PicGo下载PicGo获取Token配置PicGo结合Typora总结 简述PicGo PicGo: 一个用于快速上传图片并获取图片 URL 链接的工具 PicGo 本体支持如下图床&#xff1a; 七牛图床 v1.0腾讯云 COS v4\v5 版本 v1.1 & v1.5.0又拍云 v1.2.0GitHub v1.5.0SM.MS V2 v2.3.0-b…

Kotlin 基础教程二

constructor 构造器一般情况下可以简化为主构造器 即: class A constructor(参数) : 父类 (参数) 也可以在构造器上直接声明属性constructor ( var name) 这样可以全局访问 init { } 将和成员变量一起初始化 thread {} ktx 默认创建一个线程 susped 挂起 data class 可以简…