本期分享的主要是使用UDP实现文件下载功能,需要自己编写服务器和客户端,实现的功能主要有以下几个:
(1)服务器可以为请求的用户下发文件数据(前提是服务器得有这个数据文件)
(2)客户端请求下载数据文件
下面带大家来认真分析下,大家可以对照我遇到的问题是不是大家有遇到,避免大家踩坑,server端代码如下:
首先当然还是头文件部分,没这个可不行呀,哈哈:
#ifndef __HEAD_H__
#define __HEAD_H__
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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通信的深入了解;不懂就问,欢迎评论区留言哦!