UNP 学习笔记 2:socket 编程简介

单个地址结构体

  • 一个 TCP 套接字是用一对 ip:port 的四元组构成的,我们使用结构体保存一个 ip:port, 这个结构体是 sockaddr_in 和 sockaddr_in6.
  • 对于数据链路层的套接字,这个结构体是 sockaddr_dl, 地址则是和 (ip:port) 相呼应的链路层地址。
  • 我们为了支援所有结构体,提供通用的 connect 和 socket 函数调用,历史由于没有 void* (前 ANSI C 时期),所有的函数调用都带有第三个参数,which is 用来传结构体的 size,以用于 copyin 操作。为了传参还设计了 sockaddr 结构体,主要指定了 16 bytes 的通用结构体大小,主要是支援各种非网际套接字(比如 sockaddr_x25)。
  • 但是后来出了 ipv6,我们都知道 ipv6 地址是128bit 的,所以原来的结构体又用不了了,则又弄了一个 sockaddr_storage. 还有一个点是他们(4.3BSD)给结构体加了一个长度字段,但是 POSIX 并没有给idin,所以在我用的 Ubuntu 的 in.h 并没有定义这个字段(而 family 字段还是用的 short 即纯 8bytes),当然可以接受因为本来你为了兼容也没办法去掉 connect 等函数的第三个参数了,添加这个 8bytes 实际更加浪费,不如算了。
  • 值-结果参数用于内核填写结构体时溢出检查,如 getpeername 的最后一个参数,这个参数是用引用传递的,传进去的时候把长度信息告诉内核写的最大尺寸,而内核要把实际写了多少同样通过这个参数的内存传回来。这和 write read 那些的模式好像,但是他们是 return 了写了多少,只是风格不同,而且可能返回值要用来指示状态!

大端小端

  • 大端小端会反映在 union 上,结构体上,以及各种超过 1byte 的类型上。
  • 不同主机使用的字节序是不一样的,这就需要标准化了,网络上直接指定是大端序,这样数据到达和人看到书写印刷是一样的(高位先行)。
  • host to net 、net to host , short、long 系列函数,定义仍然在 in.h,htons,ntohl,使用 uintXX_t. 进一步标准,所以要规定 short 为 uint16,而 long 为 uint32,which 符合 ILP32 和 S16。
  • 这样一来,我们看到的位序就是高位从 0 编址,即前面说的符合人看到的书写印刷的顺序,实际基带传输的时候就是高位(这里位编号为0)先行.

strings 兼容函数

  • bzero - memset
  • bcopy - memmove,这里搞笑的是一个是 src,dst,一个是 dst,src
  • bcmp - memcmp 比较整个字符串(应该是 O(n))

inet 地址转换函数

  • 字符串是人眼大端没问题,但是我们都知道字符串和实际用 uint8 表示数字是不一样的,所以需要转换。(这下 leetcode 了)
  • 这些转换定义在 arpa/inet.h 中,arpa 是一开始的 arpanet,美国国防部高级研究计划局网络。inet 是互联网了属于是,美国政府,赢!
  • 实际就是点分字符串转 byte 而已,ipv4 的是 32位地址,我们用 in_addr_t 来 alias uint32.
  • 不要使用 inet_addr, 而使用 inet_aton 和 inet_aton, a 的意思可能是 ascii 字符串,n 是 net。而且注意对于 ntoa 来说 The string is returned in a statically allocated buffer, which subsequent calls will overwrite. 所以如果要用必须进行复制。这个也不是线程安全的很容易理解。
  • 支援 v4 v6 使用 inet_pton 和 inet_ntop. 我不知道 p 是什么意思。不过他们也不是通用的,他们很搞笑的需要用 family 来指定进行 v4 还是 v6 转换,为此 UNP 自己写了一套 sock_ntop 等函数,这些函数在源码的 lib 下面,我们之后也沿用他们好了。如果要在项目里用就要具备编译链接知识了,虽然读了编译链接和库,但是还是很模糊的印象了属于。

循环读写

  • read 或者 write(non-blocking)的时候会有提前返回不是错误但是任务未完成的情况,所以常常需要 wrap 一个 while loop 在外面,block 的 write 不会有这种情况。不过实际我们可能会使用 不block 的write,所以统一包装了 writen 和 readn,当然,read 真的没得 read 的时候,我们就真的返回就行了(which means 一个循环里 read 的确返回了 0)。
  • 区分没得 read 和暂时的 syscall 不 block 返回的标记是 errno 和返回值,如果是 EINTR 且 -1 就是读的过程中触发了中断,如果返回 n 小于我们要求读的,应该就是 EAGAIN errno,直接迭代循环就行了 ,如果 read 返回 -1 且不是 interrupt 就是出事了,此时我们也返回 -1. read 返回 0 则意味着没得读了, EOF 了。

文本行读写与缓冲区

  • 因为很多协议都是基于文本行的,比如 HTTP,SMTP,和 PTP 的控制连接协议,我们之前说了 TCP 这个东西必须要应用层自己搞一套分装机制,很容易的方案是用\r\n,谁也的确被用着。
  • finger 服务,这个是一个古早的服务,我觉得有必要知道他是个啥,他实际上是用来给其他用户查询本机的用户信息的,在早期的UNIX系统上,很多学生,研究人员利用finger服务来公开自己的某些特定信息,以便进行交流。比如有些研究人员用其公开自己的课题研究情况,有些老师公开一些课程时间安排。还有的主机播报球赛的情况,有的发布一些当前地震活动的信息。不过,finger服务更多的用处的是公开某人的特定信息。在七十年代中期,卡内基-梅窿大学计算机系安装了一台可口可乐机,后来这个系扩大,从这个机房分出一些程序员到不同楼层的办公室。他们常常走了很远,来到那台机器旁才发现没有可口可乐。或者同样糟糕,他们发现在机器中有可口可乐,但时间不够,还是热的。 他们的解决办法是:在机器中安装开关,以判断可得到多少瓶碳酸饮料,并跟踪还需多长时间每瓶饮料才冷却。为了让人们远距离检查这台机器,设立了一个专用Finger服务器。当你Finger一个专用的用户标识(coke),它将显示这台coke机器容量的状态。(什么美帝美好生活!),现代网络规模和安全考虑都不用公网地址了,NAT 的存在,别人是无法打进来的。(什么小班教学)。
  • 前面实现了 readn 和 writen,但是readline 不是一个循环就能实现的(要判断换行符),我们知道每 read 一个 byte 都会引发 trap 和 context switch,which is painfully slow,所以必须构建缓冲区等待换行符,随后返回一行数据给上层。一种实现思路就是去处理 read 一次读一个缓冲区大小,这里就涉及处理各种可能性了。很麻烦,我们直接用 unp 写好的吧。但是还有一个问题是由于存在缓冲区的问题,读了 readline 之后再 readn,效果不如预期是必然的,我们必须统一缓冲区的访问,这样要求改写 readn,但是我们可以不交叉使用这两种函数,从而达到效果。不用 stdio 可能涉及其他的问题,因为缓冲区是不可见的无法提前辨别恶意请求。(什么 defensive programing,只是OS、CSAPP之后第三次看到这个词) 。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值