Python3网络通信:Socket

Socket(套接字)是底层网络通信编程的手段,它将操作系统的网络协议栈封装成函数方便程序员调用。Socket最高支持到传输层(TCP/UDP)协议,而不提供任何应用层的协议,因此是“底层网络编程接口”。

如果用Socket编写适应应用层协议的程序(比如HTTP协议,从一个网站上GET一个网页),需要程序员手动封装应用层协议,会很麻烦,除非没有轮子,没人会这么做。所以Socket常常用来编写自定义的网络应用,比如你编写了个联机小游戏、或是最简单的局域网聊天程序,就可以使用socket交互信息。

先试一下最简单的两个主机通信的程序——单向聊天程序,你可以在同一局域网上的两台个人电脑上测试。

测试步骤:
1、在一个机器上运行server.py,它打印了本机IP
2、在另一个机器上运行client.py,输入服务端的IP,建立连接后,服务端会打印客户端的IP和端口
3、在client.py中输入消息,server.py会收到
两个程序的运行经历了以下步骤,服务端server.py运行后监听1234号端口的连接请求,客户端client.py请求连接服务端的1234号端口,服务器允许连接后,分配了一个临时端口作为客户端端口,然后一直等待客户发来聊天信息。而客户则发送消息。

# python3 server.py

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
ip = socket.gethostbyname(socket.gethostname())
print("ip: "+ip)
port = 1234
s.bind((ip,port))
s.listen(5)
c, addr = s.accept()
print("connection build with %s:%s"%addr)
while True:
    print(str(c.recv(1024), encoding="utf8"))
# python3 client.py

import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
port = 1234
s.connect((input("server ip> "), port))
while True:
    s.send(bytes(input("> "), encoding="utf8"))

然而这个程序现在缺乏健壮性,如果网络连接不好程序会报异常错跳出,这就需要try except结构来处理错误。如果聊天的双方有一个下线了,另一方的程序也会继续等待,而不能检测出对方的下线操作。这就需要设置套接字超时。

下面我们举一个用socket实现HTTP GET请求的例子,这就需要我们手写HTTP协议。我们只需要编写客户端,而服务端就是我们要访问的网站。

# python3 httpget.py

import socket

request = """
GET / HTTP/1.1
Host: wangxuan.science

"""

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("wangxuan.science", 80))
s.send(bytes(request, encoding="utf-8"))
print(str(s.recv(65536), encoding="utf-8"))

运行这个程序打印的是wangxuan.science的首页的响应报文。可以看到,程序发送了一个HTTP请求,得到了一个HTTP应答,应答分为两部分:前是HTTP首部,之后是获得的html文件的内容,把之后的内容复制到一个html文件里用浏览器打开,可以看到我的博客主页(大概会有乱码)。实际上通过这个程序我们无意的窥探了HTTP协议的基本工作原理。无非是建立在TCP连接上的,客户端(浏览器)发送一定格式的请求,服务器接收请求后发送一定格式的HTTP首部和页面,浏览器再负责把页面渲染出来。

如果我们想编写HTTP有关的程序,比如爬虫、web服务器,并不会直接用Socket,而是用现成的框架或接口就行,比如Python的urllib库,它们免去了手写HTTP协议的麻烦。