Python学习总结笔记(8)-- Socket通信初探

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),也可以直接用浏览器访问:

Python学习总结笔记(8)-- Socket通信初探_第1张图片

0x04 后记

上面讲述了Python的Socket基本知识点,在实际研发工程中还有很多实际问题需要考虑,目前也有很多成熟的网络通信框架可以使用,比如Twisted等,在以后的博客中再讲吧。

你可能感兴趣的:(Python,python,socket)