Python对Socket提供了良好的支持,我们可以使用很多成熟的模块和框架来实现Socket通信。
0x01 基本模型
服务端:
#/usr/bin/env python
#coding:utf-8
__author__ = 'kikay'
import socket
import threading
import datetime
#处理sock连接的线程类
class SockThread(threading.Thread):
def __init__(self,iSock,iAddr):
super(SockThread,self).__init__()
self.__socket=iSock
self.__ipAddr=iAddr
self.__discon=False
def run(self):
while not self.__discon:
try:
self.__socket.sendall(bytes(('current time is {t}')
.format(t=datetime.datetime.now()
.strftime('%Y-%m-%d %H:%M:%S'))))
strRecv=str(self.__socket.recv(1024))
print 'receive from {addr} ({dataRecv})'.format(addr=self.__ipAddr,dataRecv=strRecv)
if strRecv=='quit':
self.discon()
except Exception,e:
self.discon()
def discon(self):
self.__discon=True
self.__socket.close()
print 'discon from {addr}'.format(addr=self.__ipAddr)
def main():
host='0.0.0.0'
port=6666
addr=(host,port)
block=5
tcpSock=socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM
,proto=socket.IPPROTO_TCP)
tcpSock.bind(addr)
print 'socket({addr}) is listening...'.format(addr=addr)
tcpSock.listen(block)
while True:
con,addrClient=tcpSock.accept()
print 'connect from {addr}'.format(addr=addrClient)
t=SockThread(con,addrClient)
t.daemon=True
t.start()
if __name__=='__main__':
main()
客户端:
#/usr/bin/env python
#coding:utf-8
__author__ = 'kikay'
import socket
def main():
tcpSock=socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM
,proto=socket.IPPROTO_TCP)
#连接
host='127.0.0.1'
port=6666
addr=(host,port)
print 'connect {host}:{port}...'.format(host=host,port=port)
tcpSock.connect(addr)
while True:
recvData=str(tcpSock.recv(1024))
print 'receive from {server}:(dataRecv)'.format(server=host,dataRecv=recvData)
data=raw_input('>>>')
tcpSock.sendall(bytes(data))
print 'send data({dataSend})'.format(dataSend=data)
if data=='quit':
break
if __name__=='__main__':
main()
0x02 SocketServer模型
SocketServer模块是Python中的一个高级模块,其对我们前面讲的Socket基本模型进行了封装。不像前面我们自己实现多线程,SocketServer内部使用 IO多路复用(相关知识网上比较多,可以自己搜索学习下)以及 多线程/多进程 ,从而实现并发处理多个客户端请求的Socket服务端。即:每个客户端请求连接到服务器时,Socket服务端都会在服务器是创建一个线程/进程专门负责处理当前客户端的所有请求。改写上面的服务端例子:
# /usr/bin/env python
# coding:utf-8
__author__ = 'kikay'
import SocketServer
import datetime
# 继承BaseRequestHandler的处理类
class MyServerHandler(SocketServer.BaseRequestHandler):
# 处理函数
def handle(self):
# 客户端地址
ipClient = self.client_address
# 客户端连接的socket对象
iSocket = self.request
# 服务器对象
iServer = self.server
print 'connect from {host}:{port}...'.format(host=ipClient[0], port=ipClient[1])
while True:
try:
iSocket.sendall(bytes(('current time is {t}'))
.format(t=datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')))
strRev = str(iSocket.recv(1024))
print 'receive from {addr}({dataRev})'.format(addr=ipClient, dataRev=strRev)
if strRev == 'quit':
iSocket.close()
except Exception, e:
iSocket.close()
break
def main():
host = '0.0.0.0'
port = 6666
addr = (host, port)
# 定义服务端
server = SocketServer.ThreadingTCPServer(addr, MyServerHandler)
print 'socket({addr}) is listening...'.format(addr=addr)
# 启动工作
server.serve_forever()
if __name__ == '__main__':
main()
上面的类似于基本模型中采用多线程的思路。server.serve_forever()将一直运行下去,如果要停止工作,可以调用server.shutdown()来实现。另外,还有继承BaseRequestHandler实现的StreamRequestHandler模块等,简化了数据传输,这里就不再赘述。
0x03 实例
下面看2个小例子。
(1)HTTP客户端:HTTP协议首先需要客户端发送请求给服务器,服务器收到后发送数据给客户端,下面是发送HTTP请求的一个实例代码如下:
#/usr/bin/env python
#coding:utf-8
__author__ = 'kikay'
import socket
#发送HTTP数据包,并记录返回的html页面内容
def httpRequst(addr):
client=socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM
,proto=socket.IPPROTO_TCP)
try:
client.connect(addr)
#构造html数据包,发送给服务器
client.sendall((
'GET / HTTP/1.1\r\n'
'Host: {dns}\r\n'
'Connection: close\r\n\r\n').format(dns=addr))
#接收数据
buffer=[]
while True:
temp=client.recv(1024)
if temp:
buffer.append(temp)
else:
break
#关闭连接
client.close()
data=''.join(buffer)
#分割为HTTP头和内容
head,html=data.split('\r\n\r\n',1)
print head
#保存html的内容
with open('temp.html','w') as f:
f.write(html)
except Exception,e:
print 'Error:({error})'.format(error=e.message)
def main():
host='www.ip138.com'
port=80
addr=(host,port)
httpRequst(addr)
if __name__=='__main__':
main()
访问www.ip138.com,获取返回的html页面,大家可能看到返回400页面,这是因为HTTP头的还没有“伪装好”,被对方服务器“识别”为非法了,至于怎么解决这个问题可以网上学习相关知识,我以后的博客也会涉及这些问题,这里就暂时不多讲了。
(2)HTTP服务端:
#/usr/bin/env python
#coding:utf-8
__author__ = 'kikay'
import SocketServer
class HttpServerHandler(SocketServer.BaseRequestHandler):
def handle(self):
#收到请求,就返回HTTP数据报文
self.request.sendall('HTTP/1.1 200 OK\r\n\r\nHello,world!')
def main():
host = '0.0.0.0'
port = 80
addr = (host, port)
server = SocketServer.ThreadingTCPServer(addr, HttpServerHandler)
# 启动工作
server.serve_forever()
if __name__ == '__main__':
main()
执行后,可以用(1)中的客户端进行访问(修改下host),也可以直接用浏览器访问:
0x04 后记
上面讲述了Python的Socket基本知识点,在实际研发工程中还有很多实际问题需要考虑,目前也有很多成熟的网络通信框架可以使用,比如Twisted等,在以后的博客中再讲吧。