c - 客户端程序不会继续前进
问题描述
我正在编写一个程序,客户端尝试从服务器下载文件。
以下是客户端的代码。
FILE * file_to_download;
file_to_download = fopen("CLIENT_file_downloaded", "ab");
long int total_bytes = 0;
int bytes_received = 0;
char received_buffer[2048];
printf(">>> Downloading file...\n");
while ((bytes_received = read(connector, received_buffer, sizeof received_buffer)) > 0) { // keep receiving until data stops sending
printf("=");
total_bytes += bytes_received;
fwrite(received_buffer, 1, bytes_received, file_to_download);
}
if (bytes_received < 0) {
printf("\n>>> Read error\n");
exit(1);
}
printf("\n>>> File downloaded (Bytes received: %ld)\n\n", total_bytes);
当我立即关闭套接字连接时,这非常有效,但是,如果我将其打开以执行其他一些功能(例如向服务器发送消息),它会在“ >>>下载文件... ”处停止,但是,我可以在文件夹中看到下载的文件。此外,一旦我终止服务器端程序,它会打印出其他所有内容。
从其他 SO 线程来看,我认为这与套接字“阻塞”有关。
- 但是为什么我的文件被下载了(我可以在文件夹中看到它)?该
fwrite
函数负责while循环内部的这个,但是如果它进入while循环,为什么它不打印任何东西? - 我怎样才能让服务器告诉客户端它已经完成发送数据,我怎样才能让客户端程序继续前进?
解决方案
您已经收到了很多评论,但我会尝试将至少一些零碎的内容总结到一个地方。
首先,问题是:当你现在正在做的事情时,你调用的基本上有三种方式read
可以返回:
- 它返回一个严格的正值(即至少为 1),告诉您读取的字节数。
- 它返回 0 表示套接字已关闭。
- 它返回一个负值表示错误。
但是没有明确的方式让它返回并告诉你:“套接字仍然打开,没有错误,但没有等待读取的数据。”
因此,正如其他人所说,如果您要传输文件(或其他一些已定义的数据块),您通常需要在 TCP 之上定义一些应用程序级协议来支持它。最明显的起点是您首先发送文件的大小(通常作为单个固定大小的块,例如 4 或 8 个字节),然后是那么多字节的数据。
如果你这样做,你可以定义一些至少可以工作的东西。它可能会遗漏各种可能的错误,但至少如果一切正常,它就可以了。
下一步通常是添加某种校验和/CRC 之类的东西,因此当您认为传输完成时,您可以验证数据以至少合理地保证它有效(即,您收到的数据匹配发送了什么)。
另一个需要考虑的普遍方向是你的阅读方式。这里有几个选择。一是避免打电话read
,直到你确定它可以/将会成功。如果您一次只处理一个(或几个)套接字,您可以调用select
,它会告诉您套接字何时准备好读取,因此可以保证快速成功地发出读取。它的读数可能比您要求的要少,但它会返回而不是无限期地等待数据。如果您必须处理大量套接字,您可能更喜欢查找epoll
,它的作用大致相同,但是当您必须处理许多句柄时会减少开销。
处理此问题的另一种可能方法是O_NONBLOCK
为您的套接字设置选项。在这种情况下,当没有数据可用时尝试读取将被视为错误,因此它将立即返回EAGAIN
or错误EWOUDLBLOCK
(您必须为此做好准备)。这为您提供了一种相当简单的方法,至少在您没有更多可用数据时继续进行,但对有效传输数据的任何其他困难没有任何作用。
正如其他人所指出的,有很多现有的协议可以做这样的事情并重新发明它可能不是你时间的最佳利用。另一方面,某些协议可能会有些痛苦(例如,ftp
正常模式要求您打开/使用两个单独的套接字)。其他的足够复杂,您可能不想尝试自己实现它们,但是很难找到能够很好地支持它们的库。
就我个人而言,我发现 websockets 在很多这样的任务中工作得相当合理。它们包括框架(因此作为单个 websocket 写入发送的内容将通过单个 websocket 读取接收)。他们还使用 CRC 进行错误检查。因此,对于很多这样的情况,它或多或少会自动处理大部分细节。它还包括(并且在大多数情况下使用)他们所谓的 ping/pong 协议来检测连接丢失,这比 TCP 通常自己做的要快得多。
但如上所述,有很多替代方案,其中一些专为传输文件而设计(因此您收到的不仅仅是文件的内容,还包括名称和附加到该内容的其他元数据等内容)。
推荐阅读
- python - Python 每天倒计时一系列时间
- python - geopandas 无法识别多边形中的点
- unix - scp远程文件到hadoop而不将它复制到边缘节点
- python - 如何从单元测试 django 视图中模拟模型方法?
- powershell - 如何使用 PowerShell 将有关监视器的信息导出到 csv 文件?
- c++ - 如何更改另一个进程中的地址,其值也可以更改?
- r - 如何使用 dplyr 或 ggplot 绘制连续参数的中值?
- php - Yii2在dataProvider中添加自定义查询,无需多次重写查询
- python - 从行和列索引创建数据框
- r - 使用R中的if语句逐行比较csv