UNP 学习笔记 1:Socket 编程基础

学习一下 UNP,这里做一些笔记以及结合计网学习对一些点给出自己的理解,方便之后自己复习。资料是 UNPv13e 书本,UNP 课本源码。同时参考的还有 APUE 以及 linux manual。

UNP ch1 简介

daytime 客户端程序

      

socket

  • 套接字,支持网络层到其更上层的各种连接。
  • 其中 UNIX域 specify the network layer protocal such as x.25/ipv4/ipv6 etc.
  • the type specify the sock stream:byte or datagram or raw ip.
  • The protocol specifies a particular protocol to be used with the socket. Normally only a single protocol exists to support  a  particular  socket type within a given protocol family, in which case protocol can be specified as 0.  However, it is possible that many protocols may  exist,  in which  case a particular protocol must be specified in this manner.

一些子协议吧,比如一些还有不同版本的?

sockaddr_in

  • 结构体里面可以指定端口和协议家族。
  • 因为这个是 inet 的结构体, 把后 14 字节的 4 + 2 解释为 IP 地址和 TCP/UDP 端口, 注意 IP 必须和 TCP/IP 一起玩, RAW IP 应该只用 IP 地址 4 bytes.)

htons

  •  host to network short (integer), convert a port number,
  • 因为端口号两个 bytes,涉及大小端转换!

inet_pton inet_addr 的升级版

  • inet present to number, convert a ip address from char strings.
  • 就比如网络字节序是大端,而 intel C 是小端,IP 地址是 4 个 bytes,所以区分大端小端:
  • 用一个结构体放4个 bytes 小端的低地址到高地址: 1 0 168 192 大端的:192 168 0 1 (低地址先放 double word 的高位 192)。

connect

  • 把系统给的 fd,用 sockaddr 结构体的信息去创建这个绑定连接。
  • 使用通用的 sockaddr(认为是 sockaddr_in 的基类吧…, 他们的大小是一样的). 这是一个抽象使用接口而不是实现的例子, connect 不仅支持 TCP/IP, 具体实现我们解到了 socket 里面了, 每个 socket 我们通过接口 fd 来访问, 但是他们的类型自己保存的.
  • connect 成功之后, 就赢了, 因为 fd 现在支持所有 file 读写接口! 所以很有必要了解和学习 Unix 的 文件 IO (我晕).

讲解 TCP 字节流

  • 由于 TCP 是面向字节流的协议, 并且他是通过 IP 包来实现传输的, 这里要用 Segment 来代替一个从 IP 层解出来的 TCP 包,
  • 结论是, 如果我们要进行 TCP 通信, read 必须放在 loop 里面!
  • 而我们 App 层需要在字节流上传小包的话, 就应该在这里进行分装. 下面举例一些应用层协议:

应用层分包

  • HTTP1.0 不支持动态更新, HTTP 1.0规定浏览器与server仅仅保持短暂的连接。浏览器的每次请求都须要与server建立一个TCP连接,server完毕请求处理后马上断开TCP连接. 我们暂时不讲 HTTP1.1 及之后的更加复杂的, 就是理解应用层协议怎么分装他们的 PDU 就行了.
  • SMTP simple 邮件传输协议则用回车换行来区分, 所以每次读取除了用 -1 0 判断 TCP 连接状态, 还可以分小包. Sum RPC 框架以及 DNS 用的是某种分隔符.

协议无关

  • (什么抽象封装)
  • 这样能够避免程序决定是 ipv4 还是 ipv6,
  • 我们抽象好几层之后, ip 地址都委托给 DNS 负责, 这样使用的接口使用 getaddrinfo 来实现(这个至少能自动区分 IPv4 和 IPv6 吧, 当时看到这个能查 dns 自己尝试连接就把我震惊了),
  • 这里的 socket 实际是用来给那些封装用的 low level, 实际编写 high level application 还是用更高的通用的吧.

wrapper 和编程风格

  • 通过 wrapper 来封装我们需要进行的错误处理
  • 如无意外以后用到大写的就是 wrap 过的函数, 不用处理错误.
  • 好处是节约我们主要 key function 的代码量, 方便阅读 (什么 tradeoff)
  • 有的人(函数)不设置 errno, 需要程序员来设置 (什么磨洋工), 如 pthread….
  • 花括号眼花缭乱, 不要使用尽量。

全局变量 errno

  • 之前看到这个很多次都不知道这个是啥, 现在解释, 他是一个全局变量.

The <errno.h> header file defines the integer variable errno, which is set by system calls and some library functions in the event of an error to indicate what went wrong.

  • C 语言处理错误, 必须很底层的像体系结构那种设置一个错误 flag 变量, 不然无法输出错误(这里是 err_sys). 这个基本和 Java throw 的那些一样, 只不过那些由 runtime 用 class 定义,这里我们用整数定义.
  • 所以错误处理的多线程需要想办法. errno 的定义可能是一个指针也可能是一个整数值,反正他就是用宏决定是什么语义的,根据 APUE ,如果没有出错,其值不会被一个例程清除。因此,仅当函数的返回值指明出错时,才检验其值。第二条是:任一函数都不会将errno 值设置为0,在<errno.h>中定义的所有常量都不为0。多线程的问题我们可能要 UNPv2 才能学习了。

daytime 服务端程序

bind

  • well known port 即我们监听的端口!
  • 监听套接字的存在, 让系统监听请求并传到上层来. 这里涉及 connect 的本质是什么, 我们暂时认为 connect 就是建立三次握手的过程, 可不可能我们不实际响应这些 SYN 我们会让内核通知 high level 再用 accept 响应?不会,实际你 accept 的时候内核已经完成握手了,你只能关掉他不想要的情况如果。
  • bind 本身需要把这个地址和端口号的 addr 结构体给绑定到监听端口上, 我们知道 IP 层路由最终会把我们本机 IP as dst 的 packet route 到这里, 我们主要关注端口号目前, 使用 INADDR_ANY 通配匹配所有入站 IP.

listen

  • The backlog argument defines the maximum length to which the queue of pending connections for sockfd may grow.   If  a  connection request  arrives  when the queue is full, the client may receive an error with an indication of ECONNREFUSED or, if the underlying protocol supports retransmission, the request may be ignored so that a later reattempt at connection succeeds.
  • 由于我们 listen 可能会收到很多累计的请求,实际上 kernel 会为我们准备一个 queue,这样依次 accept, 避免遗漏!所以 backlog 实际是 maximum client connection count 去指示多大的缓冲 quue 内存应该被分配了属于是。
  • listen + accept 的 new fd 是怎么实现的 I:(百度了一下)

  • 必须理清文件和 TCP 端口的区别:

描述这个连接的是(src port ,src ip ,dest port ,dest ip,version)五元数对应的fd,所以和端口是不是80没有太大关系,思想实验是三次握手的时候,只涉及网络栈解析 TCP 头的情况,这时候我不管对谁的 SYN 请求,都用 80 发 ACK + SYN 就行了,反正只要我的头写对了就行。然后涉及通信的问题,后续 client 的确还会发包路由到我这里来,但是,一旦我建立好了 fd,路由过来的 IP 包本来就全部都要让内核处理,内核本来也要处理解析 TCP 头看 TCP 端口的!这个时候只需要把我之前存的 fd mapping 来 map 到对应的 new fd,然后我们把字节流数据给放到那个 fd 的 VFS 缓冲区等 high level 调用 read 就行了!请务必弄清这一步。

Accept

  • 只需要知道一个 block 选项就行了:

If no pending connections are present on the queue, and the socket is not marked as nonblocking, accept() blocks the caller  until  a connection is present.  If the socket is marked nonblocking and no pending connections are present on the queue, accept() fails with the error EAGAIN or EWOULDBLOCK.

实际是使用文件接口:fcntl - manipulate file descriptor 来实现阻塞控制,这个和 read write 那些是一样的,我们只需要设置 O_NONBLOCK mark 就行。

  • by convention,我们把 accept 返回的 new fd 叫做 confd,connected fd 的意思。

缓冲区漏洞

  • 不要使用旧版的字符串操作 IO ,他们很多都有缓冲区溢出漏洞自从他们不会检测缓冲区限制。当然,新标准以前我们可以自己写自己的安全版,这也成为很多 C 语言编程教学的练习题。
  • 使用带 n 即限制最大字符串操作长度参数存在的 字符串IO 函数。
  • 使用如 snprintf, fgets,strncat, strncpy 等。

服务器基础

  • iterative server,即本讲解中一次 Accept 一次并完成全部通信工作后 close(which 引发四次回收的第一个 FIN)再 accept 下一个挤压在 kernel queue 中的连接。
  • concurrent server,即诸如 fork 由操作系统完成计时器抢占的进程模型或者单线程 polling 使用 epoll 等的服务器模型。
  • daemon 进程服务器程序:不需要绑定终端,这一点涉及我们之前讲到的进程组与会话,控制终端的相关,同时应该学习 APUE 的 daemon process 章节。(啊这。。。。)

标准 API

  • posix 手册可以在 the open group 的网站注册后下载或在线阅读。
  • 网络相关(TCP/IP网络栈)标准在 RFC
  • 体系结构,了解 ILP32 是 integer,long,pointer 32, LP64 是 long pointer 用 64. 标准和体系结构不同,数据类型的范围不同,所以有必要规范采用专门的头文件定义网络 API 用到的数据类型。

UNIX 网络拓扑查询

  • netstat -I interface
  • netstat -ni 以 ip 地址显示?(数值而不是反向解析地址)
  • netstat r 路由表
  • ifconfig interface
  • 网络接口中的 eth0 和 lo,lo 是 loopback

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值