TCP/IP协议
- IP协议负责把数据从一台计算机通过网络发送到另一台计算机。数据被分割成一小块一小块,然后通过IP包发送出去。由于互联网链路复杂,两台计算机之间经常有多条线路,因此,路由器就负责决定如何把一个IP包转发出去。IP包的特点是按块发送,途径多个路由,但不保证能到达,也不保证顺序到达。
- TCP协议则是建立在IP协议之上的。TCP协议负责在两台计算机之间建立可靠连接,保证数据包按顺序到达。TCP协议会通过握手建立连接,然后,对每个IP包编号,确保对方按顺序收到,如果包丢掉了,就自动重发。
- 许多常用的更高级的协议都是建立在TCP协议基础上的,比如用于浏览器的HTTP协议、发送邮件的SMTP协议等。
- 一个TCP报文除了包含要传输的数据外,还包含源IP地址和目标IP地址,源端口和目标端口。
- 端口有什么作用?
- 在两台计算机通信时,只发IP地址是不够的,因为同一台计算机上跑着多个网络程序。一个TCP报文来了之后,到底是交给浏览器还是QQ,就需要端口号来区分。每个网络程序都向操作系统申请唯一的端口号,这样,两个进程在两台计算机之间建立网络连接就需要各自的IP地址和各自的端口号。
- 一个进程也可能同时与多个计算机建立链接,因此它会申请很多端口。
Socket模块
一般来说建立服务器连接需要6步
- 创建socket对象,
- socket.socket()
- 将socket绑定到指定地址上
- socket.bind(address)
- 绑定后必须准备好套接字,一边接受连接请求
- socket.listen(backlog), backlog用于指定最多连接数,至少为1
- 服务器套接字通过socket的accept方法等待客户请求一个连接
- connection,address=socket.accept()
- connection 指新的socket对象
- address指客户网络地址
- 处理阶段,服务器和客户通过send和recv方法通信,传输数据
- 传输结束,服务器调用socket的close方法关闭连接
建立一个简单的客户连接需要4步
- 创建一个socket以连接服务器socket=socket.socket(family,type)
- 使用socket的connect方法连接服务器socket.connect((host,port))
- 客户和服务器通过send和recv方法通信
- 关闭客户端socket.close()
写一个客户端,和百度建立TCP连接,向其发送请求,把结果保存在baidu.html中
这是很多爬虫请求工具的底层原理
#!/usr/bin/env python3
# coding:utf-8
import socket
import codecs
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
host = 'www.baidu.com'
port = 80
s.connect((host,port))
# 向服务端发送请求
s.send(b'GET / HTTP/1.1\r\nHost:www.baidu.com\r\nConnection:close\r\n\r\n')
# 建立一个列表,储存收到的数据
buffer=[]
while 1:
# 每次接受1k字节的数据
d = s.recv(1024)
if d:
buffer.append(d)
else:
break
# 把buffer列表里面接受到的数据拼接成data
data= b''.join(buffer)
header,html = data.split(b'\r\n\r\n',1)
print(header.decode("utf-8"))
with codecs.open('baidu.html','wb') as f:
f.write(html)
s.close()
服务器端,写一个脚本等待连接,如果连上,并且接收到客户端发来的数据后,把数据返回给客户端
服务端脚本:
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @date: 2018/6/1 上午9:57
# @author: Bill
# @file: server_socket.py
import socket
import threading
import time
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
host = socket.gethostname()
port = 9999
s.bind((host,port))
s.listen(5) # 设置最大的监听数
def tcp_link(sock, addr):
print("Accept new connection from {}".format(addr))
sock.send("欢迎使用tcp_link服务!".encode("utf-8"))
while 1:
data = sock.recv(1024)
time.sleep(1)
if not data or data.decode('utf-8') == "exit":
break
sock.send(("Receive data: %s" % data.decode("utf-8")).encode("utf-8"))
# 注意上面一行的格式,要先把客户端接收到的数据解码,然后传到返回去的时候还要编码成utf-8
sock.close()
print("Connection closed from {}".format(addr))
while 1:
sock,addr = s.accept()
t = threading.Thread(target=tcp_link,args=(sock,addr))
# 开启多线程,进行多个客户端的交互
t.start()
客户端脚本
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# @date: 2018/6/1 上午10:16
# @author: Bill
# @file: client.py
import socket
import time
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
host = socket.gethostname()
port = 9999
s.connect((host,port))
print(s.recv(1024).decode("utf-8"))
while 1:
data = input("Please input the data you want to send: ")
if data == "exit":
break
s.send(data.encode("utf-8"))
d = s.recv(1024)
time.sleep(1)
print(d.decode("utf-8"))