《TCP/IP网络编程》学习笔记 | Chapter 2:套接字类型与协议设置

《TCP/IP网络编程》学习笔记 | Chapter 2:套接字类型与协议设置

  • 《TCP/IP网络编程》学习笔记 | Chapter 2:套接字类型与协议设置
    • 套接字协议及其数据传输特性
      • 协议(Protocol)
      • 创建套接字
      • 协议族(Protocol Family)
      • 套接字类型(Type)
        • 套接字类型1:面向连接的套接字(SOCK_STREAM)
        • 套接字类型2:面向消息的套接字(SOCK_DGRAM)
      • 协议的最终选择
    • 面向连接的套接字:TCP套接字示例
    • Windows平台下的实现及验证
      • Windows操作系统的socket函数
      • 基于Windows的TCP套接字示例
    • 习题
      • (1)什么是协议?在收发数据中定义协议有何意义?
      • (2)面向连接的TCP套接字传输特性有3点,请分别说明。
      • (3)下面哪些是面向消息的套接字的特性?
      • (4)下列数据适合用哪些套接字传输?并给出原因。
      • (5)何种类型的套接字不存在数据边界?这类套接字接收数据时需要注意什么?
      • (6)tcp_server.c和tcp_client.c中需要多次调用read函数读取服务器调用1次write函数传递的字符串。更改程序,使服务器端多次调用(次数自拟)write函数传输数据,客户端调用1次read函数进行读取。为达到这一目的,客户端需延迟调用read函数,因为客户端要等待服务器端传输所有数据。

《TCP/IP网络编程》学习笔记 | Chapter 2:套接字类型与协议设置

套接字协议及其数据传输特性

协议(Protocol)

协议就是一种规则,是为了完成数据交换而定的约定。

创建套接字

#include <sys/socket.h>int socket(int domain, int type, int protocol);

成功返回文件描述符,否则-1。

参数:

  • domain:使用的协议族信息
  • type:数据传输类型信息
  • protocol:通信时用的协议信息

协议族(Protocol Family)

头文件sys/socket.h中声明的协议族:

名称协议族
PF_INETIPV4互联网协议族
PF_INET6IPV6互联网协议族
PF_LOCAL本地通信的UNIX协议族
PF_PACKET底层套接字的协议族
PF_IPXIPX Novell的协议族

套接字中,实际采用的最终协议信息是通过第三个参数传递的。但是第一个参数决定着第三个参数。

套接字类型(Type)

套接字类型1:面向连接的套接字(SOCK_STREAM)

特征:

  • 传输过程中数据不会消失
  • 按序传输数据
  • 传输的数据不存在数据边界(Boundary)

如何理解数据边界呢?

比如:计算机通过三次write传输了100字节的数据,但是接收数据的计算机仅通过一次read就接收了100字节的数据。

因为收发数据的套接字内部由缓冲(一个字节数组),因此数据也保存到该数组中。因此只要不超过数组容量,read函数和write函数调用次数没什么意义。

即使缓冲已满,也不代表数据丢失。如果read比接受数据的速度慢,则有可能会被填满。此时无法接受数据,但不意味着丢失,因为传输套接字将会停止传输。即面向连接的套接字会根据接收端的状态传输数据,如果传输出错还会重传。因此除特殊情况外,面向连接的套接字不会发生数据丢失。

需要注意:套接字必须一一对应。面向连接的套接字只能与另外一个同样特性的套接字连接。

总的来说,面向连接的套接字是:可靠的、按序传递的、基于字节的面向连接的数据传输方式的套接字。

套接字类型2:面向消息的套接字(SOCK_DGRAM)

特征:

  • 强调快速传输而非传输顺序
  • 传输的数据可能丢失也可能损毁
  • 传输的数据有数据边界
  • 限制每次传输的数据大小

总结:不可靠的、不按序传递的、以数据的高速传输为目的的套接字。

协议的最终选择

第三个参数决定最终采用的协议。尽管我们已经通过第一个参数设置协议族信息,第二个参数设置套接字数据传输方式,但是大家要知道:同一协议族中存在多个数据传输方式相同的协议。

所以数据传输方式相同,但是协议不同。

基于IPv4和面向连接,只有IPPROTO_TCP协议满足。该套接字称为TCP套接字。

int tcp_socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

SOCK_DGRAM指的是面向消息的数据传输方式,满足上述条件的协议只有IPPROTO_UDP这种套接字称为UDP套接字。

int udp_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);

面向连接的套接字:TCP套接字示例

服务端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>void error_handling(char* message);int main(int argc,char* argv[])
{int sock;struct sockaddr_in serv_addr;char message[30];int str_len;if(argc!=3){printf("Usage:%s <IP> <port>\n",argv[0]);exit(1);}sock = socket(PF_INET,SOCK_STREAM,0);if(sock==-1){error_handling("socket() error!");}memset(&serv_addr,0,sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(argv[1]);serv_addr.sin_port=htons(atoi(argv[2]));if(connect(sock,(struct sockaddr*)&serv_addr,sizeof(serv_addr))==-1){error_handling("connect() error!");}str_len = read(sock,message,sizeof(message)-1);if(str_len==-1){error_handling("read() error!");}printf("Message from server:%s \n",message);close(sock);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>void error_handling(char *message);int main (int argc, char *argv[])
{int sock;struct sockaddr_in serv_addr;char message[30];int str_len;int idx = 0, read_len = 0;if(argc != 3){printf("Usage: %s <IP> <port> \n", argv[0]);exit(1);}//若前两个参数是PF_INET,SOCK_STREAM则可省略第三个参数sock = socket(PF_INET, SOCK_STREAM, 0);if(sock == -1)error_handling("socket() error");memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_addr.s_addr = inet_addr(argv[1]);serv_addr.sin_port = htons(atoi(argv[2]));if(connect(sock,(struct sockaddr*) &serv_addr,sizeof(serv_addr)) == -1)error_handling("connect() error");
//此处每次读取一个字节。while(read_len = read(sock, &message[idx++], 1)){if(read_len == -1)error_handling("read() error");str_len += read_len;}printf("Message from server : %s \n", message);printf("Function read call count: %d \n",str_len);close(sock);return 0;
}void error_handling(char *message)
{fputs(message, stderr);fputc('\n',stderr);exit(1);
}

从运行结果可以看出,服务端发送了13字节的数据,客户端调用了13次read函数进行读取。由此验证TCP套接字的特性:不存在数据边界。

Windows平台下的实现及验证

因为套接字类型及传输特性与操作系统无关,所以只需要了解返回类型即可。

Windows操作系统的socket函数

Windows和Linux的函数名都相同,只是返回值不同。下面给出声明:

#inclde <winsock2.h>SOCKET socket(int af, int type, int protocol);

成功返回句柄,否则返回INVALID_SOCKET(实质上是-1)。

基于Windows的TCP套接字示例

服务端:

#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>void ErrorHanding(char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char *argv[])
{WSADATA wsaData;SOCKET hServerSock, hClientSock;SOCKADDR_IN serverAddr, clientAddr;int szClientAddr;char message[] = "Hello World!";if (argc != 2){printf("Usage: %s <port>\n", argv[0]);exit(1);}if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)ErrorHanding("WSAStartup() 	error!");hServerSock = socket(PF_INET, SOCK_STREAM, 0);if (hServerSock == INVALID_SOCKET)ErrorHanding("socket() 	error!");memset(&serverAddr, 0, sizeof(serverAddr));serverAddr.sin_family = AF_INET;serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);serverAddr.sin_port = htons(atoi(argv[1]));if (bind(hServerSock, (SOCKADDR *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)ErrorHanding("bind() error!");if (listen(hServerSock, 5) == SOCKET_ERROR)ErrorHanding("listen() error!");szClientAddr = sizeof(clientAddr);hClientSock = accept(hServerSock, (SOCKADDR *)&clientAddr, &szClientAddr);if (hClientSock == INVALID_SOCKET)ErrorHanding("accept() error!");send(hClientSock, message, sizeof(message), 0);closesocket(hClientSock);closesocket(hServerSock);WSACleanup();return 0;
}

客户端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>void error_handling(const char *message);int main(int argc, char *argv[])
{WSADATA wsaData;SOCKET hSocket;SOCKADDR_IN serverAddr;char message[30];int strLen = 0;int idx = 0, readLen = 0;if (argc != 3){printf("Usage: %s <IP> <port>\n", argv[0]);exit(1);}if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)error_handling("WSAStartup() error");hSocket = socket(PF_INET, SOCK_STREAM, 0);if (hSocket == INVALID_SOCKET)error_handling("hSocket() error");memset(&serverAddr, 0, sizeof(serverAddr));serverAddr.sin_family = AF_INET;serverAddr.sin_addr.s_addr = inet_addr(argv[1]);serverAddr.sin_port = htons(atoi(argv[2]));if (connect(hSocket, (SOCKADDR *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)error_handling("connect() error");while (readLen = recv(hSocket, &message[idx++], 1, 0)){if (readLen == -1)error_handling("read() error");strLen += readLen;if (message[idx - 1] == '\0')break;}printf("Message from server: %s\n", message);printf("Function read call count: %d\n", strLen);closesocket(hSocket);WSACleanup();return 0;
}void error_handling(const char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}

编译:

gcc tcp_server_win.c -lwsock32 -o hServerWingcc tcp_client_win.c -lwsock32 -o hClientWin

运行结果:

在这里插入图片描述

在这里插入图片描述

习题

(1)什么是协议?在收发数据中定义协议有何意义?

协议就是为了完成数据交换而定好的规则。

因此,定义协议意味着对数据传输所必需的的规则进行定义。

(2)面向连接的TCP套接字传输特性有3点,请分别说明。

  • 可靠的:传输过程中数据不会丢失
  • 按序传递的:按序传输数据
  • 基于字节的:传输的数据不存在数据边界

(3)下面哪些是面向消息的套接字的特性?

a. 传输数据可能丢失
b. 没有数据边界
c. 以快速传递为目的
d. 不限制每次传递数据的大小
e. 与面向连接的套接字不同,不存在连接的概念

答:a、c、e。

(4)下列数据适合用哪些套接字传输?并给出原因。

a. 演唱会现场直播的多媒体数据
b. 某人压缩过的文本文件
c. 网上银行用户与银行之间的数据传递

答:

a. UDP。演唱会讲究实时性,稍微丢包也能够有算法补救(顶多画质下降),所以用UDP更好。
b. TCP。文本文件讲究可靠传输,所以用TCP。
c. TCP。支付这种敏感数据更需要可靠传输,所以用TCP。

(5)何种类型的套接字不存在数据边界?这类套接字接收数据时需要注意什么?

面向连接的TCP套接字不存在数据边界。因此输入输出函数的响应次数不具有意义。

重要的不是函数的响应次数,而是数据的收发量。需要保证在接收套接字的缓冲区填充满之前就从buffer里读取数据。也就是,在接收套接字内部,写入buffer的速度要小于读出buffer的速度。

(6)tcp_server.c和tcp_client.c中需要多次调用read函数读取服务器调用1次write函数传递的字符串。更改程序,使服务器端多次调用(次数自拟)write函数传输数据,客户端调用1次read函数进行读取。为达到这一目的,客户端需延迟调用read函数,因为客户端要等待服务器端传输所有数据。

服务端:

#include <stdio.h>
#include <stdlib.h>
#include <winsock2.h>void error_handling(const char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char *argv[])
{WSADATA wsaData;SOCKET hServerSock, hClientSock;SOCKADDR_IN serverAddr, clientAddr;int szClientAddr;char message[] = "Hello World!";if (argc != 2){printf("Usage: %s <port>\n", argv[0]);exit(1);}if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)error_handling("WSAStartup() error!");hServerSock = socket(PF_INET, SOCK_STREAM, 0);if (hServerSock == INVALID_SOCKET)error_handling("socket() error!");memset(&serverAddr, 0, sizeof(serverAddr));serverAddr.sin_family = AF_INET;serverAddr.sin_addr.s_addr = htonl(INADDR_ANY);serverAddr.sin_port = htons(atoi(argv[1]));if (bind(hServerSock, (SOCKADDR *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)error_handling("bind() error!");if (listen(hServerSock, 5) == SOCKET_ERROR)error_handling("listen() error!");szClientAddr = sizeof(clientAddr);hClientSock = accept(hServerSock, (SOCKADDR *)&clientAddr, &szClientAddr);if (hClientSock == INVALID_SOCKET)error_handling("accept() error!");send(hClientSock, message, 4, 0);send(hClientSock, message + 4, 4, 0);send(hClientSock, message + 8, 4, 0);send(hClientSock, message + 12, sizeof(message - 12), 0);closesocket(hClientSock);closesocket(hServerSock);WSACleanup();return 0;
}

客户端:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>void error_handling(const char *message)
{fputs(message, stderr);fputc('\n', stderr);exit(1);
}int main(int argc, char *argv[])
{WSADATA wsaData;SOCKET hSocket;SOCKADDR_IN serverAddr;char message[30];if (argc != 3){printf("Usage: %s <IP> <port>\n", argv[0]);exit(1);}if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)error_handling("WSAStartup() error!");hSocket = socket(PF_INET, SOCK_STREAM, 0);if (hSocket == INVALID_SOCKET)error_handling("hSocket() error!");memset(&serverAddr, 0, sizeof(serverAddr));serverAddr.sin_family = AF_INET;serverAddr.sin_addr.s_addr = inet_addr(argv[1]);serverAddr.sin_port = htons(atoi(argv[2]));if (connect(hSocket, (SOCKADDR *)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)error_handling("connect() error!");// Busy Waitingfor (int i = 0; i < 3000; i++)printf("Wait time %d\n", i);recv(hSocket, message, sizeof(message) - 1, 0);printf("Message from server: %s\n", message);closesocket(hSocket);WSACleanup();return 0;
}

运行结果:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小林渗透入门:burpsuite+proxifier抓取小程序流量

目录 前提&#xff1a; 代理&#xff1a; proxifier&#xff1a; 步骤&#xff1a; bp证书安装 bp设置代理端口&#xff1a; proxifier设置规则&#xff1a; proxifier应用规则&#xff1a; 结果&#xff1a; 前提&#xff1a; 在介绍这两个工具具体实现方法之前&#xff0…

举重场景哑铃图像分割系统:全面改进提升

举重场景哑铃图像分割系统源码&#xff06;数据集分享 [yolov8-seg-GhostHGNetV2&#xff06;yolov8-seg-EfficientHead等50全套改进创新点发刊_一键训练教程_Web前端展示] 1.研究背景与意义 项目参考ILSVRC ImageNet Large Scale Visual Recognition Challenge 项目来源AA…

【机器学习】连续属性离散化与sklearn.preprocessing.KBinsDiscretizer

1. KBinsDiscretizer的定义 KBinsDiscretizer是 scikit-learn 库中的一个类&#xff0c;用于将连续数据离散化成区间&#xff08;bins&#xff09;。这个类通过将特征值分配到 k 个等宽的区间&#xff08;bins&#xff09;来实现离散化&#xff0c;并且可以配置不同的编码方式…

OpenGL入门002——顶点着色器和片段着色器

文章目录 一些概念坐标转换阶段顶点着色器片段着色器VBOVAO 实战简介main.cppCMakeLists.txt最终效果 一些概念 坐标转换阶段 概述&#xff1a; 模型空间、世界空间、视图空间和裁剪空间是对象在3D场景中经历的不同坐标变换阶段。每个空间对应渲染管道的一个步骤&#xff0c;…

使用 Elastic、OpenLLMetry 和 OpenTelemetry 跟踪 LangChain 应用程序

作者&#xff1a;来自 Elastic Bahubali Shetti Langchain 应用程序的使用正在增长。构建基于 RAG 的应用程序、简单的 AI 助手等的能力正在成为常态。观察这些应用程序更加困难。考虑到现有的各种选项&#xff0c;本博客展示了如何将 OpenTelemetry 检测与 OpenLLMetry 结合使…

【Linux】从零开始使用多路转接IO --- select

碌碌无为&#xff0c;则余生太长&#xff1b; 欲有所为&#xff0c;则人生苦短。 --- 中岛敦 《山月记》--- 从零开始认识五种IO模型 1 前言2 认识多路转接select3 多路转接select等待连接4 完善代码5 总结 1 前言 上一篇文章我们讲解了五种IO模型的基本概念&#xff0c;并…

客户端与微服务之间的桥梁---网关

当我们创建好了N多个微服务或者微服务的实例之后&#xff0c;每个服务暴露出不同的端口地址&#xff0c;一般对于客户端请求&#xff0c;只需要请求一个端口&#xff0c;要隔离客户端和微服务的直接关系&#xff0c;保证微服务的安全性和灵活性&#xff0c;避免敏感信息的泄露。…

docker部署nginx+nacos+redis+java镜像和容器

nginx镜像制作 Dockerfile内容&#xff1a; # 基础镜像 FROM nginx # author MAINTAINER ruoyi# 挂载目录 VOLUME /home/ruoyi/projects/ruoyi-ui # 创建目录 RUN mkdir -p /home/ruoyi/projects/ruoyi-ui # 指定路径 WORKDIR /home/ruoyi/projects/ruoyi-ui # 复制conf文件到路…

MiniWord

1.nuget 下载配置 2.引用 3. var value = new Dictionary<string, object>() { ["nianfen"] = nianfen, ["yuefen"] = yuefen, ["yuefenjian1"] = (int.Par…

SpringBoot篇(简化操作的原理)

目录 一、代码位置 二、统一版本管理&#xff08;parent&#xff09; 三、提供 starter简化 Maven 配置 四、自动配置 Spring&#xff08;引导类&#xff09; 五、嵌入式 servlet 容器 一、代码位置 二、统一版本管理&#xff08;parent&#xff09; SpringBoot项目都会继…

Jetson OrinNX平台CSI相机导致cpu load average升高问题调试

1. 前言 硬件: Orin NX JP: 5.1.2, R35.4.1 用v4l2-ctl --stream-mmap -d0 命令去获取相机数据时, 用top查看cpu使用情况, CPU占用率很低,但load average在1左右, 无任何程序运行时,load average 为0 用ps -aux 查看当前进程情况,发现有两个系统进程vi-output, …

Iceoryx2:高性能进程间通信框架(中间件)

文章目录 0. 引言1. 主要改进2. Iceoryx2 的架构3. C示例代码3.1 发布者示例&#xff08;publisher.cpp&#xff09;3.2 订阅者示例&#xff08;subscriber.cpp&#xff09; 4. 机制比较5. 架构比较6. Iceoryx vs Iceoryx2参考资料 0. 引言 Iceoryx2 是一个基于 Rust 实现的开…

获取Hive表备注

DESCRIBE EXTENDED 表名;先获取Detailed Table Information这行的data_type字段数据&#xff0c;进行正则匹配&#xff0c;拿到表备注&#xff0c;如下&#xff1a; String str ReUtil.get("parameters:\\{(?!.*?\\().*transient_lastDdlTime.*?comment(.*?)\\}&quo…

OTFS的基本原理(通俗易懂)

一、OTFS调制解调原理 Orthogonal Time Frequency Space Modulation | IEEE Conference Publication | IEEE Xplore OTFS原论文提出的方法可以概括为 可以概括为 Xdd (延迟多普勒域数据) ----> ISFFT(辛有限傅里叶变换) ---->海森堡变换 延迟多普勒域数据 …

数据结构与算法——Java实现 53.力扣938题——二叉搜索树的范围和

生命的意义 在于活出自我 而不是成为别人眼中的你 —— 24.11.3 938. 二叉搜索树的范围和 给定二叉搜索树的根结点 root&#xff0c;返回值位于范围 [low, high] 之间的所有结点的值的和。 示例 1&#xff1a; 输入&#xff1a;root [10,5,15,3,7,null,18], low 7, high 15 …

顺德自闭症全托管学校:专业照顾,细心呵护

在顺德及周边地区&#xff0c;寻找一所能够为自闭症儿童提供专业照顾与细心呵护的全托管学校&#xff0c;是许多家庭的迫切需求。自闭症儿童在社交、语言和行为上所面临的挑战&#xff0c;需要更为专业的教育环境和细致入微的关怀。而位于广州的星贝育园自闭症儿童寄宿制学校&a…

【react】基础知识点学习

1. 创建项目 npm install -g create-react-app npx create-react-app my-app cd my-app npm startindex.js为入口文件&#xff0c;App.js为根组件。 如何将react应用挂载在页面上&#xff1f; 将App组件渲染到id为root的DOM元素中 2. JSX JSX是|avaScript和XML(HTML)的缩写…

基于 JAVASSM 框架沙县小吃点餐系统

基于 JAVASSM 框架&#xff08;即 Java Spring Spring MVC MyBatis&#xff09;开发一个沙县小吃点餐系统。 步骤一&#xff1a;需求分析 明确系统需要实现的功能&#xff0c;比如&#xff1a; 用户注册和登录浏览菜单添加菜品到购物车下单并支付订单管理后台管理&#…

apt的编译安装(古老通讯)

Ubuntu系统的防火墙关闭&#xff1a; ufw disable 第一步&#xff1a;Ubuntu 安装依赖环境 apt -y install libpcre3-dev zlib1g-dev libssl-dev build-essential 如果出现无法下载则在末尾处假如 --fix missing如下图所示 出现下图则为安装成功 第二步&#xff1a; useradd…

基于微信小程序的校园失物招领系统的研究与实现(V4.0)

博主介绍&#xff1a;✌stormjun、8年大厂程序员经历。全网粉丝15w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&…