orbi 组网介绍

需求 为了优化家庭网络,购买了 Orbi rbk 753 翻新套装,进行网络搭建。 解决 主路由 Rbr 750 主路由上接猫,下接交换机,配置和普通的路由器一样配置,基本与先前的 R7000 一样的配置流程。就是只有正面一个 LED 显示,没有那么直观,并且开机时间有点太长了,需要好几分钟。 无线回程 Rbs 750 一号分机放在信号好的地方,所以直接使用了无线回程,配置起来比较简单。 web 端,base 选择增加分机,然后下一步。 等1号分机 LED 显示稳定的蓝光的时候,按下背面的 sync 按下网页的 sync. 稍等就同步完成了。 实际过程中,还是碰到了一些比较奇怪的事情。 一开机的时候,网络信号是 Orbi750 配置过程中,网络信号大概是 Netgear-hidden-45,类似这样的。 最后才正常了。 有线回程 Rbs 750 2号分机在信号不好的地方,所以考虑使用有线回程。但是实际过程中很麻烦。 直接放在信号不好的地方 使用有线,直接放在信号不好的地方,使用网页 sync 来同步,但是信号一直保持 Orbi750。 然后使用 app 来同步,一直是找不到这台分机。 先同步再有线 然后我把机器先拿到信号好的地方,使用网页 sync 来同步,可以成功,说明机 器本身没有问题。 但是我 sync 后,把机器放到信号不好的地方,使用有线,结 果还是连不上。并且 web 端,接入设备的显示也比较奇怪,接入状态都是有问题的。 但是我 ping 这个分机能 ping 通。 排查问题 我把分机拿到主机旁边,使用有线直连,可以正常识别出来。但是我发现了一个问题,固件版本不一致。其他的都是 4.x.16, 这个分机是 4.x.9. 我怀疑可能是版本的问题,直接升级了固件。 固件升级好之后,也是能够连接上的情况。 把网线插在交换机上,从直连变成通过交换机连接。结果也是正常的。 把分机拿到信号不好的地方,使用网线,现在也是能够正常连接上了。 总结:...

<span title='2024-05-09 08:05:00 +0800 CST'>2024-05-09</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;160 words&nbsp;·&nbsp;RamLife

t-mobile esim 开通

需求 申请 t-mobile 号码 解决 手机准备 准备一台支持 esim 的手机。这个一般海外手机会支持这个功能,比如说 pixel 系列。 App 渠道申请 下载 t-mobile prepaid esim app 输入邮箱地址 根据你选择后的地址,输入地址区号. 这个可以使用美国地址生成器或者网站查找。 尽量选择免税州. 选择最便宜的 $10 套餐 输入 visa 卡,可以使用国内的双币卡 创建一个 pin, 用于后续使用。 付款 等待 app 自动下载并配置好 esim 网站后续 网站上申请账号,需要输入前面的 pin 设置好 911 地址。 手机设置 手机配置中,设置好 wifi calling. 开启飞行模式,只打开 wifi. 其他 一个手机如果有多个 t-mobile,后面申请的时候,可能会自动安装 esim 失败。 这个就需要找 小蓝鸟上面的客服,让客服那边帮忙,重新下载安装一次即可。 可以找第三方去办理 $0 月租的套餐,大约 700 元一个账号。 一定要注意 911 地址,如果这个地址有问题,那么就不能 wifi calling,只能 emergency calling. 参考 T-Mobile原生手机卡购买激活教程,eSIM激活教程,改3美元套餐,转入手机靓号 itangtalk 3款eSIM国外电话卡,0月租,保号成本低,适合在中国漫游使用 美国区号与州及地名对应查询表 美国地址生成器 美国邮编 全面解析美国免税州 2022年最新美国免税州及各州州税介绍 extrabux 全网最详细的eSIM使用教程 【领取1....

<span title='2024-03-09 10:15:00 +0800 CST'>2024-03-09</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;101 words&nbsp;·&nbsp;RamLife

P13 select 模型上

电脑承受进程数量 1核2GB 可以大约 100 多进程或线程 普通服务器大概是 10倍性能,即 1000 多进程或线程。 IO 多路复用能力 1个进程或线程可以使用多个 TCP。 单进程 select 可以 1024 单进程 poll 可以数千 单进程 epoll 可以百万 fdset 本质上是 int[32] 组成的 bitmap, 刚好对应 1024 个文件描述符。 参考 IO 多路复用 select 模型上

<span title='2023-09-24 18:05:00 +0800 CST'>2023-09-24</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;36 words&nbsp;·&nbsp;RamLife

P6 万恶的结构体

客户端和服务端字符串转字节为啥不一样? 客户端 struct hostent* h; // 用于存放服务端IP地址(大端序)的结构体的指针。 if ( (h = gethostbyname(argv[1])) == nullptr ) // 把域名/主机名/字符串格式的IP转换成结构体。 { cout << "gethostbyname failed.\n" << endl; close(sockfd); return -1; } memcpy(&servaddr.sin_addr,h->h_addr,h->h_length); // ③指定服务端的IP(大端序)。 服务端 servaddr.sin_addr.s_addr=inet_addr(argv[1]); // ③指定服务端的IP,只能用IP,不能用域名和主机名。 原因 inet_addr 这个只能转换 ip 地址 gethostbyname 可以支持 IP,域名等等。 对于服务端来说,给他 ip 地址就够了。但是对于客户端来说,他需要连接的更多的可能是域名,而不是 IP。 服务端为什么也要设置 IP 如果服务端是运行于多网卡的服务器上面,那么必须要明确当前这个服务端是服务于那一个网段的,通过设置 servaddr.sin_addr.s_addr 就可以确定服务的网段了。 参考 万恶的结构体

<span title='2023-09-24 18:05:00 +0800 CST'>2023-09-24</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;54 words&nbsp;·&nbsp;RamLife

P12 TCP 缓存

getsockopt 作用 可以获取 socket 缓冲区大小。 int bufsize = 0; socklen_t optlen = sizeof(bufsize); getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, &bufsize, &optlen); // 获取发送缓冲区的大小。 cout << "send bufsize=" << bufsize << endl; getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, &bufsize, &optlen); // 获取接收缓冲区的大小。 cout << "recv bufsize=" << bufsize << endl; send 会阻塞吗? 如果发送太快,接受太慢,send 也会被阻塞。 send 之后,立即关闭,能接收到吗? 可以的,因为 send 是把数据写入到内核中的发送缓冲区,就算立即关闭,也能接收到。 nagle 算法 在发送一个小于 MSS 长度的包之后,必须收到回复,才能发下一个小包。保证了网络中小包数量得到控制。一般 ACK 大概 40ms. 接收延迟:在接受到一个包之后,会等待 40ms,才会发送 ACK, 这样可以尽量和应用层的回复数据合一个包。 在联机游戏,证券交易等应用场景,时效性要求高,一般会禁用 Nagle 算法。开启 TCP_NODELAY 选项。 #include <netinet/tcp....

<span title='2023-09-23 18:05:00 +0800 CST'>2023-09-23</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;82 words&nbsp;·&nbsp;RamLife

P11 三握四挥

netstat 使用 netstat 可以用来查看 socket 状态。 安装 如果系统里面没有,那么 yum install net-tools -y 可以安装。 使用 使用 netstat -a | less 或者 netstat -a | more 来查看。 上半部分的信息是 TCP 相关,下半部分是进程间的 socket 通信。 可以观察右边的 listen, established 等状态。 客户端的 port 是客户端系统随机选择的,不要去太过于关注。 port 使用限制 普通权限只能使用 1024 以上端口 root 可以使用 1024 以下的端口。 close 状态是假想状态,不存在的。 listen 的第二个参数作用 第二个参数 + 1 是 established 队列的大小,超过队列的客户端连接状态为 SYN_RCV. 三次握手 客户端申请向服务端的通道 服务端接受了,并且向客户端申请通道 客户端接受了,并给出回应 这样经过三次握手,双方建立了双向通道 四次挥手 A 请求关闭 B 同意关闭单向通道 B 把剩余的数据全部发送。 B 向 A 发送关闭请求 A 发送回应 主动端开的 A 在最后的回应后有个 TIME_WAIT 状态,时间为 2MSL,一个 MSL 在 30秒到1分钟。 服务端主动关闭 会导致 socket 释放,并且 2MSL 后端口才能重用。否则会有 bind 错误提示,并且在 netstat 中可以看到处于 TIME_WAIT 状态。 可以使用 setsockopt 函数,在 bind 之前,可以防止 bind 错误。 客户端主动关闭 没有危害,因为客户端的端口是随机分配端口号的,并且一般用的不多,够用了。...

<span title='2023-09-22 18:05:00 +0800 CST'>2023-09-22</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;107 words&nbsp;·&nbsp;RamLife

P10 多进程服务端

signal(i, SIG_IGN) 作用 // 忽略全部的信号,不希望被打扰。顺便解决了僵尸进程的问题。 for (int ii=1;ii<=64;ii++) signal(ii,SIG_IGN); kill(0, SIGTERM) 作用 kill(0,SIGTERM); // 向全部的子进程发送15的信号,通知它们退出。 因为在子进程中 SIGTERM 是退出信号。 父进程关掉 clientsocket, 子进程关掉 listensocket int pid=fork(); if (pid==-1) { perror("fork()"); return -1; } // 系统资源不足。 if (pid> 0) { // 父进程。 tcpserver.closeclient(); // 父进程关闭客户端连接的socket。 continue; // 父进程返回到循环开始的位置,继续受理客户端的连接。 } tcpserver.closelisten(); // 子进程关闭监听的socket。 在 /proc/xxx 下面的是进程相关资源,里面的 fd 是根据当前最小可用来递增的。 如果父进程不关 clientsocket, 那么每次 accept 之后,fd 都需要增加一个,然后在后面的 fork 中,这些递增的 fd 也都会被复制到子进程中。对于子进程来说,它不需要其他子进程相关的 =clientsocket=,所以完全是浪费资源。 子进程只需要自己对应的 clientsocket, 不需要 listensocket. listensocket 是专门给父进程用来监听的。 子进程结束需要 return return 0; // 子进程一定要退出,否则又会回到accept()函数的位置。 如果子进程结束没有 return=,让进程退出,那么会在 =while 中重新回到 accept 这个地方,进行了监听,然后 fork 了。...

<span title='2023-09-21 18:05:00 +0800 CST'>2023-09-21</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;88 words&nbsp;·&nbsp;RamLife

P9 文件传输

为什么 TCP 是可靠的通信,服务端每一步都要有回复。 因为服务端可能没有 accept 或者可能忙于其他事情,bug 掉线等等。所以虽然 TCP 本身是可靠的,但是服务器可能是不可靠的。所以为了业务是正常的,就必须在服务端正常应答的情况下,才能继续。 读取文件使用 ifstream 操作 字符串拼接报错? 一般情况都是只有 char * 和 char * 拼接,才会报错。只要语句中有一个 string, 拼接就不会报错。 参考 实现文件传输功能

<span title='2023-09-20 18:05:00 +0800 CST'>2023-09-20</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;24 words&nbsp;·&nbsp;RamLife

P7 封装 socket 客户端

封装时 send 函数为啥使用 const string & buf 作为参数类型 bool send(const string &buffer) // buffer不要用const char * const string & buf 既可以接收 string 也可以接受 char *. 估计是对 char * 进行了隐式转换成了 string. send 函数的参数为啥是 buf.data() 和 buf.size(), 而不是 buf.c_str() 和 buf.length if ((::send(m_clientfd,buffer.data(),buffer.size(),0))<=0) return false; buf.data() 和 buf.c_str() 都指向的是内容的指针,但是语义不一样。 data 表示的就是原始数据,而 c_str 表示的是转化为 char *. buf.size() 的语义是原始内容的大小,而 length 表示的是字符串的长度。 所以都是因为语义不合适。虽然都能用,但是确实不合适。 recv 函数的参数为啥是 & buf[0] 而不是 buf.c_str() 或者 buf.data() int readn=::recv(m_clientfd,&buffer[0],buffer.size(),0); // 直接操作buffer的内存。 因为后面两种返回的都是带有 const, 只有 &buf[0] 才没有 const, 适合对内容进行修改。...

<span title='2023-09-19 18:05:00 +0800 CST'>2023-09-19</span>&nbsp;·&nbsp;1 min&nbsp;·&nbsp;125 words&nbsp;·&nbsp;RamLife