博客摘录「 TCP/IP网络编程——习题答案」2023年10月29日

cpp 复制代码
clnt_sd=accept(serv_sd, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);
	
	read(clnt_sd, file_name, BUF_SIZE);   
	fp=fopen(file_name, "rb");           //尝试打开客户端请求的文件
	if(fp!=NULL)              //如果文件存在,则传送给客户端
	{
		while(1)
		{
			read_cnt=fread((void*)buf, 1, BUF_SIZE, fp);
			if(read_cnt<BUF_SIZE)
			{
				write(clnt_sd, buf, read_cnt);
				break;
			}
			write(clnt_sd, buf, BUF_SIZE);
		}
	}

在C++中,`fread`是一个函数,用于从文件中读取数据。它的原型如下:

cpp 复制代码
size_t fread(void* ptr, size_t size, size_t count, FILE* stream);

其中,`ptr`是一个指向存储读取数据的内存块的指针;`size`是每个数据块的字节数;`count`是要读取的数据块数;`stream`是一个指向要读取的文件的指针。

`fread`函数将从文件中读取`count`个数据块,每个数据块的大小为`size`字节,然后将这些数据存储到`ptr`指向的内存块中。函数返回实际读取的数据块数,如果读取失败或到达文件末尾,返回的值可能小于`count`。

以下是一个使用`fread`函数读取二进制文件的示例:

cpp 复制代码
#include <iostream>
#include <cstdio>

int main() {
    FILE* fp = std::fopen("data.bin", "rb");
    if (fp == nullptr) {
        std::perror("Failed to open file");
        return 1;
    }

    int buffer[4];
    std::size_t count = std::fread(buffer, sizeof(int), 4, fp);
    if (count != 4) {
        std::perror("Failed to read file");
        return 1;
    }

    for (int i = 0; i < 4; ++i) {
        std::cout << buffer[i] << " ";
    }

    std::fclose(fp);
    return 0;
}

在上面的示例中,我们打开了一个名为"data.bin"的二进制文件,并使用`fread`函数读取了其中的4个整数。`fread`函数将这些整数存储到`buffer`数组中,然后我们将这些整数输出到控制台。需要注意的是,我们使用了"rb"作为打开文件的方式,以确保文件以二进制方式打开。

上述代码的含义是:

  1. `clnt_sd=accept(serv_sd, (struct sockaddr*)&clnt_adr, &clnt_adr_sz);`:通过服务器套接字`serv_sd`接受客户端的连接请求,并创建一个新的套接字`clnt_sd`来与客户端进行通信。这个函数用于服务器端接受客户端的连接请求。

  2. `read(clnt_sd, file_name, BUF_SIZE);`:从已连接的套接字`clnt_sd`中读取客户端发送的文件名,并将其存储到`file_name`变量中。

  3. `fp=fopen(file_name, "rb");`:尝试以只读二进制方式打开客户端请求的文件。如果文件存在,则返回一个指向该文件的文件指针`fp`。

  4. `if(fp!=NULL)`:如果文件指针`fp`不为空,即文件存在,则执行下面的代码块。

  5. `while(1)`:无限循环,用于从文件中读取数据并发送给客户端。

  6. `read_cnt=fread((void*)buf, 1, BUF_SIZE, fp);`:从文件中读取最多`BUF_SIZE`字节的数据,并将其存储到缓冲区`buf`中。`fread`函数返回实际读取的字节数。

  7. `if(read_cnt<BUF_SIZE)`:如果实际读取的字节数小于`BUF_SIZE`,说明已经读取到了文件末尾。

  8. `write(clnt_sd, buf, read_cnt);`:将从文件中读取的数据通过已连接的套接字`clnt_sd`发送给客户端。

  9. `break;`:跳出循环,结束文件传输。

  10. `write(clnt_sd, buf, BUF_SIZE);`:将从文件中读取的数据块`buf`通过已连接的套接字`clnt_sd`发送给客户端。

综合起来,这段代码的作用是服务器端接受客户端连接请求,读取客户端发送的文件名,并尝试打开该文件。如果文件存在,则从文件中读取数据并通过已连接的套接字发送给客户端。这段代码适用于服务器端响应客户端下载文件的请求。

在服务器向客户端发送文件数据时,可能存在以下两种情况:

  1. 文件大小正好是`BUF_SIZE`的整数倍:此时每次从文件中读取`BUF_SIZE`字节的数据块,并使用`write(clnt_sd, buf, BUF_SIZE)`将数据块发送给客户端。

  2. 文件大小不是`BUF_SIZE`的整数倍:此时最后一次从文件中读取的数据块大小可能小于`BUF_SIZE`,因此需要使用`write(clnt_sd, buf, read_cnt)`将实际读取的数据块发送给客户端。

因此,在`while`循环中需要使用两次`write`函数,分别处理上述两种情况。第一次使用`write(clnt_sd, buf, BUF_SIZE)`发送完整的数据块,第二次使用`write(clnt_sd, buf, read_cnt)`发送最后一次读取的不完整的数据块。这样可以确保将整个文件的数据都发送给客户端。

相关推荐
吴梓穆11 小时前
UE5学习笔记 FPS游戏制作38 继承标准UI
笔记·学习·ue5
V---scwantop---信12 小时前
英文字体:大胆都市街头Y2Y涂鸦风格品牌海报专辑封面服装字体 Chrome TM – Graffiti Font
笔记·字体
Moonnnn.12 小时前
运算放大器(四)滤波电路(滤波器)
笔记·学习·硬件工程
吴梓穆12 小时前
UE5学习笔记 FPS游戏制作37 蓝图函数库 自己定义公共方法
笔记·学习·ue5
吴梓穆13 小时前
UE5学习笔记 FPS游戏制作41 世界模式显示UI
笔记·学习·ue5
s_little_monster13 小时前
【Linux】进程信号的捕捉处理
linux·运维·服务器·经验分享·笔记·学习·学习方法
RedMery14 小时前
论文阅读笔记:Denoising Diffusion Implicit Models (4)
论文阅读·笔记
go_bai14 小时前
Linux环境基础开发工具——(2)vim
linux·开发语言·经验分享·笔记·vim·学习方法
吴梓穆14 小时前
UE5学习笔记 FPS游戏制作35 使用.csv配置文件
笔记·学习·ue5
100分题库小程序15 小时前
2025年机动车授权签字人考试判断题分享
经验分享·笔记