《TCP IP网络编程》第七章

news2024/12/24 10:05:22

第七章:优雅的断开套接字的连接

        TCP 的断开连接过程比建立连接更重要,因为连接过程中一般不会出现大问题,但是断开过程可能发生预想不到的情况。因此应该准确掌控。所以要掌握半关闭(Half-close),才能明确断开过程。

单方面断开连接带来的问题:

        Linux 和 Windows 的 closesocket 函数意味着完全断开连接。完全断开不仅指无法传输数据,而且也不能接收数据。因此在某些情况下,通信一方单方面的断开套接字连接,显得不太优雅。如图所示:

        图中描述的是 2 台主机正在进行双向通信,主机 A 发送完最后的数据后,调用 close 函数断开了最后的连接,之后主机 A 无法再接受主机 B 传输的数据。实际上,是完全无法调用与接受数据相关的函数。最终,由主机 B 传输的、主机 A 必须要接受的数据也销毁了。 

        为了解决这类问题,「只关闭一部分数据交换中使用的流」的方法应运而生。断开一部分连接是指,可以传输数据但是无法接收,或可以接受数据但无法传输。顾名思义就是只关闭流的一半。

套接字和流(Stream):

        两台主机通过套接字建立连接后进入可交换数据的状态,又称「流形成的状态」。也就是把建立套接字后可交换数据的状态看作一种流。

        此处的流可以比作水流。水朝着一个方向流动,同样,在套接字的流中,数据也止呕能向一个方向流动。因此,为了进行双向通信,需要如图所示的两个流:

        一旦两台主机之间建立了套接字连接,每个主机就会拥有单独的输入流和输出流。当然,其中一个主机的输入流与另一个主机的输出流相连,而输出流则与另一个主机的输入流相连。另外,本章讨论的「优雅的断开连接方式」只断开其中 1 个流,而非同时断开两个流。Linux 和 Windows 的 closesocket 函数将同时断开这两个流,因此与「优雅」二字还有一段距离。

针对优雅断开的 shutdown 函数 :

#include <sys/socket.h>
int shutdown(int sock, int howto);
/*
成功时返回 0 ,失败时返回 -1
sock: 需要断开套接字文件描述符
howto: 传递断开方式信息
*/

调用上述函数时,第二个参数决定断开连接的方式,其值如下所示:

  • SHUT_RD : 断开输入流
  • SHUT_WR : 断开输出流
  • SHUT_RDWR : 同时断开 I/O 流

        若向 shutdown 的第二个参数传递SHUT_RD,则断开输入流,套接字无法接收数据。即使输入缓冲收到数据也会抹去,而且无法调用相关函数。如果向 shutdown 的第二个参数传递SHUT_WR,则中断输出流,也就无法传输数据。若如果输出缓冲中还有未传输的数据,则将传递给目标主机。最后,若传递关键字SHUT_RDWR,则同时中断 I/O 流。这相当于分 2 次调用 shutdown ,其中一次以SHUT_RD为参数,另一次以SHUT_WR为参数。

基于半关闭的文件传输程序:

服务器端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int serv_sd, clnt_sd;// 服务器套接字描述符、客户端套接字描述符
    FILE *fp;// 文件指针
    char buf[BUF_SIZE];// 缓冲区
    int read_cnt;// 读取的字节数

    struct sockaddr_in serv_adr, clnt_adr;// 服务器地址、客户端地址结构体
    socklen_t clnt_adr_sz;// 客户端地址结构体大小
    // 检查命令行参数
    if (argc != 2)
    {
        printf("Usage : %s <port>\n", argv[0]);
        exit(1);
    }
    // 以二进制只读方式打开文件
    fp = fopen("file_server.c", "rb");
    // 创建套接字
    serv_sd = socket(PF_INET, SOCK_STREAM, 0);
    // 设置服务器地址结构体
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family = AF_INET;
    serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);// 任意可用的IP地址
    serv_adr.sin_port = htons(atoi(argv[1]));// 从命令行参数获取端口号
    // 绑定套接字和地址
    bind(serv_sd, (struct sockaddr *)&serv_adr, sizeof(serv_adr));
    // 监听连接请求
    listen(serv_sd, 5);
    // 接受客户端连接
    clnt_adr_sz = sizeof(clnt_adr);
    clnt_sd = accept(serv_sd, (struct sockaddr *)&clnt_adr, &clnt_adr_sz);
    // 读取文件内容,并发送给客户端
    while (1)
    {
        //从文件流中读取数据,buffer为接收数据的地址,size为一个单元的大小,count为单元个数,stream为文件流
        //返回实际读取的单元个数
        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);// 将缓冲区中的数据发送给客户端
    }
    // 关闭写端,等待客户端读取完数据
    shutdown(clnt_sd, SHUT_WR);
    // 读取来自客户端的消息并打印
    read(clnt_sd, buf, BUF_SIZE);
    printf("Message from client: %s \n", buf);

    fclose(fp);
    close(clnt_sd);
    close(serv_sd);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

客户端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>

#define BUF_SIZE 30
void error_handling(char *message);

int main(int argc, char *argv[])
{
    int sd;// 套接字描述符
    FILE *fp;// 文件指针

    char buf[BUF_SIZE];
    int read_cnt;
    struct sockaddr_in serv_adr;// 服务器地址结构体

    if (argc != 3)
    {
        printf("Usage : %s <IP> <port>\n", argv[0]);
        exit(1);
    }
    // 以二进制写方式打开文件
    fp = fopen("receive.cpp", "wb");
    // 创建套接字
    sd = socket(PF_INET, SOCK_STREAM, 0);
    // 设置服务器地址结构体
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family = AF_INET;
    serv_adr.sin_addr.s_addr = inet_addr(argv[1]);// 从命令行参数获取服务器IP地址
    serv_adr.sin_port = htons(atoi(argv[2]));// 从命令行参数获取服务器端口号
    // 连接服务器
    connect(sd, (struct sockaddr *)&serv_adr, sizeof(serv_adr));
    // 从套接字读取数据,并写入文件
    while ((read_cnt = read(sd, buf, BUF_SIZE)) != 0)
        fwrite((void *)buf, 1, read_cnt, fp);

    puts("Received file data");
    write(sd, "Thank you", 10);
    fclose(fp);
    close(sd);
    return 0;
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}

        客户端接受完成后,服务器会接收到来自客户端的感谢信息:

 


习题:

1、解释 TCP 中「流」的概念。UDP 中能否形成流?请说明原因。

        两台主机中通过套接字建立连接后进入可交换数据的状态,又称「流形成的状态」。也就是把建立套接字后可交换数据的状态看做一种流。UDP 没有建立连接的过程,所以不能形成流。

2、Linux 中的 close 函数或 Windows 中的 closesocket 函数属于单方面断开连接的方法,有可能带来一些问题。什么是单方面断开连接?什么情形下会出现问题?

        单方面断开连接就是两台主机正在通信,其中一台主机关闭了所有连接,那么一台主机向另一台主机传输的数据可能会没有接收到而损毁。

        传输文件的服务器只需连续传输文件数据即可,而客户端不知道需要接收数据到何时。客户端也没有办法无休止的调用输入函数。现在需要一个 EOF 代表数据已经传输完毕,那么这时就需要半关闭,服务端把自己的输出流关了,这时客户端就知数据已经传输完毕,因为服务端的输入流还没关,客户端可以给服务器汇报,接收完毕。

3、什么是半关闭?针对输出流执行半关闭的主机处于何种状态?半关闭会导致对方主机接收什么消息?

        半关闭就是把输入流或者输出流关了。针对输出流执行半关闭的主机处于可以接收数据而不能发送数据。半关闭会导致对方主机接收一个 EOF 文件结束符。对方就知道你的数据已经传输完毕。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/767226.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

131、仿真-基于51单片机智能电子称HX711报警仿真设计(程序+原理图+PCB图+Proteus仿真+参考论文+元器件清单等)

摘 要 电子秤是将检测与转换技术、计算机技术、信息处理、数字技术等技术综合一体的现代新型称重仪器。它与我们日常生活紧密结合息息相关。 电子称主要以单片机作为中心控制单元&#xff0c;通过称重传感器进行模数转换单元&#xff0c;在配以键盘、显示电路及强大软件来组成…

【手撕C语言基础】递归

(꒪ꇴ꒪ ),hello我是祐言博客主页&#xff1a;C语言基础,Linux基础,软件配置领域博主&#x1f30d;快上&#x1f698;&#xff0c;一起学习&#xff01;送给读者的一句鸡汤&#x1f914;&#xff1a;集中起来的意志可以击穿顽石!作者水平很有限&#xff0c;如果发现错误&#x…

数据结构(王道)——数据结构之 二叉树

一、数据结构之 二叉树概念&#xff1a; 特殊的二叉树结构&#xff1a; 满二叉树完全二叉树 二叉排序树 平衡二叉树 二叉树基本概念总结&#xff1a; 二、二叉树的常用性质&#xff1a; 1、叶子结点比二分支结点多一个

汽车的空气悬架的功能以及发展趋势

空气悬架能实现什么功能以及发展趋势 了解空气悬架之前,首先得快速了解什么是悬架。 教科书说法是: 悬架系统是汽车的车架与车桥或车轮之间的一切传力连接装置的总称。悬架系统基本构成有弹性元件(各类弹簧,缓冲作用);减震元件(减震器,减震作用);导向机构(控制臂等…

技术分享:如何用pytest_addoption切换自动化测试环境?

前言 在目前互联网公司中&#xff0c;都会存在多个测试环境&#xff0c;那么当我们编写的自动化想要在多套测试环境下进行运行时&#xff0c;如何使用&#xff1f; 大多数人想到的可能是通过将我们自动化代码中的地址修改成不同环境。 但是这时候就会增加一些工作量&#xf…

智安网络|移动安全的转型:零信任如何重新定义格局

数字化转型和远程/移动办公的常态化已经成为许多企业的现实。这一转变为企业带来了许多便利&#xff0c;但同时也引入了前所未有的风险&#xff0c;涉及员工的隐私、个人身份和特权访问凭证。尤其是在经济衰退和疫情的持续影响下&#xff0c;许多企业不得不在提高生产力的同时面…

260道网络安全工程师面试题汇总(附答题解析+配套资料)

由于我之前写了不少网络安全技术相关的文章和回答&#xff0c;不少读者朋友知道我是从事网络安全相关的工作&#xff0c;于是经常有人私信问我&#xff1a; 我刚入门网络安全&#xff0c;该怎么学&#xff1f; 想找网络安全工作&#xff0c;应该要怎么进行技术面试准备&…

Linux gdb汇编调试

文章目录 一、示例代码二、gdb汇编指令2.1 step/stepi2.2 next/nexti2.3 info registers2.4 set2.5 x2.6 rip寄存器2.7 rip 寄存器 参考资料 一、示例代码 &#xff08;1&#xff09; #include <stdio.h>int add(int a, int b) {return a b; }int main() {int a 3;in…

Linux安装或者升级cmake,例子为v3.10.2升级到v3.25.0(自己指定版本)

1. 引言 系统: ubuntu 1804 64位点我进入清华源-GCC链接: https://cmake.org/files/v3.25/ 先卸载自己电脑上的cmake。 cmake -version sudo apt-get autoremove cmake2. 下载编译指定版本cmake 可以根据自己的需求下载对应版本的cmake。 cd ~ && mkdir cmake &…

3Ds max图文教程:高精度篮球3D建模

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 第 1 步。使用以下设置在顶部视口上创建球体&#xff1a; 第 2 步。将球体转换为可编辑的多边形&#xff1a; 第 3 步。转到 Edge 子对象级别并剪切以下边缘&#xff1a; 第 4 步。选择以下边&#xff0c;然…

农产品后台管理系统(一)——项目总览

后端技术栈 SpringBoot2.xMybatis-plusMysql8.0redisjsoup&#xff08;测试爬取数据&#xff09; 前端技术栈 Vue3EchartsAxios前端组件&#xff1a;element-china-area-data、element-plus 项目概览截图 登录界面 注册界面 农产品发布界面 用户管理界面 用户画像界面 订单…

【JavaEE】Tomcat的安装和使用、创建Mevan项目使用Servlet写一个程序

目录 前言 一、Tomcat的下载和安装 二、写一个简单的Servlet项目 1、创建一个Maven项目 2、引入依赖 3、创建目录 4、编写Servlet代码。 5、打包程序 6、将程序部署到Tomcat上 7、验证程序运行结果 三、在IDEA上安装Smart Tomcat插件 四、Servlet中的一些常见错误 …

欧姆龙plc之间的以太网通讯

捷米特JM-ETH-CP转以太网模块控&#xff0c;用于欧姆龙 CP1L/ CP1E/ CP1H 系列 PLC 的以太网数据采集&#xff0c;非常方便构建生产管理系统。 支持 FINS/UDP、FINS/TCP 以太网协议通信&#xff0c;支持上位机软件&#xff08;组态王、MCGS、力控、KepWare OPC 服务器等&#…

怎么把pdf转换成jpg图片?这几种方法纯干货

怎么把pdf转换成jpg图片&#xff1f;PDF和JPG是两种常见的文件格式&#xff0c;它们各自有自己的应用场景和优势。PDF格式通常用于创建和共享文档&#xff0c;因为它可以保留文档中的格式和布局&#xff0c;而且可以在几乎所有设备上查看。JPG格式通常用于存储和共享图像文件&a…

5款实用的软件,能帮你解决一些工作和生活中的问题

​ 我乐于分享一些有用的软件给大家&#xff0c;让大家的工作更高效。今天&#xff0c;我要向大家推荐五款实用的软件&#xff0c;它们都能帮你解决一些工作和生活中的问题。 桌面整理——蜂窝桌面整理 ​ 蜂窝桌面整理是一个桌面整理软件&#xff0c;可以让你在桌面上创建和…

OpenCV for Python 学习第四天 :通道的获取与合并

上一篇博客&#xff0c;我们学习了如何通过更快的item()和itemset()的方法访问图片&#xff0c;以及了解了图像的兴趣位置的获取方法&#xff0c;那么今天&#xff0c;我们将学习通道的处理方法&#xff0c;通过通道的拆分和合并的实例&#xff0c;让大家更好的了解咱们有关于B…

论文解读:LaMa:Resolution-robust Large Mask Inpainting with Fourier Convolutions

论文&#xff1a;https://arxiv.org/pdf/2109.07161.pdf 代码&#xff1a; GitHub - advimman/lama: &#x1f999; LaMa Image Inpainting, Resolution-robust Large Mask Inpainting with Fourier Convolutions, WACV 2022 目录 1 摘要&#xff1a; 2 主要贡献&#xff…

Bland-Altman LOA:衡量测量方法一致性的统计分析方法,也可用来做分割评价指标

Bland-Altman LOA&#xff08;Limits of Agreement&#xff09;是一种用于评估两种测量方法一致性的常用统计分析方法。 在医学研究和临床实践中&#xff0c;我们经常会面临不同测量方法之间的比较和评估问题。为了确定两种测量方法是否能够得出相似的结果&#xff0c;我们需要…

D. Accommodation

Problem - 1804D - Codeforces 思路&#xff1a;首先我们可以统计一下开着灯的个数&#xff0c;对于一个卧室的来说&#xff0c; 不会存在让灯减少的情况&#xff0c;而对于两个卧室的来说&#xff0c;存在4种情况&#xff0c;00&#xff0c;01&#xff0c;10&#xff0c;11&am…

hosts文件锁定,如何修改hosts文件权限

hosts文件能够让我们配置域名和IP的映射关系&#xff0c;方便局域网的用户&#xff0c;可是有的用户想在hosts里添加修改&#xff0c;发现host文件被锁死&#xff0c;host文件左下角出现一个&#x1f512;&#xff0c;怎么办呢&#xff0c;如何修改hosts文件权限。 应用程序-实…