IRC(Internet Relay Chat的缩写,“因特网中继聊天”)是一种通过网络的即时聊天方式。其主要用于群体聊天,但同样也可以用于个人对个人的聊天。
芬兰人雅尔可·欧伊卡利宁(Jarkko Oikarinen)于1988年8月创造了IRC来替换一个叫做MUT的程序。

IRC是一种公开的协议,采用TCP和SSL协议。一个IRC服务器可以连接其他的IRC服务器以扩展为一个IRC网络。IRC用户通过客户端软件和服务器相连。大多数的IRC服务器不需要客户注册登录,虽然在连接前必须设定好昵称(nickname),但客户端一般都会自动分给一个。

中心化拓扑

咱们每天上网访问的网站中,其实绝大多数都是“服务器-客户机”架构的,这是一种中心化的架构,基本长成这个样子(图片来自 wikipedia 的 p2p 的页面):

可以看出,这里有个老大地位很不一样,大家都去访问他,跟他要内容。

缺陷

去中心,反脆弱。

在中心化的这种架构中,中央戊己土坐着的那个老大看上去很牛,其实就是他最脆弱。比如,一旦他瘫痪了,整个系统就瘫痪了,一旦他被一个强权势力关停了,那么整个这个系统也就完蛋了。

P2P 对等拓扑/去中心/分布式

在 P2P 网络上,随便有几个节点突然没有了,整个系统也不会受到影响。举个例子,比特币系统就是 P2P 网络在金融方面的一个应用,系统的核心数据库,也就是比特币的公共账本,的安全是整个系统继续运行的关键。所以在设计上,必须认为每一个网络节点都是不完全可信的,每个节点都可能遭受攻击或者被行政命令关停,所以去中心化的 P2P 网络就成为了比特币系统设计上的必然选择。

非结构化网络

结构化网络

在结构化的对等网络中,叠加层被组织成一个特定的拓扑结构,并且该协议可以确保任何节点都可以有效地在网络中搜索文件/资源​​,即使资源极其稀少。

分布式散列表 DHT

最常见的结构化P2P网络类型实现了分布式散列表(DHT),其中使用一致散列的变体来将每个文件的所有权分配给特定对等体。这使同伴能够使用哈希表在网络上搜索资源:即(密钥,值)对存储在DHT中,并且任何参与节点可以有效地检索与给定密钥相关联的值。

  • 一个文件被分成很多块,下载时同时进行块的下载。

发展

BitTorrent 使用”分布式哈希表”(DHT)来为无 tracker 的种子(torrents)存储 peer 之间的联系信息。这样每个 peer 都成了 tracker。这个协议基于 Kademila 网络并且在 UDP 上实现。
在使用DHT分发Peer之前,Tracker是找到Peer的唯一方法。

2005年5月2日,Azureus 2.3.0.0(现在称为Vuze)发布,通过称为“分布式数据库”的系统引入了对“无tracker”种子的支持。该系统是一个分布式散列表DHT实现,它允许客户端使用没有BitTorrent tracker的种子。接下来的一个月,BitTorrent公司发布了Mainline BitTorrent客户端的4.2.0版本,该客户端支持与Azureus不兼容的另一种DHT实现(俗称“ Mainline DHT ”,在其网站上的草稿中概述)。最近的测量显示,Mainline DHT的用户从1000万到2500万,每日活跃至少1000万。 主线DHT可以说是世界上最大的DHT。

动画展示BT下载过程地址

参考

抓包 斗鱼

浏览器信息

浏览器访问的URL

1
2
http://www.douyu.com/   # 浏览器访问斗鱼的主页,下面是对视频的请求
http://175.25.168.26/hdl3.douyucdn.cn/live/868191rzb9Z8BR7r.flv?wsAuth=927a7c21a4c2303678529e208271ea1b&token=web-566086-868191-d17a433e9e430492d1b7be4cf13dc183&logo=0&expire=0&wsiphost=ipdb

socket会keep-alive,复用同一个socket,url保持不变。

Request Header

1
2
3
4
5
6
7
8
9
GET /hdl3.douyucdn.cn/live/852315r71MZwNkYi.flv?wsAuth=98cdd891123dc85668196c67d741a853&token=web-566086-852315-c12ea88bc6b9657e3e6bf1e3f7794fa9&logo=0&expire=0&wsiphost=local HTTP/1.1
Host: 114.64.222.49 # 首页会因为直播间不同,而Host和URL不同。
Connection: keep-alive
X-Requested-With: ShockwaveFlash/22.0.0.192
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36
Accept: */*
Referer: http://www.douyu.com/
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,en-US;q=0.4

Response Header

1
2
3
4
5
6
7
HTTP/1.1 200 OK
Expires: Sat, 30 Jul 2016 09:28:28 GMT
Cache-Control: no-cache
Content-Type: video/x-flv
Pragma: no-cache
Via: 1.1 zdx49:7755 (Cdn Cache Server V2.0)
Connection: close

wireshark抓包

filter: ip.dst==114.64.222.49 or ip.src==114.64.222.49

filter之后显示的连接数据包如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
No      Time        Source          Destination     Protocol length Info
# 三次握手
1672 15.896971 9.186.58.171 114.64.222.49 TCP 66 54185 → 80 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=4 SACK_PERM=1
1674 15.906057 114.64.222.49 9.186.58.171 TCP 66 80 → 54185 [SYN, ACK] Seq=0 Ack=1 Win=1460 Len=0 MSS=1380 SACK_PERM=1 WS=128
1675 15.906271 9.186.58.171 114.64.222.49 TCP 54 54185 → 80 [ACK] Seq=1 Ack=1 Win=16560 Len=0
# 握手成功,第四个包才是http的
1676 15.907013 9.186.58.171 114.64.222.49 HTTP 592 GET /hdl3.douyucdn.cn/live/852315r71MZwNkYi.flv?wsAuth=98cdd891123dc85668196c67d741a853&token=web-566086-852315-c12ea88bc6b9657e3e6bf1e3f7794fa9&logo=0&expire=0&wsiphost=local HTTP/1.1
# server向client发送一个ack码,约定以后客户端必须通过seq=539来确认收到的总字节数目
1677 15.916612 114.64.222.49 9.186.58.171 TCP 54 80 → 54185 [ACK] Seq=1 Ack=539 Win=15744 Len=0

# 传输视频流数据
1679 16.376026 114.64.222.49 9.186.58.171 TCP 247 [TCP segment of a reassembled PDU]
1680 16.378970 114.64.222.49 9.186.58.171 TCP 1434 [TCP segment of a reassembled PDU]
1681 16.379198 114.64.222.49 9.186.58.171 TCP 1434 [TCP segment of a reassembled PDU]
1682 16.379304 114.64.222.49 9.186.58.171 TCP 1434 [TCP segment of a reassembled PDU]
1683 16.379412 114.64.222.49 9.186.58.171 TCP 1434 [TCP Previous segment not captured] [TCP segment of a reassembled PDU]
1684 16.379451 114.64.222.49 9.186.58.171 TCP 1434 [TCP segment of a reassembled PDU]
1685 16.379476 114.64.222.49 9.186.58.171 TCP 1434 [TCP Out-Of-Order] [TCP segment of a reassembled PDU]
1686 16.379921 9.186.58.171 114.64.222.49 TCP 54 54185 → 80 [ACK] Seq=539 Ack=1574 Win=16560 Len=0 #
1687 16.379937 9.186.58.171 114.64.222.49 TCP 54 54185 → 80 [ACK] Seq=539 Ack=4334 Win=16560 Len=0

连接流程

1
2
3
4
5
6
三次握手:
第一次握手:客户端向服务器发送连接请求包,标志位SYN(同步序号)置为1,序号为X=0
第二次握手:服务器收到客户端发过来报文,由SYN=1知道客户端要求建立联机。向客户端发送一个SYN和ACK都置为1的TCP报文,设置初始序号Y=0,将确认序号(Acknowledgement Number)设置为客户的序列号加1,即X+1 = 0+1=1,
第三次握手:客户端收到服务器发来的包后检查确认序号(Acknowledgement Number)是否正确,即第一次发送的序号加1(X+1=1)。以及标志位ACK是否为1。若正确,服务器再次发送确认包,ACK标志位为1,SYN标志位为0。确认序号(Acknowledgement Number)=Y+1=0+1=1,发送序号为X+1=1。客户端收到后确认序号值与ACK=1则连接建立成功,可以传送数据了。
HTTP请求:
断开连接: (客户端要求断开连接则需要四次挥手)

HTTP包(No 1676)

1
2
3
4
5
6
7
物理层: Frame:  1676: 592 bytes on wire (4736 bits), 592 bytes captured (4736 bits) on interface 0   // 物理层的数据帧概况
链路层: Ethernet II, Src: IntelCor_09:85:f8 (e8:b1:fc:09:85:f8), Dst: All-HSRP-routers_65 (00:00:0c:07:ac:65) // 数据链路层以太网帧头部信息,e8:b1:fc:09:85:f8这是我的mac地址
网络层: Internet Protocol Version 4, Src: 9.186.58.171, Dst: 114.64.222.49 // 9.186.58.171是本机ipconfig显示的ip,也就是局域网ip (不是网关ip,也不是外网ip)
传输层: Transmission Control Protocol, Src Port: 54185 (54185), Dst Port: 80 (80), Seq: 1, Ack: 1, Len: 538
应用层: 跟浏览器中的request header相同

注意: Frame = sum[header] + content(null),即一堆header叠加在一起,封装在物理层数据帧中,没有内容

TCP视频流数据包(No 1680)

1
2
3
4
5
6
物理层: Frame 1680: 1434 bytes on wire (11472 bits), 1434 bytes captured (11472 bits) on interface 0
链路层: Ethernet II, Src: CiscoInc_42:96:c0 (e8:ba:70:42:96:c0), Dst: IntelCor_09:85:f8 (e8:b1:fc:09:85:f8)
网络层: Internet Protocol Version 4, Src: 114.64.222.49, Dst: 9.186.58.171
传输层: Transmission Control Protocol, Src Port: 80 (80), Dst Port: 54185 (54185), Seq: 194, Ack: 539, Len: 1380

注意: Frame = sum[header] + content(很多内容)

其他细节

TCP out of order

原因:
多半是网络拥塞,导致顺序包抵达时间不同,延时太长,或者包丢失,需要重新组合数据单元,因为他们可能是通过不同的路径到达的。
TCP Out-Of-Order ,有些 Packet 可能 Lost,所以重新传送造成。
另一个可能是因为 Client 到 Server 间有两条网路路径,像是 Load Balance 之类的架构。
因此若两个封包走不同路径,晚送的封包却比早送的到达,就会发生 Out-Of-Order。

解决方法:

comments

  • 能够看出是一直复用的一个连接: 54189<-->80 端口之间建立的socket连接

疑问

  • 为什么采用TCP
  • TCP阻塞问题怎样解决的
  • tcp丢包是否还会续传?续传仍然丢包呢
  • socket编程中,send之后的status只怎么来的?应该是两个数据包吧?可以试试

introduction

  • python socket编程
  • java socket编程

python socket编程

测试1

import socket

def sendHttpViaSocket():
    tcpsoc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    print tcpsoc.connect(('9.186.102.103', 8080)) # 三次握手 (三个包)
    print tcpsoc.send('GET /bluescan/ HTTP/1.1\r\nHOST:anystring\r\n\r\n') # 进行get请求,并获取数据
    response = tcpsoc.recv(1000000)  # 不发送数据包
    print response

if __name__ == '__main__':
    sendHttpViaSocket()

console输出:

None
43
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Accept-Ranges: bytes
ETag: W/"2946-1467880723568"
Last-Modified: Thu, 07 Jul 2016 08:38:43 GMT
Content-Type: text/html;charset=UTF-8
Content-Length: 2946
Date: Sat, 30 Jul 2016 12:14:57 GMT

<!DOCTYPE html>
<html>  
...
</html>

wireshark抓包:

filter: ip.dst==9.186.102.103 or ip.src==9.186.102.103

No  Time        Source          Destination     Pro   Len   Info
// tcpsoc.connect(('9.186.102.103', 8080)) # 往返发送三个数据包,进行三次握手
66    3.063170    9.186.58.171    9.186.102.103    TCP      66    58415 → 8080 [SYN] Seq=0 Win=8192 Len=0 MSS=1460 WS=4 SACK_PERM=1
67    3.064642    9.186.102.103    9.186.58.171    TCP      66    8080 → 58415 [SYN, ACK] Seq=0 Ack=1 Win=8192 Len=0 MSS=1460 WS=256 SACK_PERM=1
68    3.064861    9.186.58.171    9.186.102.103    TCP      54    58415 → 8080 [ACK] Seq=1 Ack=1 Win=17520 Len=0

// tcpsoc.send('GET /bluescan/ HTTP/1.1\r\nHOST:anystring\r\n\r\n')
// 一个html文件比较大,会分成几个数据包发送。
69    3.065188    9.186.58.171    9.186.102.103    HTTP  94    GET /bluescan/ HTTP/1.1
70    3.070695    9.186.102.103    9.186.58.171    TCP      1514    [TCP segment of a reassembled PDU]
71    3.071086    9.186.102.103    9.186.58.171    TCP      1514    [TCP segment of a reassembled PDU]
72    3.071471    9.186.58.171    9.186.102.103    TCP   54    58415 → 8080 [ACK] Seq=41 Ack=2921 Win=17520 Len=0
73    3.072820    9.186.102.103    9.186.58.171    HTTP  322    HTTP/1.1 200 OK  (text/html) // 这里会对之前的数据包做一个拼接(只拼接No)

// 程序结束,socket由客户端自动结束 (四次挥手)
74    3.074085    9.186.58.171    9.186.102.103    TCP      54    58415 → 8080 [FIN, ACK] Seq=41 Ack=3189 Win=17252 Len=0
75    3.076070    9.186.102.103    9.186.58.171    TCP      54    8080 → 58415 [ACK] Seq=3189 Ack=42 Win=65536 Len=0
76    3.076118    9.186.102.103    9.186.58.171    TCP      54    8080 → 58415 [FIN, ACK] Seq=3189 Ack=42 Win=65536 Len=0
77    3.076302    9.186.58.171    9.186.102.103    TCP      54    58415 → 8080 [ACK] Seq=42 Ack=3190 Win=17252 Len=0

其中HTTP包(No 70)

物理层: Frame 49: 1514 bytes on wire (12112 bits), 1514 bytes captured (12112 bits) on interface 0
链路层: Ethernet II, Src: CiscoInc_42:89:c0 (e8:ba:70:42:89:c0), Dst: IntelCor_09:85:f8 (e8:b1:fc:09:85:f8)
网络层: Internet Protocol Version 4, Src: 9.186.102.103, Dst: 9.186.58.203
传输层: Transmission Control Protocol, Src Port: 8080 (8080), Dst Port: 58415 (58415), Seq: 1, Ack: 44, Len: 1460

##

测试2,分小包发送

import socket

# 一口气能说完的话,非要分开很多包发送,很贱哎
def sendHttpViaSocket():
    tcpsoc = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    print tcpsoc.connect(('9.186.102.103', 8080)) # 三次握手 (三个包)
    tcpsoc.send('GET /bluescan/ HTTP/1.1') # 发送一个数据包,server返回一个ack (两个包)
    tcpsoc.send('\r\n') # 发送一个数据包,server返回一个ack (两个包)
    tcpsoc.send('HOST:anystring\r\n') # 发送一个数据包,server返回一个ack (两个包)
    tcpsoc.send('\r\n') # 到这才开始发送http请求数据包,并收到数据
    response = tcpsoc.recv(1000000) # do nothing
    print response

if __name__ == '__main__':
    sendHttpViaSocket()

java socket编程

client端代码

package song.net.socket;

/**
 * download from
 * http://www.javatpoint.com/socket-programming
 */

import java.io.*;
import java.net.*;

public class MyClientOneside {
    public static void main(String[] args) {
        try {
            Socket s = new Socket("9.186.102.103", 6666); // 进行三次握手 (三个数据包)
            DataOutputStream dout = new DataOutputStream(s.getOutputStream());  // 没包
            dout.writeUTF("Hello Server"); // 发送一个数据包,返回一个确认包 (两个包)
            dout.flush();

            dout.close();
            s.close();

        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

server端代码:

package song.net.socket;

/**
 * download from
 * http://www.javatpoint.com/socket-programming
 */
import java.io.*;
import java.net.*;

public class MyServerOneside {
    public static void main(String[] args) {
        try {
            ServerSocket ss = new ServerSocket(6666);
            Socket s = ss.accept();// establishes connection,等待连接,client端 new Socket("localhost", 6666)后,这一行执行好
            DataInputStream dis = new DataInputStream(s.getInputStream()); // server端接收socket的信息
            String str = (String) dis.readUTF(); //这里会卡住,等待client端 dout.writeUTF("Hello Server");
            System.out.println("message= " + str);
            ss.close();

        } catch (Exception e) {
            System.out.println(e);
        }
    }
}

浏览器访问

‘’’

Name                        Status Type      Initiator      Size
login.html                  304    document  Other  123 B   349 ms   
// server返回304,表示浏览器可以直接在本地缓存中读取资源,省去传输消耗
jquery-1.7.2.min.js         304    script    login.html:43  124 B
util.js                     304    script    login.html:44  124 B
01_BlueSCAN_sign-in.gif     304    gif       login.html:43  125 B
BlueSCAN_input_username.png 304    png       login.html:43  123 B
BlueSCAN_input_password.png 304    png       login.html:43  123 B
nudge-icon-arrow-up.png     200    png       Preview.js:28  (from cache)
nudge-icon-arrow-down.png   200    png       Preview.js:28  (from cache)
nudge-icon-arrow-lr.png     200    png       Preview.js:28  (from cache)
nudge-icon-return.png       200    png       Preview.js:28

wireshark抓包

comments

  • java socket编程怎么这么丑啊!!!

疑问

notes发送邮件

1
2
3
4
5
6
7
8
filter: ip.dst==114.64.222.49 or ip.src==114.64.222.49
(not arp) and (not icmp) #

tcp.port eq 25 or tcp.port eq 587 or icmp # Show only SMTP (port 25) and ICMP traffic:

IBM nots SMTP server

tcp.port eq 25 or tcp.port eq 587 or tcp.port eq 26 or tcp.port eq 465 or icmp

hotmail发邮件

wireshark 抓包

demo

首先,去web/html5/websocket教程中查看demo,

建立连接

webSocket建立以后不再有tcp连接建立:

过滤规则(rules in filter)

方式一:按端口过滤

1
tcp.dstport == your_websoket_port

方式二:按协议过滤

1
websocket

抓包分析 – echo.websocket.org

我的ip 9.186.58.178
echo.websocket.org 174.129.224.73

1
2
No      Time        Source          Destination     Protocol length Info
12821 649.588871 174.129.224.73 9.186.58.178 WebSocket 84 WebSocket Text [FIN]
1
2
3
4
5
6
7
Frame 12821: 84 bytes on wire (672 bits), 84 bytes captured (672 bits) on interface 0
Ethernet II, Src: Cisco_42:89:c0 (e8:ba:70:42:89:c0), Dst: IntelCor_09:85:f8 (e8:b1:fc:09:85:f8)
Internet Protocol Version 4, Src: 174.129.224.73, Dst: 9.186.58.178
Transmission Control Protocol, Src Port: 80, Dst Port: 65331, Seq: 543, Ack: 667, Len: 30
WebSocket
Line-based text data
Rock it with HTML5 WebSocket

疑问

[FIN] 和 [FIN][MASKED]什么区别?

websocket为什么是单独的一个层?是不是算在了应用层和传输层之间的某个层。
line-based text data包含数据,为什么也是单独的一个层?

是否明文传输?
是明文传输的!

我为啥开了ssl也能看见明文?(用https握手之后用wss连接的) – 来自知乎

参考

http://blog.csdn.net/lusyoe/article/details/53858320