当前位置: 首页 > news >正文

一个UDP下载服务器的实现(模拟下载文件)

本期分享的主要是使用UDP实现文件下载功能,需要自己编写服务器和客户端,实现的功能主要有以下几个:
(1)服务器可以为请求的用户下发文件数据(前提是服务器得有这个数据文件)
(2)客户端请求下载数据文件
下面带大家来认真分析下,大家可以对照我遇到的问题是不是大家有遇到,避免大家踩坑,server端代码如下:
首先当然还是头文件部分,没这个可不行呀,哈哈:

#ifndef __HEAD_H__
#define __HEAD_H__#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>#endif

1.接下来看一下具体server的代码实现:

#include "head.h"struct sockaddr_in senaddr;//存放服务器的Ip以及端口号的结构体
int bindOfIP(const char *pIp, int Port)		//绑定服务器的ip和端口
{int sockfd = 0;int ret = 0;sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sockfd){perror("fail to sockfd");return -1;}senaddr.sin_family = AF_INET;senaddr.sin_port = htons(Port);senaddr.sin_addr.s_addr = inet_addr(pIp);ret = bind(sockfd, (struct sockaddr *)&senaddr, sizeof(senaddr));if (-1 == ret){perror("fail to bind");return -1;}return sockfd;
}

以下是发送文件的模块,自从用了UDP发送数据才知道了IO的部分还比较欠缺,那么来看下那一部分有问题:
(1)当选择fread去读取文件的时候(第二个参数设置为1,也就是每次读取成员的大小一个字节),一定要知道它的返回值就是是成功读取文件字节的个数;只有当参数不是1的时候,那么返回值就是成功读取成员的个数;但是最终读取的成员最终还是存放在了我们定义的第一个参数中了;

ret = fread(tmpbuff, 1, sizeof(tmpbuff), fp);

因此在sendto的第三个参数中我们恰好可以使用fread的返回值;

sendto(sockfd, tmpbuff, ret, 0, sendaddr, len);

(2)不使用字节进行传输时,不能在sendto时把第三个变量换位strlen(tmpbuff),因为二进制文件不允许strlen;

int sendFile(char *filename, int sockfd, struct sockaddr *sendaddr, socklen_t len)
{FILE *fp = NULL;char tmpbuff[4096] = {0};ssize_t ret = 0;char *ptmp = NULL;fp = fopen(filename, "r");if (NULL == fp){perror("fail to fopen");return -1;}printf("开始发送!\n");while(1){memset(tmpbuff, 0, sizeof(tmpbuff));ret = fread(tmpbuff, 1, sizeof(tmpbuff), fp);if (ret <= 0){break;}ret = sendto(sockfd, tmpbuff, ret, 0, sendaddr, len);if (-1 == ret){perror("fail to sendto");return -1;}}memset(tmpbuff, 0, sizeof(tmpbuff));sprintf(tmpbuff, "__quit__");ret = sendto(sockfd, tmpbuff, strlen(tmpbuff), 0, sendaddr, len);if (-1 == ret){perror("fail to sendto");return -1;}return 0;
}int main(int argc, const char *argv[])
{int sockfd = 0;char filename[32] = {0};ssize_t nsize = 0;socklen_t len = sizeof(senaddr);int ret = 0;sockfd = bindOfIP("192.168.209.128", 50000);while (1){nsize = recvfrom(sockfd, filename, sizeof(filename), 0, (struct sockaddr *)&senaddr, &len);if (-1 == nsize){perror("fail to recvfrom");return -1;}else{printf("请求的文件名和路径:filename = %s\n", filename);ret = sendFile(filename, sockfd, (struct sockaddr *)&senaddr, len);if (0 == ret){printf("发送成功!\n");}}}return 0;
}

2.下面来看一下client端的实现:

#include "head.h"struct sockaddr_in recvbuf;
int bindOfIP(const char *pIp, int Port)
{int sockfd = 0;int ret = 0;sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sockfd){perror("fail to sockfd");return -1;}recvbuf.sin_family = AF_INET;recvbuf.sin_port = htons(Port);recvbuf.sin_addr.s_addr = inet_addr(pIp);ret = bind(sockfd, (struct sockaddr *)&recvbuf, sizeof(recvbuf));if (-1 == ret){perror("fail to bind");return -1;}return sockfd;
}
//接收服务器的文件
int recvFile(int sockfd, char *filename)
{FILE *fp = NULL;char tmpbuff[4096] = {0};ssize_t nsize = 0;printf("进来了\n");fp = fopen(filename, "w");if (NULL == fp){perror("fail to fopen");return -1;}while (1){memset(tmpbuff, 0, sizeof(tmpbuff));nsize = recvfrom(sockfd, tmpbuff, sizeof(tmpbuff), 0, NULL, NULL);if (nsize <= 0){break;}if (!strcmp("__quit__", tmpbuff)){break;}fwrite(tmpbuff, sizeof(char), nsize, fp);fflush(fp);}fclose(fp);return 0;
}int main(int argc, const char *argv[])
{int sockfd = 0;char filename[32] = {0};char *name = NULL;ssize_t nsize = 0;socklen_t len;struct sockaddr_in senaddr;int ret = 0;//	sockfd = bindOfIP("192.168.209.129", 50001);如果需要可以绑定自己的IP地址和端口sockfd = socket(AF_INET, SOCK_DGRAM, 0);if (-1 == sockfd){perror("fail to sockfd");return -1;}senaddr.sin_family = AF_INET;senaddr.sin_port = htons(50000);senaddr.sin_addr.s_addr = inet_addr("192.168.209.128");while (1){printf("请输入您需要下载的文件路径:");gets(filename);nsize = sendto(sockfd, filename, strlen(filename), 0, (struct sockaddr *)&senaddr, sizeof(senaddr));if (-1 == nsize){perror("fail to sendto");return -1;}name = filename + strlen(filename) - 1;while (*name != '/'){--name;}++name;//解析出文件名printf("name = %s\n", name);//调试代码ret = recvFile(sockfd, name);if (0 == ret){printf("接收成功!\n");break;}}return 0;
}

这个就是一个简单的UDP下载服务器的实现,其实也是很简单的,但是需要注意的细节还是很多的,能提高对IO操作以及UDP通信的深入了解;不懂就问,欢迎评论区留言哦!

http://www.lryc.cn/news/90682.html

相关文章:

  • 01.hadoop上课笔记之hadoop介绍
  • 小鹏汽车Q1财报:押注G6、大力降本,明年智驾BOM降半
  • VMware ESXi 8.0U1a 发布 - 领先的裸机 Hypervisor
  • Unity的IPreprocessBuild:深入解析与实用案例
  • htmlCSS-----CSS选择器(下)
  • RDK X3 Module发布,全新软硬件平台加速实现量产级产品落地
  • 【面试实战】Redis缓存设计
  • 如何解决js定时器不准确问题
  • 学习笔记——vue中使用el-dropdown组件报错
  • Java之旅(八)
  • 华为OD机试真题(Java),四则运算(100%通过+复盘思路)
  • HTML表单标签form分析
  • Qt 项目文件Pri详解
  • Keil 5 MDK 发律师函警告了,如何用STCubeIDE开发标准库的程序(STM32F103C8T6为例)
  • 接口测试--apipost接口断言详解
  • YYDS练手 130道python练习题 完整版PDF
  • 2-python的变量类型
  • Python之并发编程一背景知识
  • Redis分区
  • 代码随想录算法训练营第56天 | 583、72
  • c++输入输出文件操作stream
  • 【小呆的力学笔记】非线性有限元的初步认识【三】
  • python计算闰年
  • 聊聊如何使用Js写一个简单的二级联动和三级联动呢?
  • IPv4 和 IPv6 的特点、区别以及在互联网中的应用
  • JavaScript处理移动web交互
  • 4年测试经验,一问三不知,过于离谱...
  • Java 与查找算法(2)二分查找
  • Java程序设计入门教程--原始类与包装类
  • pip安装python库速度慢、失败及超时报错解决办法