【网络】网络基础知识(协议、mac、ip、套接字)

文章目录

  • 1. 计算机网络的背景
  • 2. 认识网络协议
    • 2.1 协议分层
    • 2.2 OS与网络的关系
  • 3. 网络传输基本流程
    • 3.1 局域网通信流程
    • 3.2 跨网络通信流程
  • 4. Socket 编程预备
    • 4.1 理解源IP地址和目的IP地址
    • 4.2 端口号与Socket
    • 4.3传输层的典型代表
    • 4.4 网络字节序
  • 5. socket 编程接口
    • 5.1 介绍
    • 5.2 案例演示

在这里插入图片描述

1. 计算机网络的背景

在这里插入图片描述
所谓的 “局域网” 和 “广域网” 只是一个相对的概念。

2. 认识网络协议

我们都知道,协议就是“双方”都必须遵守的约定。

那什么叫网络协议呢?

计算机生产厂商有很多,计算机操作系统也有很多,计算机网络硬件设备还是有很多。
如何让这些不同厂商之间生产的计算机能够相互顺畅的通信呢?就需要有人站出来,约定一个共同的标准,大家都来遵守,这就是网络协议

无论是电脑、手机还是其他设备,只要它们遵循相同的网络协议,就能在网络上互相通信、交换数据。

2.1 协议分层

协议本质也是软件,在设计上为了更好的进行模块化、解耦合,也是被设计成为层状结构的。

在现实生活中,我们可以通过电话直接和朋友交流,因此我们认为“同层协议可以直接通信”;但本质上,同层协议之间没有直接通信,而是各自使用下层结构的能力,完成通信的,我们本质上是在对电话讲话。

在这里插入图片描述

通过上图我们发现,任何一层的变化,只要遵守协议,都不会影响其它层,所以分层可以实现解耦合,让软件维护的成本更低。

  1. OSI的七层模型

在这里插入图片描述
很多书中都讲了7层不好,其实在网络角度, OSI 定的协议7层模型其实非常完善,但是在实际操作的过程中,会话层、表示层是不可能接入到操作系统中的所以在工程实践中,最终落地的是 5 层协议。

  1. TCP/IP 五层(或四层)模型

TCP/IP 是一组协议的代名词,它还包括许多协议,组成了 TCP/IP 协议族。
在这里插入图片描述

TCP/IP 通讯协议采用了 5 层的层级结构, 每一层都呼叫它的下一层所提供的网络来完成自己的需求。

  • 物理层:负责光/电信号的传递方式。比如现在以太网通用的网线(双绞 线)、 早 期以太网采用的的同轴电缆(现在主要用于有线电视)、 光纤,现在的 wifi 无线网使用 电磁波等都属于物理层的概念。 物理层的能力决定了最大传输速率、 传输距离、 抗 干扰性等。集线器(Hub)工作在物理层。
  • 数据链路层: 负责设备之间的数据帧的传送和识别。例如网卡设备的驱动、帧同步(就是说从网线上检测到什么信号算作新帧的开始)、 冲突检测(如果检测到冲突就 自动重发)、数据差错校验等工作。有以太网、令牌环网、无线 LAN 等标准。 交换机 (Switch)工作在数据链路层.
  • 网络层: 负责地址管理和路由选择。 例如在 IP 协议中, 通过IP 地址来标识一台 主机,,并通过路由表的方式规划出两台主机之间的数据传输的线路(路由)。路由器 (Router)工作在网路层.
  • 传输层: 负责两台主机之间的数据传输。如传输控制协议 (TCP), 能够确保数据 可靠的从源主机发送到目标主机.
  • 应用层: 负责应用程序间沟通, 如简单电子邮件传输(SMTP)、文件传输协 议(FTP)、 网络远程访问协议(Telnet) 等.

网络编程主要就是针对应用层

物理层我们考虑的比较少, 我们只考虑软件相关的内容,因此很多时候我们直接称为TCP/IP 四层模型。

  • 对于一台主机,它的操作系统内核实现了从传输层到物理层的内容
  • 对于一台路由器,它实现了从网络层到物理层
  • 对于一台交换机,它实现了从数据链路层到物理层
  • 对于集线器,它只实现了物理层

但是并不绝对,很多交换机也实现了网络层的转发;很多路由器也实现了部分传输层的内容(比如端口转发)。

  1. 重新理解协议

上面的内容,我们只是懂了一些基本概念,还是达不到我们的目标,下面我们再次重新理解协议和协议分层:

那么为什么要有 TCP/IP 协议?

  • 首先,即便是单机,你的计算机内部,其实都是存在协议的,比如:其他设备和内存通信,会有内存协议。 其他设备和磁盘通信,会有磁盘相关的协议,比如:SATA, IDE, SCSI 等。 只不过我们感知不到罢了。 而且这些协议都在本地主机各自的硬件中,通信的成本、问题比较少。
  • 其次,网络通信最大的特点就是主机之间变远了。 任何通信特征的变化,一定会带来新的问题,有问题就得解决问题,所以需要新的协议。

所以,协议产生的本质就是:解决因距离变远而产生的通信问题。
在这里插入图片描述
由于距离变远产生的问题多种多样,所以要将它们分类,即协议要分层。

2.2 OS与网络的关系

我们都清楚的知道,操作系统各式各样,没有标准,但是为什么不同的操作系统间能够通信呢?

那是因为不同的操作系统中,网络协议栈必须按照TCP/IP协议标准来实现,即不同操作系统使用的网络协议栈一样,那就可以通信了。
在这里插入图片描述

所以,操作系统与网络的关系是:操作系统内要实现网络相关的功能,即网络是操作系统的一个模块

那么到底什么是协议呢?

既然操作系统要实现网络的功能,那OS内就会存在大量的协议,OS就要管理协议,所以协议本质就是数据结构,即结构体
在这里插入图片描述

问题: 主机 B 能识别 data, 并且准确提取 a=10, b=20, c=30 吗? 回答: 答案是肯定的!
因为双方都有同样的结构体类型 struct protocol。 也就是说,用同样的代码实现协议,用同样的自定义数据类型,天然就具有”共识“, 能够识别对方发来的数据,这不就是约定吗?

关于协议的朴素理解: 所谓协议, 就是通信双方都认识的结构化的数据类型(结构体)

3. 网络传输基本流程

3.1 局域网通信流程

首先,两台主机在同一个局域网,是否能够直接通信? - - 是的,一台主机发送的信息,在同一个局域网下的所有主机,都可以收到。

只不过在局域网上的每台主机,要有唯一的标识来保证主机的唯一性: mac 地址。

mac地址长度为 48 位,即 6 个字节。一般用 16 进制数字加上冒号的形式来表示(例如:08:00:27:03:fb:19), 在网卡出厂时就确定了,不能修改,mac 地址通常是唯一的。

在这里插入图片描述

以太网中,任何时刻,只允许一台机器向网络中发送数据

  • 如果有多台同时发送, 会发生数据干扰, 我们称之为数据碰撞

所以,以太网就是临界资源,是互斥访问的,但是不加锁,先用,碰撞出错了再说。

  • 所有发送数据的主机要进行碰撞检测碰撞避免(以保证数据读写的原子性)
  • 没有交换机的情况下,一个以太网就是一个碰撞域
  • 局域网通信的过程中,主机对收到的报文确认是否是发给自己的,是通过目标mac地址在数据链路层判定的,若不是,直接丢弃,上层用户感知不到。

在数据传输时,要经过每一层;其中每层都有协议,所以当我进行数据传输流程的时候,要进行封装和解包
在这里插入图片描述
报头部分(对应协议层的结构体字段)我们一般叫做报头;除了报头 剩下的叫做有效载荷,故报文 = 报头 + 有效载荷。

我们在明确一下不同层的完整报文的叫法,不同的协议层对数据包有不同的称谓

  • 在传输层叫做段(segment),
  • 在网络层叫做数据报 (datagram),
  • 在链路层叫做帧(frame).
  • 应用层数据通过协议栈发到网络上时,每层协议都要加上一个数据首部,称为封装。
    • 首部信息中包含了一些类似于首部有多长,载荷有多长,上层协议是什么等信息。
    • 数据封装成帧后发到传输介质上,到达目的主机后每层协议再剥掉相应的首部,根据首部中的 “上层协议字段” 将数据交给对应的上层协议处理。

从今天开始, 我们学习任何协议, 都要先宏观上建立这样的认识:

  1. 要学习的协议,是如何做到解包的? 只有明确了解包, 封包也就能理解
  2. 要学习的协议,是如何做到将自己的有效载荷, 交付给上层协议的?

3.2 跨网络通信流程

IP 地址是在 IP 协议中,用来标识网络中不同主机的地址。

  • 对于 IPv4 来说, IP 地址是一个 4 字节,32 位的整数;
  • 我们通常也使用 “点分十进制” 的字符串表示 IP 地址,例如 192.168.0.1;用点分割的每一个数字表示一个字节,范围是 0 - 255;

可是,你们又没有一个疑问:上面我们所说的mac地址也能唯一标识一个主机,但二者有什么区别呢?

在这里插入图片描述
唐僧每经过一个城池,都会询问国王下一个地方它去哪,它给国王说的是:我从长安来,到西天去。
国王根据唐僧的目的地,告诉唐僧距离当前位置最近的下一个城池。

所以:

  • IP地址是最终目标,用来标识整个网络中标识唯一性。
  • MAC地址是近期目标,用来标识主机在局域网中的唯一性。其受IP地址影响,根据路由器完成mac地址的切换(mac地址仅在局域网中有效)

跨网段的主机的数据传输,数据从一台计算机到另一台计算机传输过程中要经过一个或多个路由器。
在这里插入图片描述
为什么要去目标主机, 先要走路由器?
在这里插入图片描述
结合封装与解包,体现路由器解包和重新封装的特点
在这里插入图片描述
提炼 IP 网络的意义和网络通信的宏观流程

在这里插入图片描述

所以,IP网络层存在的意义:提供网络虚拟层,让世界的所有网络都是IP网络,屏蔽最底层网络的差异。

4. Socket 编程预备

4.1 理解源IP地址和目的IP地址

IP 在网络中,是用来标识主机的唯一性的,可以通过IP传输数据。

但是这里要思考一个问题: 数据传输到主机是目的吗? 不是的,因为数据是给人用的。

  • 比如: 聊天是人在聊天,下载是人在下载,浏览网页是人在浏览?
  • 但是人是怎么看到聊天信息的呢? 怎么执行下载任务呢? 怎么浏览网页信息呢?通过启动的 qq,迅雷,浏览器。
  • 而启动的 qq,迅雷,浏览器都是进程。 换句话说,进程是人在系统中的代表,只要把数据给进程,人就相当于就拿到了数据。
    在这里插入图片描述

所以: 数据传输到主机不是目的,而是手段。 到达主机内部,再交给主机内的进程,才是目的

那么,源IP地址和目的IP地址是干什么的呢?- - 解决两端主机的唯一性

但是系统(主机)中,同时会存在非常多的进程, 当数据到达目标主机之后,怎么转发给目标进程呢?

4.2 端口号与Socket

端口号(port)是传输层协议的内容。

  • 端口号是一个2字节16 位的整数
  • 端口号用来标识一个进程,告诉操作系统,当前的这个数据要交给哪一个进程来处理
  • 一个端口号只能被一个进程占用

端口号范围划分

  • 0 - 1023:知名端口号, HTTP、FTP、SSH 等这些广为使用的应用层协议,他们的端口号都是固定的。
  • 1024 - 65535:操作系统动态分配的端口号,客户端程序的端口号就是由操作系统从这个范围分配的

我们之前在学习系统编程的时候,学习了 pid 表示唯一一个进程;此处我们的端口号也是唯一表示一个进程,那么这两者之间是怎样的关系?

  • 进程 PID 属于系统概念,技术上也具有唯一性,确实可以用来标识唯一的一个进程, 但是这样做, 会让系统进程管理和网络强耦合,实际设计的时候, 并没有选择这样做。
  • 另外,一个进程可以绑定多个端口号, 但是一个端口号不能被多个进程绑定

传输层协议(TCP 和 UDP)的数据段中有两个端口号,分别叫做源端口号和目的端口号,就是在描述 "数据是谁发的,要发给谁";

那么, IP 地址 + 端口号就能够标识网络上的某一台主机的某一个进程,所以网络通信的本质是:进程间通信,网络就是它们看到的同一份资源,我们把IP 地址 + 端口号叫做Socket(套接字)

4.3传输层的典型代表

如果我们了解了系统,也了解了网络协议栈,我们就会清楚,传输层是属于内核的,那么我们要通过网络协议栈进行通信,必定调用的是传输层提供的系统调用,来进行的网络通信。
在这里插入图片描述

认识 TCP 协议,此处我们先对 TCP(Transmission Control Protocol 传输控制协议)有一个直观的认识

  • 传输层协议
  • 有连接
  • 可靠传输
  • 面向字节流

认识 UDP 协议,此处我们也是对 UDP(User Datagram Protocol 用户数据报协议)有一个直观的认识;

  • 传输层协议
  • 无连接
  • 不可靠传输
  • 面向数据报

4.4 网络字节序

我们知道,内存中的多字节数据相对于内存地址有大端和小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分,大端与小端机器通信,要怎么办呢

在这里插入图片描述所以,必须在网络中将大小端规定好!

  • 发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出;
  • 接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存;
  • 因此,网络数据流的地址应这样规定:先发出的数据是低地址(低权值位),后发出的数据是高地址(高权值位)
  • TCP/IP协议规定:网络数据流应采用大端字节序,即低地址处是高权值位。
  • 不管这台主机是大端机还是小端机,都会按照这个 TCP/IP 规定的网络字节序来发送/接收数据;
  • 如果当前发送主机是小端,就需要先将数据转成大端;否则就忽略,直接发送即可

因此,网络规定:所有发送到网络上的数据,都必须是大端的

为使网络程序具有可移植性,使同样的 C 代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。
在这里插入图片描述

这些函数名很好记,h表示host,n表示network,l 表示32位长整数(常用来转ip),s表示16位短整数(常用来转port)。

  • 例如 htonl表示将 32 位的长整数从主机字节序转换为网络字节序,例如将 IP 地 址转换后准备发送。
  • 如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回;
  • 如果主机是大端字节序,这些函数不做转换,将参数原封不动地返回

5. socket 编程接口

5.1 介绍

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器) 
int socket(int domain, int type, int protocol);// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);// 开始监听 socket (TCP, 服务器) 
int listen(int socket, int backlog);// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

在上述接口中,很多都有一个 sockaddr 的结构体,这是因为套接字的种类有很多:网络socket、本地socket(域间socket)、原始socket。

OS为了提供一个统一的系统调用接口,设计了sockaddr 的结构体
在这里插入图片描述
用户具体使用什么类型的socket不管,只要你传进来的指针是sockaddr类型的;OS内部为了区分是网络还是本地,在这三个结构中,大家的前两个字节用来标识地址的类型 (这在C++中不就是继承与多态嘛)

5.2 案例演示

下面使用一个简单的回显服务器和客户端代码,演示一些UDP中常用的接口

服务器端:

  1. 创建套接字

在这里插入图片描述
该系统调用的返回值是一个文件描述符,所以创建socket本质是创建了一个文件。

// 1. 创建UDP网络socket
int _socket = ::socket(AF_INET, SOCK_DGRAM, 0);
  1. 绑定端口号

在绑定socket之前,应初始化一下socket;但在上面我们说了,socket的种类很多,我们这里使用网络socket为例,所以应该先创建一个sockaddr_in类型的socket。
在这里插入图片描述
在初始化sockaddr_in之前,需要注意port与IP的网络字节序问题

在这里插入图片描述

struct sockaddr_in inetSocket;
bzero(&inetSocket,sizeof(inetSocket));  //sockaddr_in全部清零,也可用memset
inetSocket.sin_family = AF_INET;
inetSocket.sin_port = ::htons(_port);        //端口-->网络字节序
inetSocket.sin_addr.s_addr = ::inet_addr(_ip.c_str());   //string -->4字节-->网络字节序

绑定

在这里插入图片描述

// 3. 设置套接字进入内核
int n = ::bind(_socket,(sockaddr*)&inetSocket,sizeof(inetSocket));
  1. 接收信息

在这里插入图片描述

struct sockaddr_in fromUser;
socklen_t len = sizeof(fromUser);
char buffer[1024];
int n = ::recvfrom(_socket,&buffer,sizeof(buffer),0,(sockaddr*)(&fromUser),&len);
  1. 发送信息给指定socket

在这里插入图片描述

::sendto(_socket,echoString.c_str(),echoString.size(),0,(sockaddr*)(&fromUser),len);

Server完整代码:

//Common.hpp
#pragma once
#include <stdlib.h>     //exit#define Die(code)   \do              \{               \exit(code); \} while (0)#define CONVERSE(src) (struct sockaddr*)(src)enum exitCode
{SOCKET_ERR = 1,BIND_ERR
};
#pragma once#include <memory>
#include <string>
#include <cstring>#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "Log.hpp"
#include "Common.hpp"const static int g_socket = -1;
const static int g_port = 8080;
const static std::string g_default_ip = "127.0.0.1";using namespace MyLogModule;
class UdpServer
{
public:UdpServer(uint16_t port = g_port, std::string ip = g_default_ip): _socket(g_socket), _port(port), _ip(ip), _isRunning(false){}~UdpServer(){}void Init(){// 1. 创建网络socket_socket = ::socket(AF_INET, SOCK_DGRAM, 0);if (_socket == -1){LOG(FATAL) << "socket create fail";Die(SOCKET_ERR);}LOG(NORMAL) << "file fd:" << _socket;// 2. 向套接字中填充网络信息struct sockaddr_in inetSocket;bzero(&inetSocket, sizeof(inetSocket)); // sockaddr_in全部清零,也可用memsetinetSocket.sin_family = AF_INET;inetSocket.sin_port = ::htons(_port);                  // 端口-->网络字节序inetSocket.sin_addr.s_addr = ::inet_addr(_ip.c_str()); // string -->4字节-->网络字节序// 3. 设置套接字进入内核int n = ::bind(_socket, CONVERSE(&inetSocket), sizeof(inetSocket));if (n == -1){LOG(FATAL) << "bind fail";Die(BIND_ERR);}LOG(NORMAL) << "bind success";}void Start(){if (!_isRunning){_isRunning = true;while (true){struct sockaddr_in fromUser;socklen_t len = sizeof(fromUser);char buffer[1024];int n = ::recvfrom(_socket, &buffer, sizeof(buffer), 0, CONVERSE(&fromUser), &len);if (n > 0){buffer[n] = 0;// 接收成功,显示消息内容,谁发来的uint16_t clientPort = ::ntohs(fromUser.sin_port);      // 网络字节序-->端口std::string clientIp = ::inet_ntoa(fromUser.sin_addr); // 网络字节序-->4字节-->字符串std::string message = std::to_string(clientPort) + ":" + clientIp + "#:";message += buffer;LOG(DEBUG) << message;std::string echoString = "echo say#";echoString += buffer;// 将消息在回显给发送方::sendto(_socket, echoString.c_str(), echoString.size(), 0, CONVERSE(&fromUser), len);}}}_isRunning = false;}private:int _socket;int16_t _port;   // 端口std::string _ip; // ip地址bool _isRunning;
};

客户端

#ifndef __UpdClient__hpp__
#define __UpdClient__hpp__#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include "Log.hpp"#include <memory>
#include <string>using namespace MyLogModule;class UdpClient
{
public:UdpClient(std::string ip, std::string port){_port = stoi(port.c_str());_ip = ip;}~UdpClient(){}void Start(){_socket = ::socket(AF_INET, SOCK_DGRAM, 0);if (_socket == -1){LOG(FATAL) << "socket create fail";Die(SOCKET_ERR);}// 转换为网络字节序,并设置struct sockaddr_in server;server.sin_family = AF_INET;server.sin_addr.s_addr = ::inet_addr(_ip.c_str());server.sin_port = ::htons(_port);while(true){std::string message;std::cout << "Please enter# ";std::getline(std::cin,message);//客户端也要有自己的ip和port,但不需要绑定,因为OS会自动bind//服务端要显示的bind,是因为服务器的端口号,必须稳定!必须是众所周知且不能改变轻易改变的!::sendto(_socket,message.c_str(),message.size(),0,CONVERSE(&server),sizeof(server));char buffer[1024];//虽然上面已经有了sockaddr_in server,但是可能不只有一个服务器哦struct sockaddr_in tmp;socklen_t len = sizeof(tmp);int n = ::recvfrom(_socket,buffer,sizeof(buffer)-1,0,CONVERSE(&tmp),&len);if(n > 0){buffer[n] = 0;//打印服务器回写的std::cout << buffer << std::endl;}}}private:int _socket;uint16_t _port;std::string _ip;
};#endif

clientMain.cc

#include "Common.hpp"
#include "UdpClient.hpp"int main(int argc,char* argv[])
{if(argc != 3){std::cerr << "usage fail " << "Usage:" << argv[0] << "serverIp serverPort" << std::endl;Die(USE_ERR);}//使用ip与port构建UdpClient对象的指针std::shared_ptr<UdpClient> c_ptr = std::make_shared<UdpClient>(argv[1],argv[2]);c_ptr->Start();return 0;
}

至此,两主机就可完成网络通信了。

在这里插入图片描述

查看网络服务是否启动的命令:netstat

在这里插入图片描述

  • u:udp
  • a: all
  • p:pid/program
  • n:num(能显示成数字的则显示)

但是当前的程序还有一个问题,那就是如果服务器上有多张网卡,那就有多个ip地址,但是server默认的ip是127.0.0.1,这样就会导致server无法收到其它ip所搜到的信息。

因此,需要将server的ip设置为INMADDR_ANY

在这里插入图片描述
将server中涉及ip的位置都修改就可以了

在这里插入图片描述

但是上面的代码还有点不优雅,特别是涉及ip地址与端口号那里,下面我们修改一下,直接将ip与port封装成一个对象。

#pragma once#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>#include <string>
#include "Common.hpp"
class InetAddr
{
private:void portNet2Host(){_port = ::ntohs(_net_addr.sin_port);}void ipNet2Host(){// 此种方式inbuffer是局部的,不会造成问题char inbuff[64];_ip = ::inet_ntop(AF_INET, &_net_addr.sin_addr, inbuff, sizeof(inbuff));}public:InetAddr(){}InetAddr(const struct sockaddr_in &addr): _net_addr(addr){portNet2Host();ipNet2Host();}// 供服务端创建自己的InetAddr(uint16_t port): _port(port), _ip(""){_net_addr.sin_family = AF_INET;_net_addr.sin_port = ::htons(_port);_net_addr.sin_addr.s_addr = INADDR_ANY;}struct sockaddr *Getaddr() { return CONVERSE(&_net_addr); }socklen_t Getlen() { return sizeof(_net_addr); }uint16_t getPort() { return _port; }std::string getIP() { return _ip; }struct sockaddr_in getAddr() { return _net_addr; }~InetAddr(){}private:struct sockaddr_in _net_addr;uint16_t _port;std::string _ip;
};

在这里插入图片描述
效果:
在这里插入图片描述


TCP接口介绍:

// 绑定端口号 (TCP/UDP, 服务器)
int bind(int socket, const struct sockaddr *address, socklen_t address_len);// 开始监听 socket (TCP, 服务器) 
int listen(int socket, int backlog);// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address, socklen_t* address_len);// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

listen:函数的第二个参数,表示最多允许有 backlog 个客户端处于连接等待状态,如果接收到更多的连接请求就忽略,这里设置不会太大。

在这里插入图片描述

accept:

在这里插入图片描述

与UDP不同的是:tcp没有recvfrom与sendto方法,它直接使用read 和write;

在这里插入图片描述

connect

  • 客户端需要调用 connect()连接服务器;
  • connect 和 bind 的参数形式一致,区别在于 bind 的参数是自己的地址, 而connect 的参数是对方的地址

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

qtcanpool 知 08:Docking

文章目录 前言口味改造后语 前言 很久以前&#xff0c;作者用 Qt 仿照前端 UI 设计了一个 ministack&#xff08;https://gitee.com/icanpool/qtcanpool/blob/release-1.x/src/libs/qcanpool/ministack.h&#xff09; 控件&#xff0c;这个控件可以折叠。部分用户体验后&#…

【PyQt5教程 一】Qt Designer 安装及其使用方法说明,附程序源码

目录 一、PyQt5介绍&#xff1a; &#xff08;1&#xff09;PyQt简介&#xff1a; &#xff08;2&#xff09;PyQt API&#xff1a; &#xff08;3&#xff09;支持的环境&#xff1a; &#xff08;4&#xff09;安装&#xff1a; &#xff08;5&#xff09;配置环境变量…

青海摇摇了3天,技术退步明显.......

最近快手上的青海摇招聘活动非常火热&#xff0c;我已经在思考是否备战张诗尧的秋招活动。开个玩笑正片开始&#xff1a; 先说一下自己的情况&#xff0c;大专生&#xff0c;20年通过校招进入杭州某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c…

DDD第一话:业务领域分析

业务领域的概念 业务领域定义了公司的主要活动领域&#xff0c;这是公司为客户提供的服务内容。例如&#xff1a;联邦快递提供快递服务&#xff1b;星巴克最出名的是它的咖啡。 子域 为了实现其业务领域的目标和目标&#xff0c;公司必须在多个子领域中操作。子域是业务活动…

MongoDB change stream实战

什么是 Chang Stream Change Stream指数据的变化事件流&#xff0c;MongoDB从3.6版本开始提供订阅数据变更的功能。 Change Stream 是 MongoDB 用于实现变更追踪的解决方案&#xff0c;类似于关系数据库的触发器&#xff0c;但原理不完全相同&#xff1a; Change Stream 的实…

Linux其二设置端口号,静态ip以及命令

目录 1、VI编辑器 【linux版本的文本文件】 2&#xff09; 补充的vi编辑器的其他内容(了解) 2、ln 连接的意思 link的缩写 3、文件的查看 【重点】 4、压缩与解压&#xff08;重点&#xff09; 5、find 查找命令 6、which & whereis 作用是一样的&#xff0c;表示某…

MetaGPT 安装

1. 创建环境 conda create -n metagpt python3.10 && conda activate metagpt2. 可编辑方式安装 git clone --depth 1 https://github.com/geekan/MetaGPT.git cd MetaGPT pip install -e .3. 配置 metagpt --init-config运行命令&#xff0c;在C盘位置C:\Users\325…

WEB开发: Node.js路由之由浅入深(一) - 全栈工程师入门

作为一个使用Node.js多年的开发者&#xff0c;我已经习惯于用Node.js写一些web应用来为工作服务&#xff0c;因为实现快速、部署简单、自定义强。今天我们一起来学习一个全栈工程师必备技能&#xff1a;web路由。&#xff08;观看此文的前提是默认你已经装好nonde.js了&#xf…

【后端面试总结】Redis字符串实现原理

字符串是我们平时接触频率最高的一个基础类型&#xff0c;但就是这么一个平平无奇的基本类型&#xff0c;在Redis里面也是经历了各种各样的优化&#xff0c;来优化它对内存的占用&#xff0c;了解这部分内容&#xff0c;与其说是“学习Redis”&#xff0c;不如说是“向Redis学习…

GitToolBox插件:让IntelliJ IDEA的Git操作如虎添翼

GitToolBox插件介绍 GitToolBox是一款针对IntelliJ IDEA的插件&#xff0c;旨在增强IDE内置的Git功能&#xff0c;使Git操作更加便捷和高效。无论是单独开发者还是团队中的一员&#xff0c;这个插件都能帮助更好地管理代码和协作流程。 功能特点 分支管理&#xff1a;GitToolBo…

Vulhub:Shiro[漏洞复现]

目录 CVE-2010-3863(Shiro未授权) 使用浏览器访问靶场主页面 使用Yakit进行抓包 使用ffuf对靶机8080端口进行根路径FUZZ CVE-2016-4437(Shiro-550) 使用浏览器访问靶场主页面 使用Yakit进行抓包 使用Yakit反连中自带的Yso-Java Hack进行漏洞利用 首先运行脚本生成一个…

Netty 框架——TCP 粘包和拆包

Netty 框架——TCP 粘包和拆包 1. 产生的原因 在 TCP 协议中&#xff0c;发送端为了提高网络传输的效率&#xff0c;通常会使用优化算法&#xff0c;如 Nagle 算法&#xff0c;将多个小的数据包合并成一个较大的数据块一起发送。这是因为频繁的小数据包传输可能会导致效率低下…

SQL靶场第九关攻略

我们的第九关需要用到时间盲注 使用条件&#xff1a;完全没有变化的页面 我们在了解一下时间盲注和布尔盲注的区别&#xff0c;时间盲注比布尔盲注多了一个if判断加上sleep()函数的运用 if(a,b,c) if判断句&#xff0c;a为条件&#xff0c;b、c为执行语句&#xff1b;如果a为…

STM32一keil5更换芯片后报错问题的解决。

目录 一、STM32型号认识二、报错问题三、常用的启动配置文件四、问题解决 一、STM32型号认识 二、报错问题 当我们在原来工程下修改芯片时&#xff0c;原本可以编译通过的代码突然很多报错。如下所示&#xff0c;这是因为我们的启动文件配置错误。对于不同型号的芯片其flash容量…

STM32 自学笔记

摘抄于大学期间记录在QQ空间的一篇自学笔记&#xff0c;当前清理空间&#xff0c;本来想直接删除掉的&#xff0c;但是感觉有些舍不得&#xff0c;因此先搬移过来。 RAM vs ROM vs FLASH 2013-09-05记录&#xff0c;ROM和RAM指的都是半导体存储器&#xff0c;ROM是Read Only …

深入解析 HTML Input 元素:构建交互性表单的核心

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

MBox20边缘计算网关:氢能车间数据采集的智慧引擎

氢能作为未来能源体系的重要组成部分&#xff0c;其安全、高效、环保的特性备受瞩目。在氢能车间的日常运营中&#xff0c;数据采集是确保生产流程优化、设备稳定运行及能效提升的关键环节。然而&#xff0c;面对氢能车间复杂多变的生产环境和海量数据&#xff0c;如何实现高效…

敏捷开发之路

1. 引言 最近有个企业软件开发项目&#xff0c;用户要求采用敏捷开发的方法实施项目。以前也参加过敏捷方法的培训&#xff0c;结合最近找的敏捷开发材料&#xff0c;形成了下面的敏捷实施过程内容。 以下采用了QAD量化敏捷开发方法&#xff0c;关于此方法详细参考内容见最后…

threejs相机辅助对象cameraHelper

为指定相机创建一个辅助对象&#xff0c;显示这个相机的视锥。 想要在场景里面显示相机的视锥&#xff0c;需要创建两个相机。 举个例子&#xff0c;场景中有个相机A&#xff0c;想要显示相机A的视锥&#xff0c;那么需要一个相机B&#xff0c;把B放在A的后面&#xff0c;两个…

Milvus向量数据库03-搜索理论

Milvus向量数据库03-搜索理论 1-ANN搜索 通过 k-最近邻&#xff08;kNN&#xff09;搜索可以找到一个查询向量的 k 个最近向量。kNN 算法将查询向量与向量空间中的每个向量进行比较&#xff0c;直到出现 k 个完全匹配的结果。尽管 kNN 搜索可以确保准确性&#xff0c;但十分耗…