/**
* 以太网
*/
class Ethernet {
static readonly size = 14;
get Destination(): string {
return [
this.view.getUint8(0),
this.view.getUint8(1),
this.view.getUint8(2),
this.view.getUint8(3),
this.view.getUint8(4),
this.view.getUint8(5),
]
.map((it) => it.toString(16))
.join(":");
}
get Source(): string {
return [
this.view.getUint8(6),
this.view.getUint8(7),
this.view.getUint8(8),
this.view.getUint8(9),
this.view.getUint8(10),
this.view.getUint8(11),
]
.map((it) => it.toString(16))
.join(":");
}
get Type(): number {
return this.view.getUint16(12);
}
view: DataView;
constructor(ethernetBytes: number[]) {
this.view = new DataView(Uint8Array.from(ethernetBytes).buffer);
}
} /**
* 解析IP头
*/
class IP {
//See also: http://mars.netanya.ac.il/~unesco/cdrom/booklet/HTML/NETWORKING/node020.html
static readonly size = 20; /**
* ## 版本号
* - 数据报属于哪个协议版本。
*/
get Version(): number {
return this.view.getUint8(0) >> 4;
}
set Version(value: number) {
if (value > 0xf) throw new Error("value 不能超过4-bit(15).");
this.view.setUint8(0, (value << 4) | this.HeaderLength);
} /**
* - 标头中32位字的数量(DWORD)
* - 因为这是4位,所以最大报头长度为15个字(即60个字节)
* - 标头至少为20个字节,但Option(0 or more words)可能会使它更大
*/
get HeaderLength(): number {
return this.view.getUint8(0) & 0xf;
}
set HeaderLength(value: number) {
if (value > 0xf) throw new Error("value 不能超过4-bits.");
this.view.setUint8(0, value | (this.Version << 4));
} /**
* ## 服务种类
* - 包含一个3位优先级字段(今天将被忽略),4个服务位和1个未使用位。
* - 四个服务位可以是:
- 1000-最小化延迟
- 0100-最大化吞吐量
- 0010-最大化可靠性
- 0001-最小化金钱成本
*/
get TypeOfService(): number {
return this.view.getUint8(1);
}
set TypeOfService(value: number) {
if (value > 0xff) throw new Error("value 不能超过8-bits.");
this.view.setUint8(1, value);
} /**
* ## 总长度
* - 数据报的总长度(以字节为单位)。
* - 我们知道数据从头开始的位置
* - 我们通过计算“Total Length-Header Length”知道DATA的大小
* - Total Length使用(WORD)储存,最多存0xFFFF(65535)字节
* - 但是物理层可能不允许这么多字节的数据包大小
* - 因此,IP有时必须对数据包进行分段。
* - 当IP数据报被分段时,每个分段都被视为一个单独的数据报。
* - 它在最终目的地而不是在路由器处重新组装!
* - 每个片段都有自己的header
* - 标识号被复制到每个片段中。See also: MF/DF/FragmentOffset
*/
get TotalLength(): number {
return this.view.getUint16(2);
}
set TotalLength(value: number) {
if (value > 0xffff) throw new Error("value 不能超过16-bits.");
this.view.setUint16(2, value);
} /**
* ## 身份证明
* - 唯一标识数据报
* - 通常每次发送数据报时加1。
* - 数据报的所有片段都包含相同的标识值。
* - 这使目标主机可以确定哪个片段属于哪个数据报。
*/
get Identification(): number {
return this.view.getUint16(4);
}
set Identification(value: number) {
if (value > 0xffff) throw new Error("value 不能超过16-bits.");
this.view.setUint16(4, value);
} /**
* ## Don't fragment 不要碎片
* - 如果为1,则表示“请勿分段”。
* - 如果IP必须分片数据包并且该位置1,则IP丢弃数据报。
*/
get DF(): number {
return (this.view.getUint8(6) >> 6 & 1;
}
set DF(value: number) {
if (value !== 0 && value !== 1) throw new Error("value 只能为1或者0.");
if (value === this.DF) return;
this.view.setUint8(6, this.view.getUint8(6) ^ (1 << 6));
} /**
* ## More Fragment 片段标志
* - 如果该位为0,则表示这是最后一个片段
* - IP标头的所有其他字段与第一个数据包相同(校验和除外)
* - 路由器看到3个单独的数据包。在最终目的地上将数据包传递到上层之前重新组装数据包
* - 片段标志(MF)0和偏移量(FragmentOffset)0表示数据报未分片
*/
get MF(): number {
return (this.view.getUint8(6) >> 5 & 1;
}
set MF(value: number) {
if (value !== 0 && value !== 1) throw new Error("value 只能为1或者0."); if (value === this.MF) return;
this.view.setUint8(6, this.view.getUint8(6) ^ (1 << 5));
} /**
* ## 碎片偏移
* - 片段标志(MF)0和偏移量(FragmentOffset)0表示数据报未分片
* - 片段偏移量以8字节(QWORD)为单位进行测量。这是因为片段偏移字段比总长度字段短3位(并且2^3为8)。
*/
get FragmentOffset(): number {
return this.view.getUint16(6) & 0x1fff;
}
set FragmentOffset(value: number) {
if (value > 0x1fff) throw new Error("value 不能超过13-bits.");
// 删除旧址在设置新值
this.view.setUint16(6, value | (this.DF << 14) | (this.MF << 13));
} /**
* ## 生存时间
* - 路由器上限
* - 通常设置为32或64。
* - 每个处理数据报的路由器都会递减,
* - 当TTL达到0时,路由器将丢弃该数据报。
*/
get TimeToLive(): number {
return this.view.getUint8(8);
}
set TimeToLive(value: number) {
if (value > 0xff) throw new Error("value 不能超过8-bits.");
this.view.setUint8(8, value);
} /**
* ## 协议
* - 告诉IP将数据报发送到哪里。
* - 6表示TCP
* - 17表示UDP
*/
get Protocol(): number {
return this.view.getUint8(9);
}
setProtocol(value: number) {
if (value > 0xff) throw new Error("value 不能超过8-bits.");
this.view.setUint8(9, value);
} /**
* ## 标头校验和
* - 仅覆盖标题,不覆盖数据。
*/
get HeaderChecksum(): number {
return this.view.getUint16(10);
}
set HeaderChecksum(value: number) {
if (value > 0xffff) throw new Error("value 不能超过16-bits.");
this.view.setUint16(10, value);
} updateHeaderChecksum() {
this.HeaderChecksum = 0;
let offset = 0;
const a = [];
while (offset < this.view.byteLength) {
a.push(this.view.getUint16(offset));
offset += 2;
} const v = a.reduce<number>((acc, it) => {
acc += it;
if (acc > 0xffff) {
// 进位
const leftmost = acc >> 16;
acc = (acc ^ (leftmost << 16)) + leftmost;
}
return acc;
}, 0);
this.HeaderChecksum = ~v;
} checkHeaderChecksum(): boolean {
let offset = 0;
const a = [];
while (offset < this.view.byteLength) {
a.push(this.view.getUint16(offset));
offset += 2;
} const v = a.reduce<number>((acc, it) => {
acc += it;
if (acc > 0xffff) {
// 进位
const leftmost = acc >> 16;
acc = (acc ^ (leftmost << 16)) + leftmost;
}
return acc;
}, 0);
return 0 === ~v;
} /**
* 源IP地址
*/
get Source(): number {
return this.view.getUint32(12);
}
set Source(value: number) {
if (value > 0xffffffff) throw new Error("value 不能超过32-bits.");
this.view.setUint32(12, value);
} /**
* 这只是[Source]的字符串形式
*/
get SourceString(): string {
return [
this.view.getUint8(12),
this.view.getUint8(13),
this.view.getUint8(14),
this.view.getUint8(15),
].join(".");
}
/**
* 以字符串的形式设置[Source]
* @param value 如: "192.168.1.6"
*/
set SourceString(value: string) {
const a: string[] = value.split(".");
if (a.length !== 4)
throw new Error(`setSourceString value Error: (${value})`); const inta: number[] = a.map((it) => {
const r = parseInt(it, 10);
if (r > 0xff) throw new Error(`码点(${it})不能超过8-bits.`);
return r;
});
this.view.setUint8(12, inta[0]);
this.view.setUint8(13, inta[1]);
this.view.setUint8(14, inta[2]);
this.view.setUint8(15, inta[3]);
} /**
* 目的IP地址
*/
get Destination(): number {
return this.view.getUint32(16);
}
set Destination(value: number) {
if (value > 0xffffffff) throw new Error("value 不能超过32-bits.");
this.view.setUint32(16, value);
} /**
* 这只是[Destination]的字符串形式
*/
get DestinationString(): string {
return [
this.view.getUint8(16),
this.view.getUint8(17),
this.view.getUint8(18),
this.view.getUint8(19),
].join(".");
}
/**
* 以字符串的形式设置
* @param value 如: "192.168.1.6"
*/
setDestinationString(value: string) {
const a: string[] = value.split(".");
if (a.length !== 4)
throw new Error(`setDestinationString value Error: (${value})`); const inta: number[] = a.map((it) => {
const r = parseInt(it, 10);
if (r > 0xff) throw new Error(`码点(${it})不能超过8-bits.`);
return r;
});
this.view.setUint8(16, inta[0]);
this.view.setUint8(17, inta[1]);
this.view.setUint8(18, inta[2]);
this.view.setUint8(19, inta[3]);
} view: DataView;
constructor(ipBytes: number[]) {
this.view = new DataView(Uint8Array.from(ipBytes).buffer);
} toString(): string {
return `
0100 .... = Version: ${this.Version}
.... 0101 = Header Length: ${this.HeaderLength * 4} bytes (${this.HeaderLength})
Type Of Service: 0x0${this.TypeOfService.toString(16)}
Total Length: ${this.TotalLength}
Identification: 0x${this.Identification.toString(16)} (${this.Identification})
Df: ${this.DF}
MF: ${this.MF}
Fragment offset: ${this.FragmentOffset}
Time to live: ${this.TimeToLive}
Protocol: ${
this.Protocol == 6 ? "TCP" : this.Protocol == 17 ? "UDP" : "Other"
} (${this.Protocol})
Header checksum: 0x${this.HeaderChecksum.toString(16)}
Source: ${this.SourceString}
Destination: ${this.DestinationString}
`.trim();
}
} /**
* ## User Datagram Protocol 用户数据报协议
* - UDP是一种简单的无连接协议。它不提供可靠性;它只是将数据发送到IP层
*/
class UDP {
static readonly size = 8;
get SourcePort(): number {
return this.view.getUint16(0);
}
set SourcePort(value: number) {
if (value > 0xffff) throw new Error("value 不能超过16-bits.");
this.view.setUint16(0, value);
}
get DestinationPort(): number {
return this.view.getUint16(2);
}
set DestinationPort(value: number) {
if (value > 0xffff) throw new Error("value 不能超过16-bits.");
this.view.setUint16(2, value);
}
/**
* - 长度是报头和数据的字节数
* - header是8个字节
* - IP数据报的最大大小为65535字节,IP报头减去20字节===>剩余65515字节用于数据。但是,UDP标头为8个字节,剩下65507个字节用于最大数量的用户数据。(不确定)
*/
get Length(): number {
return this.view.getUint16(4);
}
set Length(value: number) {
if (value > 0xffff) throw new Error("value 不能超过16-bits.");
this.view.setUint16(4, value);
}
get Checksum(): number {
return this.view.getUint16(6);
}
set Checksum(value: number) {
if (value > 0xffff) throw new Error("value 不能超过16-bits.");
this.view.setUint16(5, value);
}
/**
* 计算校验和
*/
updateChecksum() {
this.Checksum = 0;
const dataSize = this.view.byteLength - UDP.size;
const byteLength = 20 + (dataSize % 2 !== 0 ? 0 : dataSize);
const pseudoHeader = new DataView(new ArrayBuffer(byteLength));
pseudoHeader.setUint32(0, this.ip.Source);
pseudoHeader.setUint32(4, this.ip.Destination);
pseudoHeader.setUint16(8, this.ip.Protocol);
pseudoHeader.setUint16(10, this.Length);
pseudoHeader.setUint16(12, this.SourcePort);
pseudoHeader.setUint16(14, this.DestinationPort);
pseudoHeader.setUint16(16, this.Length);
pseudoHeader.setUint16(18, this.Checksum); if (dataSize % 2 === 0) {
for (let i = 0; i < dataSize - 1; i++) {
pseudoHeader.setUint8(20 + i, this.view.getUint8(UDP.size + i));
}
} let offset = 0;
const a = [];
while (offset < pseudoHeader.byteLength) {
a.push(pseudoHeader.getUint16(offset));
offset += 2;
}
const v = a.reduce<number>((acc, it) => {
acc += it;
if (acc > 0xffff) {
// 进位
const leftmost = acc >> 16;
acc = (acc ^ (leftmost << 16)) + leftmost;
}
return acc;
}, 0); this.Checksum = ~v;
} view: DataView;
constructor(public readonly ip: IP, udpBytes: number[]) {
this.view = new DataView(Uint8Array.from(udpBytes).buffer);
}
} /**
* ## Transmission Control Protocol 传输控制协议
* - 不可靠的网络上可靠的端到端字节流。
*/
class TCP {
/**
* TCP标头必须至少包含20字节(5个字)的固定格式信息以及一个可选部分(该部分始终为4字节的整数倍)
*/
static readonly size = 20; /**
*
* - 端口是用于进程间通信的逻辑地址。端口在一台主机内(甚至在一台进程内)提供多个目的地。
* - 低于256的端口号是“知名”端口,例如:FTP 21,TELNET 23,SMTP 25,HTTP 80,POP3 110。
* - 低于1024的端口号保留用于系统服务。只允许管理员(例如Unix中的root)分配他们
* - 用户进程可以使用1024到65535(2 ^ 16-1)之间的端口号,而无需任何特殊许可。
* - 可以通过组合[SourceIPAddress.PortNumber,DestinationIPAddress.PortNumber]来标识定义通信通道端点的套接字对。
* - IP号码加端口号构成一个TSAP-传输服务访问点(有时也称为套接字)。
*/
get SourcePort(): number {
return this.view.getUint16(0);
}
set SourcePort(value: number) {
if (value > 0xffff) throw new Error("value 不能超过16-bits.");
this.view.setUint16(0, value);
} get DestinationPort(): number {
return this.view.getUint16(2);
}
set DestinationPort(value: number) {
if (value > 0xffff) throw new Error("value 不能超过16-bits.");
this.view.setUint16(2, value);
} /**
* ## 序列号
* - 一个32位数字(DWORD)
* - 数据的每个字节都有编号。
* - TCP段的序列号是该段中第一个数据字节的ID号
* - 它不需要从1开始!
* - 有效序列号的范围是0到4,294,967,295(0x0000,0000-0xFFFF,FFFF)
*/
get SequenceNumber(): number {
return this.view.getUint32(4);
}
set SequenceNumber(value: number) {
if (value > 0xffffffff) throw new Error("value 不能超过32-bits.");
this.view.setUint32(4, value);
} /**
* ## 确认号
* - 一个32位数字(DWORD)
* - 指定发送者期望的下一个字节的编号(而不是正确接收的最后一个字节!)
* - 通常将数据从接收方发送到发送方
* - 如果此确认段在特定时间内未到达,则发送方重新传输前一个段(计时器)
*/
get AcknowledgmentNumber(): number {
return this.view.getUint32(8);
}
set AcknowledgmentNumber(value: number) {
if (value > 0xffffffff) throw new Error("value 不能超过32-bits.");
this.view.setUint32(8, value);
} /**
* TCP标头中的标头长度数(32位)。此信息是必需的,因为标题有时可能超过4个字。仅4位分配给TCP标头长度字段。因此,最长为15个字(60个字节)
*/
get TCPHeaderLength(): number {
return this.view.getUint8(12) >> 4;
}
set TCPHeaderLength(value: number) {
if (value > 0xf) throw new Error("value 不能超过4-bits.");
this.view.setUint8(12, value << 4);
} /**
* ## Urgent
* - 紧急指针有效,与当前序列号的字节偏移,在当前序列号处可以找到紧急数据(中断消息)。当中止rlogin或telnet连接或ftp数据传输时,使用紧急模式。
*/
get URG(): number {
return this.view.getUint8(13) >> 5 & 1;
}
set URG(value: number) {
if (value !== 0 && value !== 1) throw new Error("value 只能为1或者0.");
if (value === this.URG) return;
this.view.setUint8(13, this.view.getUint8(13) ^ (1 << 5));
} /**
* ## Acknowledgment 确认编号有效
* - 如果ACK = 0,则此段中没有确认
*/
get ACK(): number {
return this.view.getUint8(13) >> 4 & 1;
}
set ACK(value: number) {
if (value !== 0 && value !== 1) throw new Error("value 只能为1或者0.");
if (value === this.ACK) return;
this.view.setUint8(13, this.view.getUint8(13) ^ (1 << 4));
} /**
* ## Push 接收方应尽快将此数据传递给应用程序
* - 接收方被请求在到达时将数据传递给应用程序,而不是缓冲它。
*/
get PSH(): number {
return this.view.getUint8(13) >> 3 & 1;
}
set PSH(value: number) {
if (value !== 0 && value !== 1) throw new Error("value 只能为1或者0.");
if (value === this.PSH) return;
this.view.setUint8(13, this.view.getUint8(13) ^ (1 << 3));
} /**
* ## Reset 重置(关闭)连接。
*/
get RST(): number {
return this.view.getUint8(13) >> 2 & 1;
}
set RST(value: number) {
if (value !== 0 && value !== 1) throw new Error("value 只能为1或者0.");
if (value === this.RST) return;
this.view.setUint8(13, this.view.getUint8(13) ^ (1 << 2));
} /**
* ## Syn 同步序列号以开始连接
*/
get SYN(): number {
return this.view.getUint8(13) >> 1 & 1;
}
set SYN(value: number) {
if (value !== 0 && value !== 1) throw new Error("value 只能为1或者0.");
if (value === this.RST) return;
this.view.setUint8(13, this.view.getUint8(13) ^ (1 << 1));
} /**
* ## 发送方已完成数据发送
*/
get FIN(): number {
return this.view.getUint8(13) & 1;
}
set FIN(value: number) {
if (value !== 0 && value !== 1) throw new Error("value 只能为1或者0.");
if (value === this.RST) return;
this.view.setUint8(13, this.view.getUint8(13) ^ 1);
} /**
* 此字节的发送者愿意接受的数据字节数,从确认字段中指示的字节数开始。窗口大小为0表示已收到直到确认编号1为止的字节,但是此刻接收器无法接受更多数据。稍后,如果接收方准备好接收更多数据,则他发送具有相同确认号和非零窗口大小的段(如果丢失该段,则发送方每隔一定的时间探测接收方)。
*/
get WindowSize(): number {
return this.view.getUint16(14);
}
set WindowSize(value: number) {
if (value > 0xffff) throw new Error("value 不能超过16-bits.");
this.view.setUint16(14, value);
} /**
* 整个段的16位校验和(加上伪IP标头)。
*/
get Checksum(): number {
return this.view.getUint16(16);
}
set Checksum(value: number) {
if (value > 0xffff) throw new Error("value 不能超过16-bits.");
this.view.setUint16(16, value);
} get UrgentPointer(): number {
return this.view.getUint16(18);
}
set UrgentPointer(value: number) {
if (value > 0xffff) throw new Error("value 不能超过16-bits.");
this.view.setUint16(18, value);
} view: DataView;
constructor(public readonly ip: IP, udpBytes: number[]) {
this.view = new DataView(Uint8Array.from(udpBytes).buffer);
}
} class Data {
get data(): Uint8Array {
return new Uint8Array(this.view.buffer);
}
set data(value: Uint8Array) {
this.view = new DataView(value.buffer);
} view: DataView;
constructor(dataBytes: number[]) {
this.view = new DataView(Uint8Array.from(dataBytes).buffer);
}
} class Packet {
ethernet: Ethernet;
ip: IP;
udp?: UDP;
tcp?: TCP;
data: Data; #_view!: DataView;
get view(): DataView {
for (let i = 0; i < IP.size; i++)
this.#_view.setUint8(Ethernet.size + i, this.ip.view.getUint8(i)); if (this.udp)
for (let i = 0; i < UDP.size; i++)
this.#_view.setUint8(
Ethernet.size + IP.size + i,
this.ip.view.getUint8(i)
); return this.#_view;
}
set view(value: DataView) {
this.#_view = value;
}
constructor(packetBytes: number[]) {
this.view = new DataView(Uint8Array.from(packetBytes).buffer);
this.ethernet = new Ethernet(packetBytes);
this.ip = new IP(packetBytes.slice(Ethernet.size)); let procSize = 0;
if (this.ip.Protocol === 6) {
this.tcp = new TCP(this.ip, packetBytes.slice(Ethernet.size + IP.size));
procSize = TCP.size;
} else if (this.ip.Protocol === 17) {
this.udp = new UDP(this.ip, packetBytes.slice(Ethernet.size + IP.size));
procSize = UDP.size;
}
this.data = new Data(packetBytes.slice(Ethernet.size + IP.size + procSize));
}
} const udpData =
"70 89 cc ee 84 2c 3c 2c 30 a6 a2 d0 08 00 45 80 00 7b c9 5b 00 00 80 11 00 00 c0 a8 01 06 b7 e8 7f f4 0f a5 1f 40 00 67 c7 76 02 39 3d 03 e3 4c 72 61 dc 91 5f 04 00 00 00 01 01 01 00 00 69 a1 00 00 00 00 00 00 00 00 da d2 8d 74 3e 33 16 50 ea 46 c0 cf 22 9c 93 7d 7e b3 91 89 e0 c5 b9 e4 22 b6 e6 e9 78 d8 0b 28 6e 42 6d f1 d8 44 43 b1 7f 1b f0 a5 aa a7 d6 e9 4a 07 49 e8 a0 88 a0 42 15 e2 a9 da 21 ed da af 03"; const tcpData =
"70 89 cc ee 84 2c 3c 2c 30 a6 a2 d0 08 00 45 00 00 68 9b b7 40 00 80 06 00 00 c0 a8 01 06 31 eb 6b da c8 41 0e 96 48 d2 73 52 24 9a 12 8b 50 18 ff ff 5f ce 00 00 54 4c 42 42 30 31 d5 01 18 00 00 7e 0b 84 80 58 0e 03 00 00 00 61 61 61 00 ff ff ff ff 01 00 00 00 00 00 00 54 4c 42 42 30 31 73 03 10 00 00 7f 00 00 00 00 00 00 00 00 00 00 00 00 1e 03 04 00"; const p = new Packet(
tcpData
.trim()
.split(/\s+/)
.map((it) => parseInt(it, 16))
);

ip/udp/tcp包 学习的更多相关文章

  1. 网络传输数据封装详解(IP,UDP,TCP)

    IP数据包也叫IP报文分组,传输在ISO网络7层结构中的网络层,它由IP报文头和IP报文用户数据组成,IP报文头的长度一般在20到60个字节之间,而一个IP分组的最大长度则不能超过65535个字节.  ...

  2. 【Windows socket+IP+UDP+TCP】网络基础

    Windows Socket+网络      Winsock是 Windows下套接字标准.          Winsock 编程分为UDP[Windows socket + UDP],TCP[Wi ...

  3. 以太网,IP,TCP,UDP数据包分析【转】

    原文地址:http://www.cnblogs.com/feitian629/archive/2012/11/16/2774065.html 1.ISO开放系统有以下几层: 7 应用层 6 表示层 5 ...

  4. 以太网,IP,TCP,UDP数据包分析(此文言简意赅,一遍看不懂的话,耐心的看个10遍就懂了,感谢作者无私奉献)

    1.ISO开放系统有以下几层: 7 应用层 6 表示层 5 会话层 4 传输层 3 网络层 2 数据链路层 1 物理层 2.TCP/IP 网络协议栈分为应用层(Application).传输层(Tra ...

  5. 一些重要的计算机网络协议(IP、TCP、UDP、HTTP)

    一.计算机网络的发展历程 1.计算机网络发展 与其说计算机改变了世界,倒不如说是计算机网络改变了世界.彼时彼刻,你我都因网络而有了交集,岂非一种缘分? 计算机与网络发展大致经历如下过程:

  6. [转]SOCKET通信中TCP、UDP数据包大小的确定

    TCP.UDP数据包大小的确定 UDP和TCP协议利用端口号实现多项应用同时发送和接收数据.数据通过源端口发送出去,通过目标端口接收.有的网络应用只能使用预留或注册的静态端口:而另外一些网络应用则可以 ...

  7. ETHERNET数据包格式( IP & UDP & ICMP & ARP )

    ETHERNET数据包格式( IP & UDP & ICMP & ARP ) ETHERNET数据包格式 一.ETHERNET 数据包的协议类型 TYPE 的值为 0x0800 ...

  8. [转]TCP、UDP数据包大小的确定

       TCP.UDP数据包大小的确定   http://blog.163.com/jianlizhao%40126/blog/static/1732511632013410101827640/   U ...

  9. TCP、UDP数据包大小的限制(UDP数据包一次发送多大为好)——数据帧的物理特性决定的,每层都有一个自己的数据头,层层递减

    1.概述 首先要看TCP/IP协议,涉及到四层:链路层,网络层,传输层,应用层. 其中以太网(Ethernet)的数据帧在链路层 IP包在网络层 TCP或UDP包在传输层 TCP或UDP中的数据(Da ...

随机推荐

  1. 大数据开发-Spark-拷问灵魂的5个问题

    1.Spark计算依赖内存,如果目前只有10g内存,但是需要将500G的文件排序并输出,需要如何操作? ①.把磁盘上的500G数据分割为100块(chunks),每份5GB.(注意,要留一些系统空间! ...

  2. office提示“应用程序无法正常启动(0xc0000142)。请单击确认关闭应用程序”

    打开word文档,突然弹出如下提示框: 网上查询,说应用程序无法正常启动(0xc0000142)的原因可能是缺少组件导致的.控制面板 - 时钟和区域 - 更改日期.时间或数字格式 - 管理 - 更改系 ...

  3. Spring Boot中的静态资源文件

    Spring Boot中的静态资源文件 1.SSM中的配置 2.Spring Boot 中的配置 2.1 整体规划 2.2 源码解读 2.3 自定义配置 2.3.1 application.prope ...

  4. SpringBoot配置文件 application.properties,yaml配置

    SpringBoot配置文件 application.properties,yaml配置 1.Spring Boot 的配置文件 application.properties 1.1 位置问题 1.2 ...

  5. AYIT-2020 609暑假集训第一周周赛题 A - A计划

    可怜的公主在一次次被魔王掳走一次次被骑士们救回来之后,而今,不幸的她再一次面临生命的考验.魔王已经发出消息说将在T时刻吃掉公主,因为他听信谣言说吃公主的肉也能长生不老.年迈的国王正是心急如焚,告招天下 ...

  6. CF Hello 2020 E.New Year and Castle Construction

    E.New Year and Castle Construction 题意 给定n个点,对于每个点\(p\),求出4-point 子集(该子集有四个点,并且围成的圈包含\(p\))的个数 数据给的点中 ...

  7. 2019 Multi-University Training Contest 7 Kejin Player(期望)

    题意:给定在当前等级升级所需要的花费 每次升级可能会失败并且掉级 然后q次询问从l到r级花费的期望 思路:对于单次升级的期望 我们可以列出方程: 所以我们可以统计一下前缀和 每次询问O1回答 #inc ...

  8. Educational Codeforces Round 91 (Rated for Div. 2) C. Create The Teams

    题目链接:https://codeforces.com/contest/1380/problem/C 题意 给 $n$ 个数分组,要求每组的最小值乘以该组数的个数不小于 $x$ . 题解 从大到小依次 ...

  9. 【noi 2.6_8787】数的划分(DP){附【转】整数划分的解题方法}

    题意:问把整数N分成K份的分法数.(与"放苹果"不同,在这题不可以有一份为空,但可以类比)解法:f[i][j]表示把i分成j份的方案数.f[i][j]=f[i-1][j-1](新开 ...

  10. hdu1828 Picture(线段树+扫描线+矩形周长)

    看这篇博客前可以看一下扫描线求面积:线段树扫描线(一.Atlantis HDU - 1542(覆盖面积) 二.覆盖的面积 HDU - 1255(重叠两次的面积))  解法一·:两次扫描线 如图我们可以 ...