博客摘录「 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)`发送最后一次读取的不完整的数据块。这样可以确保将整个文件的数据都发送给客户端。

相关推荐
夜流冰25 分钟前
GNU/Linux - tar命令
笔记
fcopy27 分钟前
GOLANG笔记第四周
笔记·golang
青石横刀策马1 小时前
Python学习笔记(1)装饰器、异常检测、标准库概览、面向对象
笔记·python·学习
跃渊Yuey1 小时前
【C++笔记】vector使用详解及模拟实现
c++·笔记
GIS 数据栈2 小时前
博客摘录「 pyqt 为新建子线程传参以及子线程返回数据到主线程」2023年12月7日
笔记·python·pyqt·多线程·多线程通信
yz-俞祥胜2 小时前
杨中科 .Net Core 笔记 DI 依赖注入2
笔记·.netcore
程序员劝退师_2 小时前
Vue学习笔记
vue.js·笔记·学习
祭の2 小时前
IDEA旗舰版编辑器器快速⼊门(笔记)
java·笔记·intellij-idea
尘佑不尘2 小时前
kali上安装docker,并且生成centos7容器和创建apache容器后台运行
笔记·web安全·docker·容器·apache
白八实3 小时前
汇编代码中的主要指令笔记
汇编·jvm·笔记