Unix网络编程简介

简介

字节序转换 htons&ntohs

以short类型数字512为例

表达式:

$512=2^9=2\cdot16^2$

二进制:

$0000 0010 0000 0000$

十六进制:

$0x0200$

在小端字节序处理器中:

Little-endian

而TCP/IP协议栈使用大端字节序。应用程序交换格式化数据时,字节序问题就会出现。对于TCP/IP,地址用网络字节序来表示,所以应用程序有时需要在处理器的字节序与网络字节序之间转换它们。

htons转换后:

Big-endian

附:测试平台的字节序

操作系统

处理器架构

字节序

FreeBSD 8.0

Intel Pentium

小端

Linux 3.2.0

Intel Core i5

小端

Mac OS X 10.6.8

Intel Core 2 Duo

小端

Solaris 10

Sun SPARC

大端

地址格式转换 inet_pton

函数inet_ntop将形如127.0.0.1的文本字符串地址转转出网络字节序的二进制地址,根据指定的协议分别调用inet_pton4inet_pton6

int
inet_pton(af, src, dst)
	int af;
	const char *src;
	void *dst;
{
	switch (af) {
	case AF_INET:
		return (inet_pton4(src, dst));
	case AF_INET6:
		return (inet_pton6(src, dst));
	default:
		errno = EAFNOSUPPORT;
		return (-1);
	}
	/* NOTREACHED */
}

inet_pton.c中分别定义了IPv4和IPv6转换过程中缓冲区的大小

#define	IN6ADDRSZ	16
#define	INADDRSZ	 4
...

//In inet_pton6
u_char tmp[IN6ADDRSZ];

//In inet_pton4
u_char tmp[INADDRSZ];
inet_pton函数的调用图

协议无关性 IPv4 VS IPv6

IPv4版本的客户端程序,分配并初始化sockaddr_in结构,置sin_family成员为AF_INET,指定socket函数的第一个参数为AF_INET。如果要修改为IPv6版本,则需要使用sockaddr_in6结构,参数修改为AF_INET6

daytimetcpcli.c VS daytimetcpcliv6.c

错误包裹函数

书中将一些需要判断返回值的函数调用封装成了函数首字母大写的错误包裹函数,例如本章的客户端程序中:

if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
	err_sys("connect error");

可以简写成

Connect(sockfd, (SA *) &servaddr, sizeof(servaddr));

unpv13e/lib/wrapsock.c中有很多和socket连接有关的错误包裹函数

时间/日期 客户端及服务器

TCP服务器通过socket、bind、listen三个步骤来准备一个监听套接口,编译运行服务器,我们可以查看网络端口情况。

make daytimetcpsrv
./daytimetcpsrv
sudo lsof -i -P | grep -i "listen"
...
daytimetc 25714           root    3u  IPv4 0xae12d925e13bf793      0t0    TCP *:13 (LISTEN)
...

由于我们设置的网际套接口结构时设定了任何地址,所以可以看到端口13前面的地址是“*”。

TCP/IP中我们比较关心的三次握手,发生在服务器进程调用accept()之后,处于睡眠状态的服务器会等待客户端的链接和内核对它的接受。当三次握手完成,accept()函数返回,返回值是一个文件描述符(connfd),每个连接服务器的客户端都会分配一个,用来标识服务器与不同客户端的通信。

本章的时间日期例子,服务器打印时间戳给客户端之后,就会主动关闭连接,通过调用close()发起四次挥手,客户端收完那行时间戳之后就会退出。

客户端与服务器代码图解

使用tcpdump分析本章中的TCP/IP连接

先查看所有适配器

jackieluo@JACKIELUO-MC0 ~ tcpdump -D
1.en0 [Up, Running]
2.bridge0 [Up, Running]
3.utun0 [Up, Running]
4.en2 [Up, Running]
5.en3 [Up, Running]
6.lo0 [Up, Running, Loopback]
7.en1 [Up]
8.gif0
9.stf0
10.p2p0
11.awdl0
12.XHC20

抓一下lo0(因为客户端连的127.0.0.1)

sudo tcpdump -i lo0 ‘port 13 and tcp’ -X -s 0 -S

-X 只打印 IP Header,TCP Header,Application Data

-S 顺序号使用绝对值

IP Header

IP Header图解

前面一大坨也看不太懂啥意思,但是源IP和目的IP还是很好懂的,先用抓到的包按照这个分解

抓到的第一个包:

16:25:52.755769 IP localhost.60933 > localhost.daytime: Flags [S], seq 1267114631, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 4110257572 ecr 0,sackOK,eol], length 0
	0x0000:  4500 0040 0000 4000 4006 0000 7f00 0001  E..@..@.@.......
	0x0010:  7f00 0001 ee05 000d 4b86 a287 0000 0000  ........K.......
	0x0020:  b002 ffff fe34 0000 0204 3fd8 0103 0305  .....4....?.....
	0x0030:  0101 080a f4fd 8da4 0000 0000 0402 0000  ................

按照图去分解:

  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  | Ver: 4 | IHL: 5 | ToS: 00 | Total Length: 0040 |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |Identification: 0000 |F: 4 | Fragment Offset: 000 |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  | TTL: 40 | Protocol: 06 | Header Checksum: 0000 |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  | Source Address: 7f00 0001 |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  | Destination Address: 7f00 0001 |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

可以看到Source Address和Destination Address都是7f00 0001

二进制:0111 1111 0000 0000 0000 0000 0000 0001

IP地址:127.0.0.1

TCP Header

TCP Header图解.png

同样是上面的包,按照图和剩下的数据去解TCP Header:

注意options mss 16344,nop,wscale 5,nop,nop,TS val 4110257572 ecr 0,sackOK,eol

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Port: ee05 | Destination Port: 000d |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Sequence Number: 4b86 a287 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Acknowledgment Number: 0000 0000 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|DO: b |Reserved: 0| Flags: 02| Window: ffff |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Checksum: fe34 | Urgent Pointer: 0000 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| TCP Options: 0204 3fd8 |(mss 16344)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| TCP Options: 0103 |(nop)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| TCP Options: 0305 0101 |(wscale 5)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| TCP Options: 080a f4fd 8da4 0000 0000 |(TCP timestamp)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| TCP Options: 0402 0000 |(SACK)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Data: |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

三次握手

第一次握手 localhost.60933 > localhost.daytime SYN=1

seq=1267114631

16:25:52.755769 IP localhost.60933 > localhost.daytime: Flags [S], seq 1267114631, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 4110257572 ecr 0,sackOK,eol], length 0
	0x0000:  4500 0040 0000 4000 4006 0000 7f00 0001  E..@..@.@.......
	0x0010:  7f00 0001 ee05 000d 4b86 a287 0000 0000  ........K.......
	0x0020:  b002 ffff fe34 0000 0204 3fd8 0103 0305  .....4....?.....
	0x0030:  0101 080a f4fd 8da4 0000 0000 0402 0000  ................

第二次握手 localhost.daytime > localhost.60933 SYN=1

seq=4208814824,ack=1267114631+1

16:25:52.755822 IP localhost.daytime > localhost.60933: Flags [S.], seq 4208814824, ack 1267114632, win 65535, options [mss 16344,nop,wscale 5,nop,nop,TS val 4110257572 ecr 4110257572,sackOK,eol], length 0
	0x0000:  4500 0040 0000 4000 4006 0000 7f00 0001  E..@..@.@.......
	0x0010:  7f00 0001 000d ee05 fadd 6ae8 4b86 a288  ..........j.K...
	0x0020:  b012 ffff fe34 0000 0204 3fd8 0103 0305  .....4....?.....
	0x0030:  0101 080a f4fd 8da4 f4fd 8da4 0402 0000  ................

第三次握手 localhost.60933 > localhost.daytime

ack=4208814824+1

16:25:52.755828 IP localhost.60933 > localhost.daytime: Flags [.], ack 4208814825, win 12759, options [nop,nop,TS val 4110257572 ecr 4110257572], length 0
	0x0000:  4500 0034 0000 4000 4006 0000 7f00 0001  E..4..@.@.......
	0x0010:  7f00 0001 ee05 000d 4b86 a288 fadd 6ae9  ........K.....j.
	0x0020:  8010 31d7 fe28 0000 0101 080a f4fd 8da4  ..1..(..........
	0x0030:  f4fd 8da4                                ....

上述三次握手,TCP Header中都不包含Data,三次header长度不同只是因为Option有差异。

一个疑惑:第四次 localhost.daytime > localhost.60933

ack=1267114631+1

16:25:52.755833 IP localhost.daytime > localhost.60933: Flags [.], ack 1267114632, win 12759, options [nop,nop,TS val 4110257572 ecr 4110257572], length 0
	0x0000:  4500 0034 0000 4000 4006 0000 7f00 0001  E..4..@.@.......
	0x0010:  7f00 0001 000d ee05 fadd 6ae9 4b86 a288  ..........j.K...
	0x0020:  8010 31d7 fe28 0000 0101 080a f4fd 8da4  ..1..(..........
	0x0030:  f4fd 8da4                                ....

这里也是个空包,但是没太理解,后续找原因

数据传输

seq=4208814851, ack=1267114631+1

16:25:52.755868 IP localhost.daytime > localhost.60933: Flags [P.], seq 4208814825:4208814851, ack 1267114632, win 12759, options [nop,nop,TS val 4110257572 ecr 4110257572], length 26
	0x0000:  4500 004e 0000 4000 4006 0000 7f00 0001  E..N..@.@.......
	0x0010:  7f00 0001 000d ee05 fadd 6ae9 4b86 a288  ..........j.K...
	0x0020:  8018 31d7 fe42 0000 0101 080a f4fd 8da4  ..1..B..........
	0x0030:  f4fd 8da4 5361 7420 4175 6720 2034 2031  ....Sat.Aug..4.1
	0x0040:  363a 3235 3a35 3220 3230 3138 0d0a       6:25:52.2018..

可以看到此时服务器向客户端传输了数据,即服务器时间

Sat Aug  4 16:25:52 2018

四次挥手

第一次挥手 localhost.daytime > localhost.60933 FIN=1

由于代码中关闭连接的一方是服务器,第一次挥手由服务器发起

seq=4208814851, ack=1267114631+1

16:25:52.755873 IP localhost.daytime > localhost.60933: Flags [F.], seq 4208814851, ack 1267114632, win 12759, options [nop,nop,TS val 4110257572 ecr 4110257572], length 0
	0x0000:  4500 0034 0000 4000 4006 0000 7f00 0001  E..4..@.@.......
	0x0010:  7f00 0001 000d ee05 fadd 6b03 4b86 a288  ..........k.K...
	0x0020:  8011 31d7 fe28 0000 0101 080a f4fd 8da4  ..1..(..........
	0x0030:  f4fd 8da4                                ....

第二次挥手 localhost.60933 > localhost.daytime

ack=4208814851

16:25:52.755882 IP localhost.60933 > localhost.daytime: Flags [.], ack 4208814851, win 12758, options [nop,nop,TS val 4110257572 ecr 4110257572], length 0
	0x0000:  4500 0034 0000 4000 4006 0000 7f00 0001  E..4..@.@.......
	0x0010:  7f00 0001 ee05 000d 4b86 a288 fadd 6b03  ........K.....k.
	0x0020:  8010 31d6 fe28 0000 0101 080a f4fd 8da4  ..1..(..........
	0x0030:  f4fd 8da4                                ....

又一个疑惑,为什么这里也多一次挥手

ack=4208814852

16:25:52.755884 IP localhost.60933 > localhost.daytime: Flags [.], ack 4208814852, win 12758, options [nop,nop,TS val 4110257572 ecr 4110257572], length 0
	0x0000:  4500 0034 0000 4000 4006 0000 7f00 0001  E..4..@.@.......
	0x0010:  7f00 0001 ee05 000d 4b86 a288 fadd 6b04  ........K.....k.
	0x0020:  8010 31d6 fe28 0000 0101 080a f4fd 8da4  ..1..(..........
	0x0030:  f4fd 8da4                                ....

第三次挥手 localhost.60933 > localhost.daytime FIN=1

seq=1267114631+1, ack=4208814852

16:25:52.756045 IP localhost.60933 > localhost.daytime: Flags [F.], seq 1267114632, ack 4208814852, win 12758, options [nop,nop,TS val 4110257572 ecr 4110257572], length 0
	0x0000:  4500 0034 0000 4000 4006 0000 7f00 0001  E..4..@.@.......
	0x0010:  7f00 0001 ee05 000d 4b86 a288 fadd 6b04  ........K.....k.
	0x0020:  8011 31d6 fe28 0000 0101 080a f4fd 8da4  ..1..(..........
	0x0030:  f4fd 8da4                                ....

第四次挥手 localhost.daytime > localhost.60933

ack=1267114631+1+1

16:25:52.756072 IP localhost.daytime > localhost.60933: Flags [.], ack 1267114633, win 12759, options [nop,nop,TS val 4110257572 ecr 4110257572], length 0
	0x0000:  4500 0034 0000 4000 4006 0000 7f00 0001  E..4..@.@.......
	0x0010:  7f00 0001 000d ee05 fadd 6b04 4b86 a289  ..........k.K...
	0x0020:  8010 31d7 fe28 0000 0101 080a f4fd 8da4  ..1..(..........
	0x0030:  f4fd 8da4                                ....

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏IT笔记

SpringBoot开发案例之整合mongoDB

JDK1.7、Maven、Eclipse、SpringBoot1.5.2、mongodb3.4,Robomongo(可视化工具)

70660
来自专栏为数不多的Android技巧

Android 插件化原理解析——Hook机制之AMS&PMS

在前面的文章中我们介绍了DroidPlugin的Hook机制,也就是代理方式和Binder Hook;插件框架通过AOP实现了插件使用和开发的透明性。在讲述Dr...

14810
来自专栏Jerry的SAP技术分享

ABAP和Java的destination和JNDI

36330
来自专栏ImportSource

微服务弹性框架hystrix-javanica详解(上)

Java语言相比其他语言有一些比较great的优点,那就是反射(refleaction)和注解(annotation)。 几乎所有的流行框架比如Spring, ...

521100
来自专栏JackieZheng

Spring Boot系列——AOP配自定义注解的最佳实践

AOP(Aspect Oriented Programming),即面向切面编程,是Spring框架的大杀器之一。

19720
来自专栏Lambda

编程规范

领域层–编码规范 2018年4月4日14:10:38 Controller层编写规范 controller层只是负责从service层获得数据,对外暴露API接...

37460
来自专栏蘑菇先生的技术笔记

多线程中的锁系统(四)-谈谈自旋锁

30570
来自专栏芋道源码1024

【追光者系列】HikariCP 源码分析之故障检测那些思考 fail fast &amp; allowPoolSuspension

由于时间原因,本文主要内容参考了 https://segmentfault.com/a/1190000013136251 ,并结合一些思考做了增注

23940
来自专栏后端云

resize失败原因调查

对一个vm做resize,即从一个小的flavor换一个大的flavor,没有成功

17530
来自专栏用户2442861的专栏

Spring+Mybatis+SpringMVC后台与前台分页展示实例(附工程)

      林炳文Evankaka原创作品。转载请注明出处http://blog.csdn.net/evankaka

19220

扫码关注云+社区

领取腾讯云代金券