0%

本文基于自己的理解,试图让猪都能理解补码的概念^_^。老规矩,先是几个概念。

机器数

机器数是将符号数字化的数,最高位是符号位,正数符号位为0,负数符号位为1,其余位表示数值。

真值

机器数所对应的实际数值。 

编程语言类型

无符号数

全部二进制位均代表数值,没有符号位。

有符号数

最高位代表符号位(“0”表示“+”,“1”表示“-”),其余位表示数值。

阅读全文 »

TCP

传输控制协议(Transmission Control Protocol,TCP)是一种面向连接的、可靠的、基于字节流的传输层通信协议。

TCP协议位于四层模型中的传输层

TCP报文

源端口:16bit,共65536个端口,端口0有特殊含义,不能使用,这样可用端口最多只有65535。

目的端口:16bit,接收方的端口号。

序号:32bit,可靠传输服务的关键字段。序号(sequence number, seq)是报文段首字节的字节流编号,TCP把数据看成是有序的字节流,TCP会隐式地对数据流的每个字节进行编号。第一个包的编号由本地随机产生,到达2^32-1后会再回到0。客户端、服务器端有各自的seq序号。序号主要用来解决网络包乱序(reordering)问题

确认号:32bit,可靠传输服务的关键字段。确认号(acknowledgment number,ack)是期望的对方的下一个序号。主要用来解决不丢包的问题

阅读全文 »

网络模型

网络模型常见的有7,4,5层模型。

IP协议位于四层模型中的网络层。

IP报文

版本:IP协议的版本,占4bit,目前的IP协议版本号为4,下一代IP协议版本号为6。注意这里讨论的是IPv4的报文,IPv6的报文已经不长这个样了。

首部长度:IP报头的长度。固定部分的长度(20字节)和可变部分的长度之和。共占4位。最大为1111,即10进制的15,以32bits4字节为单位,代表IP报头的最大长度可以为15个32bits(4字节),也就是最长可为15*4=60字节,除去固定部分的长度20字节,可变部分的长度最大为40字节。

服务类型:Type Of Service。

总长度:IP报文的总长度。报头的长度和数据部分的长度之和。用16位二进制数表示,单位为8bits1字节,因此,IP报文的最大长度为65535字节。但由于MTU的限制,超过的话需要分片传输。所以就必须要有分片相关信息的字段,后面三个就是分片相关的字段。

阅读全文 »

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{
NSString *string = @"";
NSString *ret = [string stringByReplacingCharactersInRange:NSMakeRange(0, 0) withString:@"因吹斯汀"];
NSLog(@"string:%@\nret:%@", string, ret); //因吹斯汀
}
{
NSString *string = @"倒计时开关是";
NSString *ret = [string stringByReplacingCharactersInRange:NSMakeRange(0, 0) withString:@"因吹斯汀"];
NSLog(@"string:%@\nret:%@", string, ret); //因吹斯汀倒计时开关是
}
{
NSString *string = @"倒计时开关是";
NSString *ret = [string stringByReplacingCharactersInRange:NSMakeRange(0, 1) withString:@"因吹斯汀"];
NSLog(@"string:%@\nret:%@", string, ret); //因吹斯汀计时开关是
}
{
NSString *string = @"倒计时开关是";
NSString *ret = [string stringByReplacingCharactersInRange:NSMakeRange(string.length, 0) withString:@"因吹斯汀"]; //倒计时开关是因吹斯汀
NSLog(@"string:%@\nret:%@", string, ret);
}
{
NSString *string = @"倒计时开关是";
NSString *ret = [string stringByReplacingCharactersInRange:NSMakeRange(string.length - 1, 1) withString:@"因吹斯汀"]; //倒计时开关因吹斯汀
NSLog(@"string:%@\nret:%@", string, ret);
}
{
NSString *string = @"倒计时开关是";
NSString *ret = [string stringByReplacingCharactersInRange:NSMakeRange(string.length, 1) withString:@"因吹斯汀"]; //崩溃
NSLog(@"string:%@\nret:%@", string, ret);
}

总结:

传入的range必须满足:(range.location + range.length) <= string.length,否则会发生越界崩溃。

看以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
NSUInteger a = 100;
NSUInteger b = 104;
NSUInteger c = 30;
NSInteger ret = a - b;
if (ret > c) { //ret被隐式转换为无符号整型
NSLog(@"1");
}
if (ret > (NSInteger)c) {
NSLog(@"2");
}
NSLog(@"ret:%ld", (long)ret);
NSLog(@"ret:%lu", (unsigned long)ret);

打印:

1
2
3
2023-04-30 13:10:02.474708+0800 PodDemo[22390:3868894] 1
2023-04-30 13:10:02.474798+0800 PodDemo[22390:3868894] ret:-4
2023-04-30 13:10:02.474845+0800 PodDemo[22390:3868894] ret:18446744073709551612

po 与 p:对象最好使用po,基础数据类型使用p。

swift则会溢出崩溃:swift直接写上面的代码编译都通不过,得拐个弯:

1
2
3
4
5
6
7
8
9
10
11
12
func myoverflow() {
let a: UInt64 = 100
let b: UInt64 = 103
_myoverflow(a, b)
}
func _myoverflow(_ a: UInt64, _ b: UInt64) {
let ret = a - b //Thread 1: Swift runtime failure: arithmetic overflow
let c: Int = 30
if ret > c {
print("sd")
}
}

swift确实是一门安全的语言。

在读了一些框架源码后,个人觉得应该从以下几个方面学习一个框架:

1.框架实现的功能
这个框架是用来干嘛的。

2.框架的工作流程
高屋建瓴的描述一下框架的工作流程。

3.简单的使用框架
做一个小demo。

4.框架使用的基础库
基于哪些系统框架或其他第三方库。

5.框架的设计
架构设计,设计模式,数据结构和算法,类的UML图。

6.框架的实现细节
语言的高级使用,多线程处理,复用,内存优化,CPU优化,边界情况处理等等。

7.框架的优缺点
在对框架达到一定的了解后,明白框架的最佳使用场景及局限性。

8.对框架的改进
尝试解决框架中存在的不足。

大致有下面五种IO模型:

阻塞IO模型,非阻塞IO模型,IO复用模型,信号驱动IO模型,异步IO模型。

阻塞IO模型

读数据,发现没有数据,线程就一直等待直到有数据读取。

非阻塞IO模型

读数据,发现没有数据,线程不等待直接返回读错误码。由于不知道什么时候会有数据所以这种非阻塞IO模型需要调用方不断检查是否有数据。

fd默认是阻塞IO访问,可以使用fcntl函数设置为非阻塞IO:

1
status = fcntl(socketFD, F_SETFL, O_NONBLOCK);

IO复用模型

对非阻塞IO模型的一种改进,因为非阻塞IO模型需要不断检查是否有数据可读。假如有100个fd要读,那么就需要100个线程各自在不断的检查是否有数据可读,这显然浪费了大量的CPU和内存。

阅读全文 »

ifconfig

全称:interfaces config,主要用于设置或显示网络设备信息的。

打印:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384
options=1203<RXCSUM,TXCSUM,TXSTATUS,SW_TIMESTAMP>
inet 127.0.0.1 netmask 0xff000000
inet6 ::1 prefixlen 128
inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1
nd6 options=201<PERFORMNUD,DAD>
gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280
stf0: flags=0<> mtu 1280
en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500
options=50b<RXCSUM,TXCSUM,VLAN_HWTAGGING,AV,CHANNEL_IO>
ether b0:e5:f9:f3:a8:de //mac地址
inet6 fe80::1c2c:2b1d:6af8:3558%en0 prefixlen 64 secured scopeid 0x6 //ipv6地址
inet 10.1.63.198 netmask 0xffffff00 broadcast 10.1.63.255 //ipv4地址、子网掩码、广播地址
nd6 options=201<PERFORMNUD,DAD>
media: autoselect (1000baseT <full-duplex>)
status: active

ifconfig打印的是所有网络设备的信息,并且名称是专有名词比如en0,一般来说如果电脑只连了WIFI那么en0代表的就是WIFI网卡信息,如果还插了网线那么en0代表的就是以太网网卡信息,en1代表的才是WIFI网卡信息。可以配合networksetup -listallhardwareports 查看。

networksetup -listallhardwareports

打印:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Hardware Port: Ethernet
Device: en0
Ethernet Address: b0:e5:f9:f3:a8:de

Hardware Port: USB 10/100/1000 LAN
Device: en4
Ethernet Address: 00:e0:4c:89:fe:55

Hardware Port: Ethernet Adapter (en5)
Device: en5
Ethernet Address: 16:6a:42:01:62:87

Hardware Port: Ethernet Adapter (en6)
Device: en6
Ethernet Address: 16:6a:42:01:62:88

Hardware Port: Wi-Fi
Device: en1
Ethernet Address: b0:e5:f9:f2:56:75

...

networksetup -listallnetworkservices

打印:

阅读全文 »

runloop启动前必须先添加输入源或定时器源,否则runloop一启动就会退出。总之runloop需要有监听的事件,否则就会退出。

启动runloop

NSRunLoop提供了如下3种启动runloop的方法。当然NSRunLoop其实封装的是CFRunloop。

1
2
3
4
5
- (void)run NS_SWIFT_UNAVAILABLE_FROM_ASYNC("run cannot be used from async contexts.");

- (void)runUntilDate:(NSDate *)limitDate NS_SWIFT_UNAVAILABLE_FROM_ASYNC("run(until:) cannot be used from async contexts.");

- (BOOL)runMode:(NSRunLoopMode)mode beforeDate:(NSDate *)limitDate NS_SWIFT_UNAVAILABLE_FROM_ASYNC("run(_:before:) cannot be used from async contexts.");

相同点:如果没有输入源或定时器源附加到runloop上,runloop会马上退出,并且方法也会立即退出。

run
它会让runloop置身在一个永久的循环当中.即使runloop因为处理完了某个输入源事件而退出,该方法又会让它重新运行.因此如果你想处理完某个事件后能够退出runloop,那么你就不能使用该方法了。runloop运行在default模式。

runUntilDate:
和run方法差不多只是多了个截止时间,截止时间到了runloop就会退出。runloop同样运行在default模式。

runMode:beforeDate:
可以指定runloop运行的模式以及一个截止时间。处理定时器源事件后不会退出runloop,但处理输入源事件后会退出runloop,因此需要外部重新驱动进入runloop。

退出runloop

根据官方文档,手动移除输入源或定时器源不能确保runloop会退出。最好的办法是使用runMode:beforeDate:方法启动runloop,设立标志位,给子线程发送消息perform selector。

阅读全文 »