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

本期分享的主要是使用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通信的深入了解;不懂就问,欢迎评论区留言哦!

你可能感兴趣的:(日常小玩,udp,服务器,网络,linux,c语言)