Python学习目录python
互联网的实现,分红好几层。每一层都有本身的功能,就像建筑物同样,每一层都靠下一层支持。如何分层有不一样的模型,有的模型分七层,有的分四层。我以为,把互联网分红五层,比较容易解释。最底下的一层叫作"实体层"(Physical Layer),最上面的一层叫作"应用层"(Application Layer),中间的三层(自下而上)分别是"连接层"(Link Layer)、"网络层"(Network Layer)和"传输层"(Transport Layer)。越下面的层,越靠近硬件;越上面的层,越靠近用户。编程
Socket是网络编程的一个抽象概念。一般咱们用一个Socket表示“打开了一个网络连接”,而打开一个Socket须要知道目标计算机的IP地址和端口号,再指定协议类型便可。服务器
# 导入socket库:
import socket
# 建立一个socket:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 创建链接:
s.connect(('www.sina.com.cn', 80))
复制代码
建立Socket
时,AF_INET
指定使用IPv4协议,若是要用更先进的IPv6,就指定为AF_INET6
。SOCK_STREAM
指定使用面向流的TCP协议,这样,一个Socket
对象就建立成功,可是尚未创建链接。网络
s.connect(('www.sina.com.cn', 80))
复制代码
注意参数是一个tuple
,包含地址和端口号。多线程
# 发送数据:
s.send(b'GET / HTTP/1.1\r\nHost: www.sina.com.cn\r\nConnection: close\r\n\r\n')
复制代码
TCP链接建立的是双向通道,双方均可以同时给对方发数据。可是谁先发谁后发,怎么协调,要根据具体的协议来决定。例如,HTTP协议规定客户端必须先发请求给服务器,服务器收到后才发数据给客户端。app
# 接收数据:
buffer = []
while True:
# 每次最多接收1k字节:
d = s.recv(1024)
if d:
buffer.append(d)
else:
break
data = b''.join(buffer)
复制代码
# 接收数据:
buffer = []
while True:
# 每次最多接收1k字节:
d = s.recv(1024)
if d:
buffer.append(d)
else:
break
data = b''.join(buffer)
复制代码
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
复制代码
建立一个基于IPv4和TCP协议的Socket。socket
# 监听端口:
s.bind(('127.0.0.1', 9999))
s.listen(5)
print('Waiting for connection...')
复制代码
listen()
方法传入的参数指定等待链接的最大数量。tcp
while True:
# 接受一个新链接:
sock, addr = s.accept()
# 建立新线程来处理TCP链接:
t = threading.Thread(target=tcplink, args=(sock, addr))
t.start()
复制代码
每一个链接都必须建立新线程(或进程)来处理,不然,单线程在处理链接的过程当中,没法接受其余客户端的链接:函数式编程
def tcplink(sock, addr):
print('Accept new connection from %s:%s...' % addr)
sock.send(b'Welcome!')
while True:
data = sock.recv(1024)
time.sleep(1)
if not data or data.decode('utf-8') == 'exit':
break
sock.send(('Hello, %s!' % data.decode('utf-8')).encode('utf-8'))
sock.close()
print('Connection from %s:%s closed.' % addr)
复制代码
TCP是创建可靠链接,而且通讯双方均可以以流的形式发送数据。相对TCP,UDP则是面向无链接的协议。函数
使用UDP协议时,不须要创建链接,只须要知道对方的IP地址和端口号,就能够直接发数据包。可是,能不能到达就不知道了。
虽然用UDP传输数据不可靠,但它的优势是和TCP比,速度快,对于不要求可靠到达的数据,就能够使用UDP协议。
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定端口:
s.bind(('127.0.0.1', 9999))
复制代码
建立Socket时,SOCK_DGRAM
指定了这个Socket的类型是UDP。绑定端口和TCP同样,可是不须要调用listen()
方法,而是直接接收来自任何客户端的数据:
print('Bind UDP on 9999...')
while True:
# 接收数据:
data, addr = s.recvfrom(1024)
print('Received from %s:%s.' % addr)
s.sendto(b'Hello, %s!' % data, addr)
复制代码
recvfrom()
方法返回数据和客户端的地址与端口,这样,服务器收到数据后,直接调用sendto()
就能够把数据用UDP发给客户端。
注意这里省掉了多线程,由于这个例子很简单。
客户端使用UDP时,首先仍然建立基于UDP的Socket,而后,不须要调用connect()
,直接经过sendto()
给服务器发数据:
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in [b'Michael', b'Tracy', b'Sarah']:
# 发送数据:
s.sendto(data, ('127.0.0.1', 9999))
# 接收数据:
print(s.recv(1024).decode('utf-8'))
s.close()
复制代码
从服务器接收数据仍然调用recv()
方法。