下图显示了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编程

从使用Python开发一个Socket示例说到开发者的思维和习惯问题

Sockets programming in Python