【读书笔记】Linux高性能服务器编程(第二篇 第五章)

2023-11-13

第五章 Linux网络编程基础API

 

5.1 socket地址API

5.1.1 主机字节序和网络字节序


字节序分为:

1. 大端字节序:一个整数的高位字节(23-31 bit)存储在内存的低地址处,低位字节(0-7 bit)存储在内存的高地址处。

2. 小端字节序:整数的低位字节存储在内存的低地址处,而高位字节则存储在内存的高地址处。

小端字节序又被称为主机字节序。

大端字节序又被称为网络字节序。

判断机器字节序:

union{

short value;

char union_bytes[ sizeof(short) ] ; 

}test ;

赋值给test.value 然后比较 test.union_bytes数组,可得到机器字节序。

 

Linux提供了如下4个函数来完成主机字节序和网络字节序之间的转换:

#include<netinet/in.h>

unsigned long int htonl( unsigned long int hostlong ) ;

unsigned short int htons( unsigned short int hostshort ) ;

unsigned long int ntohl( unsigned long int netlong ) ;

unsigned short int ntohs( unsigned short int netshort ) ;

长整型函数通常用来转换IP地址,短整型函数用来转换端口号(当然不限于此,任何格式化的数据通过网络传输时,都应该使用这些函数来转换字节序)。

 

5.1.2   通用socket地址

#include<bits/socket.h>

struct  sockaddr

{

sa_family_t  sa_family ;

char  sa_data[ 14 ] ;

sa_family 成员是地址族类型(sa_family_t)的变量。地址族类型通常与协议族类型对应。常见的协议族(protocol family,也称domain)和对应的地址族,如表:

PF_* AF_* 都定义在bits/socket.h 头文件中,且后者与前者有完全相同的值,所以二者通常混用。


sa_data成员用于存放socket地址值。但是,不同的协议族的地址值具有不同的含义和长度,如表:


由表5-2可见,14字节的sa_data根本无法完全容纳多数协议族的地址值。因此,Linux定义了下面这个新的通用socket地址结构体:

#include<bits/socket.h>

struct  sockaddr_storage

{

sa_family_t  sa_family ;

unsigned long int  __ss_align;

char  __ss_padding[128 - sizeof ( __ss_align )] ; 

这个结构体不仅提供了足够大的空间用于存放地址值,而且是内存对齐的(这是__ss_align成员的作用)。

 

5.1.3 专用socket地址

Unix 本地域协议族使用如下专用 socket 地址结构体:

#include<sys/un.h>

struct  sockaddr_un

{

sa_family_t  sin_family;   /* 地址族:AF_UNIX  */ 

char  sun_path[ 108 ] ;  /*  文件路径名  */

} ;

 

 TCP/IP 协议族有 sockaddr_in 和 sockaddr_in6 两个专用 socket 地址结构体,分别用于 IPv4 和 IPv6 


struct  sockaddr_in

{

sa_family_t  sin_family ;  /* 地址族:AF_INET */

u_intl6_t  sin_port ;  /* 端口号,要用网络字节序表示 */

struct  in_addr  sin_addr ;  /* IPv4地址结构体  */ 

};


struct  in_addr

{

u_int32_t  s_addr ;  /* IPv4地址,用网络字节序表示*/

};

 

struct  sockaddr_in6

{

sa_family_t  sin6_family ; /*  地址族:AF_INET6 */

u_int16_t  sin6_port ;  /* 端口号,用网络字节序表示 */

u_int32_t  sin6_flowinfo ; /* 流信息,应设置为0 */

struct  in6_addr  sin6_addr ;  /* IPv6地址结构体 */

u_int32_t  sin6_scope_id ;  /* scope ID ,实验阶段*/

} ;


struct  in6_addr

{

unsigned  char  sa_addr[ 16 ] ; /* IPv6地址,用网络字节序表示*/

};

所有专用socket地址(以及sockaddr_storage)类型的变量在实际使用时都需要转化为通用socket地址类型sockaddr(强制转换即可),因为所有socket编程接口使用的地址参数的类型都是sockaddr 

 

 

 

5.1.4 IP地址转换函数

下面三个函数可用于点分十进制字符串表示的IPv4地址和用网络字节序整数表示的IPv4地址之间的转换:

#include<arpa/inet.h>

in_addr_t  inet_addr ( const  char *  strptr ) ;

int inet_aton ( const  char *cp  ,  struct  in_addr * inp) ;

char * inet_ntoa ( struct in_addr  in ) ;

inet_addr 函数将用点分十进制字符串表示的IPv4地址转化为用网络字节序整数表示的IPv4地址。失败时返回 INADDR_NONE

 

inet_aton函数完成和inet_addr 同样的功能,但是将转化结果存储于参数inp 指向的地址结构中 。 成功返回 ,失败则返回

 

inet_ntoa 函数将用网络字节序整数表示的IPv4地址转化为用点分十进制字符串表示的IPv4地址。

注意:该函数内部用一个静态变量存储转化结果,函数的返回值指向该静态内存,因此inet_ntoa 是不可重入的 。

 

 

 

 

 

下面这对更新的函数也能完成和前面三个函数同样的功能,并且它们同时适用于IPv4IPv6地址:

#include<arpa/inet.h>

int inet_pton ( int af , const char* src , void* dst ) ;

const char* inet_ntop( int af, ,const void* src , char* dst , socklen_t cnt) ;

inet_pton函数将用字符串表示的IP地址src(用点分十进制字符串表示的IPv4地址或用十六进制字符串表示的IPv6地址)转换成用网络字节序整数表示的IP地址,并把转换结果存储于dst 指向的内存中。

af 参数指定地址族(AF_INET或者AF_INET6

inet_pton成功时返回1,失败则返回0,并设置errno 

 

inet_ntop函数进行相反的转换,前三个参数的含义与inet_pton的参数相同,最后一个cnt指定目标存储单元的大小。

下面两个宏能帮助我们指定这个大小(分别用于IPv4IPv6

#include<netinet/in.h>

#define  INET_ADDRSTRLEN  16

#define  INET6_ADDRSTRLEN  46

inet_ntop成功时返回目标存储单元的地址,失败则返回NULL并设置errno 

 

 

 

5.2 创建socket

UNIX/Linux 的一个哲学:万物皆文件。

socket 就是可读,可写,可控制,可关闭的文件描述符。

下面的socket系统调用可创建一个socket

#include<sys/types.h>

#include<sys/socket.h>

int  socket ( int domain , int type , int protocol ) ;

domain 参数告诉系统使用哪个底层协议族,对TCP/IP协议族而言,该参数设置为PE_INET(用于IPv4PF_INET6(用于IPv6;对于UNIX本地域协议族而言,该参数应为PF_UNIX

type 参数指定服务类型服务类型主要有 SOCK_STREAM服务(流服务)和SOCK_UGRAM(数据报)服务。对TCP/IP协议族而言,其值取SOCK_STREAM表示传输层使用TCP协议,取SOCK_DGRAM表示传输层使用UDP协议

注意:type参数可以接受上述服务类型与下面两个重要的标志(SOCK_NONBLOCKSOCK_CLOEXEC)相与的值。

SOCK_NONBLOCK表示将新创建的socket 设为非阻塞的。

SOCK_CLOEXEC表示用fork调用创建子进程时在子进程中关闭该socket 

protocol 参数是在前两个参数构成协议集合下,再选择一个具体的协议,这个值通常唯一(前两个参数已经决定它的值),几乎所有情况下设置为0,表示使用默认协议。

socket系统调用成功时返回socket文件描述符,失败则返回 -1,设置errno

 

5.3 命名(绑定) socket

将一个socketsocket地址绑定称为给socket命名。

在服务器程序中,我们通常要命名socket,因为只有命名后客户端才能知道如何连接它。

客户端通常不需要命名socket,而是采用匿名方式,即使用操作系统自动分配的socket地址。

命名socket的系统调用 bind

#include<sys/types.h>

#include<sys/socket.h>

int bind(int sockfd ,const struct sockaddr* my_addr , socklen_t addrlen ) ;

 

bind将 my_addr 所指的socket 地址分配给未命名的sockfd 文件描述符。

addrlen 参数指出该socket地址的长度。

bind成功时返回0,失败则返回-1并设置errno

其中两种常见的errnoEACCESEADDRINUSE,含义如下:

EACCES:被绑定的地址是受保护的地址,仅超级用户能够访问。

(例如:普通用户将socket绑定到知名服务端口(0-1023)时,bind将返回EACCES错误。)

EADDRINUSE:被绑定的地址正在使用中。

(例如:将socket绑定到一个处于TIME_WAIT状态的socket地址)。

 

5.4 监听socket

#include<sys/socket.h>

int  listen ( int  sockfd , int  backlog) ;

sockfd参数指定被监听的socket

backlog参数提示内核监听队列的最大长度。

监听队列的长度如果超过backlog(监听队列中完整连接的上限通常比backlog值略大),服务器将不受理新的客户连接,客户端也将受到ECONNREFUSED错误信息。

注意:backlog只表示处于完全连接状态的socket的上限,处于半连接状态的socket的上限则由 /proc/sys/net/ipv4/tcp_max_syn_backlog内核参数定义。

backlog参数的典型值为5

listen成功时返回0,失败则返回-1并设置errno

 

5.5 接受连接

下面的系统调用从listen监听队列中接受一个连接:

#include<sys/types.h>

#include<sys/socket.h>

int accept( int sockfd , struct  sockaddr  *addr , socklen_t  *addrlen);

 

sockfd参数是执行过listen系统调用的监听socket

addr 参数用来获取被接受连接的远端socket地址,该socket地址的长度由addrlen参数指出。

accept成功时返回一个新的连接socket,该socket唯一地标识了被接受的这个连接,服务器可通过读写该socket来与被接受连接对应的客户端通信。

accept失败时返回-1并设置errno

 

如果监听队列中处于ESTABLISHED状态的连接对应的客户端出现网络异常(掉线),或提早退出,那么服务器对这个连接执行的accept调用依然成功。

因此注意:accept只是从监听队列中取出连接,而不论连接处于何种状态,更不关心任何网络状况的变化。

 

5.6发起连接

服务器通过listen系统调用被动接受连接,客户端通过connect系统调用来与服务器建立连接。

#include<sys/types.h>

#include<sys/socket.h>

int connect (int sockfd, const struct sockaddr *serv_addr , socklen_t addrlen) ;

sockfd参数由socket系统调用返回一个socket

serv_addr参数是服务器监听的socket地址。

addrlen参数指定这个地址的长度。

connect成功时返回0,一旦成功建立连接,sockfd就唯一标识了这个连接,客户端就可以通过读写sockfd来与服务器通信。

connect失败则返回-1并设置errno

其中两种常见的errno是:

1. ECONNREFUSED:目标端口不存在(客户端收到复位报文段),连接被拒绝。

2. ETIMEOUT:连接超时(网络繁忙导致服务器没有发回应答报文,客户端不断重连,多次重连仍然无效则连接超时)。

 

5.7 关闭连接

关闭一个连接实际就是关闭该连接对应的socket,可以通过如下系统调用完成:

#include<unistd.h>

int  close( int  fd ) ;

fd 参数是待关闭的socket。不过,close系统调用并非总是立即关闭一个连接,而是将fd 的引用计数减一。只有当fd 的引用计数为0时,才真正关闭连接。

多进程程序中,一次fork 系统调用默认将使父进程中打开的socket的引用计数加1因此我们必须在父进程和子进程中都对该socket执行close调用才能将连接关闭。

如果无论如何都要立即终止连接(而不是将socket的引用计数减1),可以使用如下shutdown系统调用。

#include<sys/socket.h>

int  shutdown ( int  sockfd , int  howto ) ;

sockfd 参数是待关闭的socket 

howto 参数决定了shutdown的行为可取下面表中的某个值:


由此可见,shutdown能够分别关闭socket上的读或写,或者都关闭。

close在关闭连接时只能将socket上的读和写同时关闭。

shutdown成功时返回0,失败则返回-1并设置errno 

 

5.8 数据读写

5.8.1  TCP数据读写

对文件的读写操作readwrite同样适用于socket

socket编程接口提供了几个专门用于socket数据读写的系统调用,它们增加了对数据读写的控制,其中用于TCP流数据读写的系统调用是:

#include<sys/types.h> 

#include<sys/socket.h>

ssize_t  recv ( int  sockfd  , void *buf ,  size_t  len ,  int  flags );

ssize_t  send ( int  sockfd  , const void *buf , size_t  len , int  flags);

recv 读取 sockfd上的数据,buflen参数分别指定读缓冲区的位置和大小。

flags 参数含义见后文,通常设置为0即可。

recv成功时返回实际读取到的数据的长度。

recv可能返回0,这意味着通信对方已经关闭连接了。

recv出错时返回-1并设置errno 

flags参数为数据收发提供了额外的控制,它可以取下表中的一个或几个的逻辑或


 

5.8.2 UDP数据读写

socket编程接口中用于UDP数据报读写的系统调用时:

#include<sys/types.h>

#include<sys/socket.h>

ssize_t  recvfrom(int sockfd , void *buf,  size_t  len,  int flags, 

struct  sockaddr *src_addr,  socklen_t  *addrlen );

 

ssize _t  sendto ( int sockfd , const  void *buf ,  size_t  len ,int flags ,

 const struct sockaddr *dest_addr  , socklen_t  addrlen ) ;

 

recvfrom 读取sockfd上的数据, buf 和 len 参数分别指定读缓冲区的位置和大小。因为UDP通信没有连接的概念,所以我们每次读取数据都需要获取发送端的socket地址,即参数src_addr所指的内容,addrlen参数则指定该地址的长度。

 

sendto sockfd 上写入数据,buf 和 len 参数分别指定缓冲区的位置和大小。dest_addr参数指定接收端的socket地址,addrlen参数则指定该地址的长度。

 

这两个系统调用的flags参数以及返回值的含义均与 send/recv系统调用的flags参数以及返回值相同。

注意:recvfrom / sendto 系统调用也可以用于面向流(STREAM)的socket的数据读写,只需要把最后两个参数都设置为NULL以忽略 发送端/接收端的 socket地址(因为已经建立了连接,已经知道socket地址)。


5.8.3  通用数据读写函数

socket编程接口还提供了一对通用的数据读写系统调用。

可用于TCP流数据 和 UDP数据报。

#include<sys/socket.h>

ssize_t  recvmsg ( int  sockfd , struct msghdr *msg , int flags ) ;

ssize_t  sendmsg ( int  sockfd , struct msghdr *msg , int flags ) ;

sockfd 参数指定被操作的目标socket, msg参数是msghdr结构图类型的指针,msghdr结构体的定义如下:


struct  msghdr{

void *msg_name ;  / * socket 地址 */

socklen_t  msg_namelen  /* socket 地址的长度 */

struct  iovec *msg_iov ;  /* 分散的内存块,见后文 */

int  msg_iovlen ;  /* 分散内存块的数量 */

void  *msg_control ; / * 指向辅助数据的起始位置 */

socklen_t  msg_controllen ;  /* 辅助数据的大小*/ 

int  msg_flags ;/*复制函数中的flag参数,并在调用过程中更新*/

};


msg_name成员指向一个socket地址结构变量,它指定通信双方的socket地址。对于TCP,该成员必须被设置为NULL。这是因为对数据流socket而言,对方的地址已经知道。

msg_namelen成员则指定了msg_name所指socket地址的长度。

msg_iov成员是iovec结构体类型的指针,iovec结构体的定义如下:


struct  iovec{

void  *iov_base ; /*内存起始地址 */

size_t  iov_len ;  /* 这块内存的长度 */

};

msg_iovlen 指定这样的iovec结构对象有多少个。

对于recvmsg而言,数据将被读取并存放在msg_iovlen块分散的内存中,这些内存的位置和长度则由msg_iov指向的数组指定,这成为分散读;对于sendmsg而言,msg_iovlen块分散内存中的数据将被一并发送,这称为集中写。

msg_controlmsg_controllen 成员用于辅助数据的传送。

msg_flags 成员无须设定,它会复制recvmsg/sendmsgflags参数的内容以影响数据读写过程。recvmsg还会在调用结束前,将某些更新后的标志设置到msg_flags中。

recvmsg/sendmsg flags参数以及返回值的含义与send/recvflags参数及返回值相同。

 

5.9 带外标记

Linux内核检测到TCP紧急标志时,将通知应用程序有带外数据需要接收,内核通知应用程序带外数据到达得两种常见方式是:I/O复用产生的异常事件和SIGURG信号。

但是,即使应用程序得到了有带外数据需要接收的通知,还需要知道带外数据在数据流中的具体位置,才能准确接收带外数据。

可用如下系统调用实现:

#include<sys/socket.h>

int  sockatmark ( int  sockfd ) ;

sockatmark 判断 sockfd 是否处于带外标记,即下一个读取到的数据是否是带外数据。如果是,sockatmark 返回1,此时我们就可以利用带MSG_OOB标志的recv调用来接收带外数据。如果不是,则sockatmark返回0

 

5.10 地址信息函数

当我们想知道一个连接socket的本端socket地址,以及远端的socket地址,可调用这两个函数:

#include<sys/socket.h>

int getsockname ( int  sockfd ,  struct  sockaddr  *address ,  socklen_t  *address_len ) ;

int getpeername ( int  sockfd ,  struct  sockaddr  *address ,   socklen_t  *address_len ) ;

getsockname获取sockfd对应的本端socket地址,并将其存储于address参数指定的内存中,该socket地址的长度则存储于

address_len参数指向的变量中。如果实际socket地址的长度大于address所指内存区的大小,那么该socket地址将被截断。

getsockname成功时返回0,失败返回-1并设置errno

getpeername获取sockfd对应的远端socket地址,其参数及返回值的含义与getsockname的参数及返回值相同。

 

5.11    socket选项

下面两个系统调用则是专门用来读取和设置socket文件描述符属性的方法:

#include<sys/socket.h>

int  getsockopt ( int  sockfd , int  level , int  option_name , 

void *option_value ,  socklen_t  *restrict  option_len ) ;

int  setsockopt ( int  sockfd , int level ,  int  option_name , 

const  void *option_value , socklen_t  *option_len );

sockfd参数指定被操作的目标socket。 

level 参数指定要操作哪个协议的选项(即属性,比如IPv4IPv6TCP等)。

option_name 参数则指定选项的名字。

下表列举了socket通信中几个比较常用的socket选项,option_valueoption_len参数分别是被操作选项的值和长度,不同选项具有不同类型的值。



getsockoptsetsockopt 这两个函数成功时返回0,失败时返回-1并设置errno 

注意,对服务器而言,有部分socket选项只能在调用listen系统调用前针对监听socket设置才有效。这是因为连接socket只能

accept调用返回,而acceptlisten监听队列中接受的连接至少已经完成了TCP三次握手的前两个步骤(因为listen监听队列中

的连接至少已进入SYN_RCVD状态),说明服务器已经往被接受连接上发送出了TCP同步报文段。但是,有的socket选项却应

该在TCP同步报文段中设置(比如TCP最大报文段选项。)对这种情况,Linux给开发人员提供的解决方案是:对监听socket

置这些socket选项,那么accept返回的连接socket将自动继承这些选项。

这些socket选项包括:SO_DEBUG , SO_DONTROUTE , SO_KEEPALIVE , SO_LINGER , SO_OOBINLINE , SO_RCVBUF , 

SO_RCVLOWAT , SO_SNDBUF , SO_SNDLOWAT , TCP_MAXSEG ,

和 TCP_NODELAY 。 

对客户端而言,这些socket选项则应该在调用connect 函数之前设置,因为connect调用成功返回之后,TCP三次握手已完成 。

 

5.11.1    SO_REUSEDDR选项

TCP连接处于TIME_WAIT状态时,服务器程序可以通过设置socket选项SO_REUSEADDR来强制使用处于TIME_WAIT状态的连接占用的socket地址。

经过setsocketopt的设置之后,即使sock处于TIME_WAIT状态,与之绑定的socket地址也可以立即被重用。此外,我们也可以

通过修改内核参数/proc/sys/net/ipv4/tcp_tw_recycle来快速回收被关闭的socket,从而使得TCP连接根本就不进入TIME_WAIT

态,进而允许应用程序立即重用本地的socket地址。

 

5.11.2   SO_RCVBUFSO_SNDBUF选项

SO_RCVBUFSO_SNDBUF选项分别表示TCP接受缓冲区和发送缓冲区的大小。

当我们用setsockopt来设置TCP的接受缓冲区和发送缓冲区的大小时,系统都会将其值加倍,并且不得小于某个最小值。

TCP接受缓冲区的最小值是256字节,而发送缓冲区的最小值是2048字节(不同系统可能有不同的默认最小值)。

系统这样做的目的,主要是确保一个TCP连接拥有足够的空闲缓冲区来处理阻塞(比如快速重传算法就期望TCP接受缓冲区

至少容纳4个大小为SMSSTCP报文段)。

注意:我们可以直接修改内核参数 /proc/sys/net/ipv4/tcp_rmem 

/proc/sys/net/ipv4/tcp_wmem 来强制TCP接收缓冲区和发送缓冲区的大小没有最小值限制。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【读书笔记】Linux高性能服务器编程(第二篇 第五章) 的相关文章

随机推荐

  • S7-200 PLC的CPU模块介绍

    更多关于西门子S7 200PLC内容查看 西门子200系列PLC学习课程大纲 课程筹备中 1 什么是西门子200PLC的CPU 如下图1 1所示 S7 200 PLC CUP是将一个微处理器 一个集成电源 一定的数字量或模拟量I O 一定的
  • 一、markdown 常见公式

    文章目录 公式 字符 希腊字符 三角 对数 指数函数 向量 矩阵 省略号 空格 运算符 累加累积 开方 极限 导数 微积分 括号 绝对值 范数 分段 逻辑运算符 集合运算符 箭头 上下标符 公式 字符 希腊字符 字符 Markdown 字符
  • CSP-J (NOIP普及组) 历年复赛真题考察内容(1998~2021)

    TZOJ题目分类 本博客原文地址 https www cnblogs com BobHuang p 14522022 html 其中 1 较简单题26题左右 2 动态规划17题 其中9题较好做 3 模拟 阅读题目将问题抽象建模写出程序 为1
  • 华为机考模拟题

    一 字符串平均重量 we have defined the necessary header files here for this problem If additional header files are needed in your
  • 嵌入式ERPC框架正式发布了

    一 ERPC开发的原由 随着科技日新月异的快速发展 电子产品的功能越来越多 业务也越来越复杂 以前靠单打独斗完成电子产品的研发的时代已经慢慢远去 更多的是靠一个团队协作共同努力才能完成 这就为电子产品的设计和研发带来了新的问题 团队的协作
  • RDF与可视化【转载】

    Rudolf RDFViz Exploring tools for RDF Graph Visualisation http www ilrt bris ac uk discovery rdf dev rudolf rdfviz To vi
  • 网站遭遇CC及DDOS攻击紧急处理方案

    检测访问是否是CC攻击的命令 80口为网站的访问端口 可以根据实际情况进行修改 netstat anlp grep 80 grep tcp awk print 5 awk F print 1 sort uniq c sort nr head
  • 华为OD机试真题-矩阵元素的边界值-2023年OD统一考试(B卷)

    题目描述 给定一个N M矩阵 请先找出M个该矩阵中每列元素的最大值 然后输出这M个值中的最小值 补充说明 N和M的取值范围均为 0 100 示例1 输入 1 2 3 4 输出 3 说明 第一列元素为 1和3 最大值为3 第二列元素为 2和4
  • python奥特曼代码_【Python】奥特曼VS小怪兽、大战300回合

    上一节 lt gt 上一节我们学了类的基本使用 为了巩固学习 今天继续使用 类 来编程 好好学习 天天打卡 今天我们要研究的是 奥特曼打小怪兽 游戏策划 不要觉得没有用 题目 写出一个 奥特曼 和 小怪兽 类 基本属性有名字 生命值和魔法值
  • 服务的分类

    从技术实现角度和业务角度切入 来梳理服务的类别与层次 1 服务的基本类别 工具 实体和任务 工具服务 标准Api的封装 如JMS JDBC 公共功能区域的提炼 如消息传递 HTTP服务 安全性 非功能性需求的抽取 如拓展性 可用性 以及常见
  • Android Property System

    属性系统是android的一个重要特性 它作为一个服务运行 管理系统配置和状态 所有这些配置和状态都是属性 每个属性是一个键值对 key value pair 其类型都是字符串 从功能上看 属性与windows系统的注册表非常相似 许多an
  • 浅谈深拷贝和浅拷贝的区别

    举例 假设B复制了A 当修改A时 看B是否会发生变化 如果B也跟着变了 说明这是浅拷贝 如果B没变 那就是深拷贝 阐述一下栈堆 基本数据类型与引用数据类型 因为这些概念能更好的让你理解深拷贝与浅拷贝 一 预备知识 两种不同数据类型的值 基本
  • 静态/动态博客Blog框架简介

    简言之 Blog就是以网络作为载体 简易迅速便捷地发布自己的心得 及时有效轻松地与他人进行交流 再集丰富多彩的个性化展示于一体的综合性平台 博客搭建的方式 总体来说 Blog搭建的方式可以分为静态博客和动态博客 顾名思义 静态博客 博文内容
  • css强制一行显示超出的部分显示点点点

    要达到的效果 1 首先盒子应该有固定的宽度 width 200px 2 设置 溢出隐藏 overflow hidden 3 设置文本强制在一行显示 white space nowrap 4 设置文本溢出显示省略号 text overflow
  • js判断多个数组之间是否存在交集

    代码如下 定义一个二维数组 数组中包含N个数组 const arrays 1 2 3 4 5 6 2 7 8 9 10 2 把数组传入进来 function checkIntersection arrays for let i 0 i lt
  • 【小程序】那些年小程序碰过的坑——Scroll-view跳转问题

    序 写到scroll view 就不得不涉及到其中的各个Object属性 但是这个控件本身又很奇特 属性内部经常自相矛盾 着实让人头疼不已 好记性不如烂笔头 记录下来警示自己 也帮助他人 何乐而不为呢 正文 要想Scrolliew进行跳转
  • mat1 and mat2 shapes cannot be multiplied (128x432 and 576x64)的解决

    仅作为记录 大佬请跳过 今天博主在跑大佬博主的pytorch cnn分类mnist数据集的程序 大佬博主文章 第四个网络 出现问题 经过尝试 发现是在定义网络中的问题 将x x view 128 1 改为x x view 1 64 3 3
  • error: struct type mismatch between shaders for uniform (named light)

    openGL系列文章目录 文章目录 openGL系列文章目录 前言 一 错误原因 二 排查错误 1 使用glslangValidator exe工具 2 顶点着色器着色器1 vert1Shader vert 顶点着色器着色器2 vert2S
  • 【GD32F427开发板试用】 CAN总线收发测试

    本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动 更多开发板试用活动请关注极术社区网站 作者 meijing 这篇测试下CAN通信的收发测试 代码使用库例程中修改 硬件部分 测试用到了CAN0 串口0和定时器1 1 gt
  • 【读书笔记】Linux高性能服务器编程(第二篇 第五章)

    第五章 Linux网络编程基础API 5 1 socket地址API 5 1 1 主机字节序和网络字节序 字节序分为 1 大端字节序 一个整数的高位字节 23 31 bit 存储在内存的低地址处 低位字节 0 7 bit 存储在内存的高地址