下图显示了TCP的通信流程,摘自从使用Python开发一个Socket示例说到开发者的思维和习惯问题:
##1、先来一个示例 ###1.1、服务器端tcp_server.py:
#-*-encoding:utf-8-*-
from socket import *
s = socket(AF_INET, SOCK_STREAM)
s.bind(('127.0.0.1', 8888))
s.listen(5)
while True:
client, addr = s.accept()
print 'got a connection from: ', addr
print client.recv(1024)
client.send('hello, this is server')
client.close()
既然是服务器端,服务器要做的工作是:
1、建立TCP套接字
socket(AF_INET, SOCK_STREAM)
参数AF_INET
指明地址族规范为IPv4,SOCK_STREAM
指明使用的套接字类型是面向对象的可靠字节流,即TCP。
2、绑定地址,用于监听
s.bind(('127.0.0.1', 8888))
地址族不同,bind()的参数也会有所不同。在使用TCP时,bind()的参数是一个元组,元组第一个元素是IP,也可以是域名,也可以为空字符串(这表示主机上的任何internet接口);元组第二个参数是绑定的端口。
3、开始监听来自客户端的连接
s.listen(5)
参数是指最大的挂起连接数量。即最多允许5个客户端连接到这个服务器,来自客户端的连接会排队,队满后拒绝其他的连接。
4、接受连接
client, addr = s.accept()
等待并获取一个连接。返回值client是连接对象,addr是客户端的套接字地址。client对应一个TCP连接。
此后就可以使用send()和recv()和客户端交互了。
5、接收数据
client.recv(1024)
1024指定要接收的最大数据量。recv()可以根据需要调用多次。如果已经与客户端断开连接了,recv()将返回空字符串。
6、发送数据
client.send('hello, this is server')
send()可以根据需要调用多次。其返回值是要发送的字节数量。
7、关闭TCP连接
client.close()
8、处理下一个连接。
###1.2、客户端tcp_client.py:
#-*-encoding:utf-8-*-
from socket import *
s = socket(AF_INET, SOCK_STREAM)
s.connect(('127.0.0.1', 8888))
s.send('hello, this is client')
print s.recv(1024)
s.close()
这里只有一个新的函数:
s.connect(('127.0.0.1', 8888))
即创建一个目标地址是('127.0.0.1', 8888)的TCP连接。
###1.3、运行 打开终端A,执行:
$ python tcp_server.py
打开终端B,执行:
$ python tcp_client.py
hello, this is server
hello, this is server
是马上就输出的。
可以看到终端A输出以下信息:
got a connection from: ('127.0.0.1', 43638)
hello, this is client
客户端的端口 43638 是操作系统随机分配的。
##2、限制->一次只能处理一次连接 上述代码其实是串行地处理TCP连接,即一次只能处理一个连接。我们可以使用time.sleep验证一下。
将tcp_server.py内容修改如下:
#-*-encoding:utf-8-*-
from socket import *
import time
s = socket(AF_INET, SOCK_STREAM)
s.bind(('127.0.0.1', 8888))
s.listen(5)
while True:
client, addr = s.accept()
print 'got a connection from: ', addr
print client.recv(1024)
client.send('hello, this is server')
time.sleep(10) #sleep 10 秒
client.close()
服务器将hello, this is server
发送出去后会睡眠10秒。
打开终端A,运行:
$ python tcp_server.py
打开终端B,运行:
$ python tcp_client.py
hello, this is server
结果会即时地出现,不过如果迅速打开终端C,运行:
$ python tcp_client.py
需要等待几秒后才会出现结果。这就意味着如果服务器需要处理一个耗时的操作,后续的客户端请求则很难及时地响应。
另外一种情况是,在tcp_client.py的
s.connect(('127.0.0.1', 8888))
后如果time.sleep(10)后再想server发送数据,server会阻塞。
这种情况并非我们期望的,可以使用多线程等方法来解决。
##3、基于HTTP的简单服务器
将tcp_server.py内容修改如下:
#-*-encoding:utf-8-*-
from socket import *
s = socket(AF_INET, SOCK_STREAM)
s.bind(('127.0.0.1', 8888))
s.listen(2)
while True:
client, addr = s.accept()
print 'got a connection from: ', addr
print client.recv(1024)
msg = '''HTTP/1.0 200 OK
Server: python
Date: Fri, 01 Aug 2014 06:44:11 GMT
Content-Type: text/html;charset=UTF-8
<h1>hello</h1>
'''
print msg
client.send(msg)
client.close()
运行上面的代码,在浏览器访问http://127.0.0.1:8888
,可以得到html<h1>hello</h1>
。或者使用curl:
$ curl http://127.0.0.1:8888
<h1>hello</h1>
##4、参考 传输控制协议
从使用Python开发一个Socket示例说到开发者的思维和习惯问题