python服务器

作者:Vamei 出处:http://www.cnblogs.com/vamei

在Python中,我们使用标准库中的socket包来进行底层的socket编程。
server端:

首先是服务器端,socket.socket()创建一个socket对象,我们使用bind()方法来赋予socket以固定的地址和端口,并使用listen()方法来被动的监听该端口。当有客户尝试用connect()方法连接的时候,服务器使用accept()接受连接,从而建立一个连接的socket:

import socket

HOST=''
PORT=8000

reply=b"yes"

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.bind((HOST,PORT))

s.listen(3)
conn,addr=s.accept()


request=conn.recv(1024) #接收1024B

print('request is',request)
print('Connected by',addr)

conn.sendall(reply)
conn.close()

client端:
然后用另一台电脑作为客户,我们主动使用connect()方法来搜索服务器端的IP地址(在Linux中,你可以用$ifconfig来查询自己的IP地址)和端口,以便客户可以找到服务器,并建立连接。

我们对socket的两端都可以调用recv()方法来接收信息,调用sendall()方法来发送信息。这样,我们就可以在分处于两台计算机的两个进程间进行通信了。当通信结束的时候,我们使用close()方法来关闭socket连接。

(如果没有两台计算机做实验,也可以将客户端IP想要connect的IP改为“127.0.0.1”,这是个特殊的IP地址,用来连接当地主机。)

import socket

HOST='127.0.0.1'
PORT=8000

request=b'Can you hear me?'

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect((HOST,PORT))

s.sendall(request)

reply=s.recv(1024)

print("reply is: ",reply)

s.close()

注意:server与client的端口号要一致(PORT)

s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

参数一:地址簇
socket.AF_INET 表示IPV4(默认)
socket.AF_INET6 表示IPV6
socket.AF_UNIX 只能用于单一的Unix系统进程间的通信

参数二:类型
socket.SOCK_STREAM 流式socket for TCP(默认)
socket.SOCK_DGRAM 数据格式socket,for UDP
socket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP,IGMP等网络报文,可以通过IP_HDRINCL套接字选项由用户构造IP头

listen(cnt)

cnt指等待队列中最大TCP请求个数。

s.accept()

接收连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据,address是连接客户端的地址。

s.sendall(string)

将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有的数据,成功返回None,失败则抛出异常。内部通过递归调用send,将所有内容发送出去。

HTTP服务器



#encode()-->string转为bytes
#decode()-->bytes转为string

import socket


HOST = ''
PORT = 8003


text_content = '''HTTP/1.x 200 OK  
Content-Type: text/html

<head>
<title>WOW</title>
</head>
<html>
<p>Wow, Python Server</p>
<IMG src="test.jpg"/>
</html>
'''


f = open('test.jpg','rb')
pic_content = '''
HTTP/1.x 200 OK  
Content-Type: image/jpg

'''

pic_content = pic_content.encode()+ f.read()         #用encode()-->转化为bytes类型才可以发送
print(type(pic_content))
f.close()


s    = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))

# infinite loop, server forever
while True:
    # 3: maximum number of requests waiting
    s.listen(3)
    conn, addr = s.accept()
    request    = conn.recv(1024).decode()          #转为string类型

    method    = request.split(' ')[0]

    src            = request.split(' ')[1]

    # deal with GET method
    if method == 'GET':
        # ULR    
        if src == '/test.jpg':
            content = pic_content
        else: content = text_content.encode()     #转换为bytes类型以便sendall()发送!

        print ('Connected by', addr)
        print ('Request is:', request)
        print(type(request))

        conn.sendall(content)                   #python3中 socket.send()或socket.sendall() 传递的数据必须是bytes。

    conn.close()

支持POAT


我首先增加该服务器的功能。这里增添了表格,以及处理表格提交数据的”POST”方法。如果你已经读过用socket写一个Python服务器,会发现这里只是增加很少的一点内容。

原始程序:(运行环境:python3.6)


#encode()-->string转为bytes
#decode()-->bytes转为string

import socket

# Address
HOST = ''
PORT = 8005

# Prepare HTTP response
text_content = '''HTTP/1.x 200 OK  
Content-Type: text/html

<head>
<title>WOW</title>
</head>
<html>
<p>Wow, Python Server</p>
<IMG src="test.jpg"/>
<form name="input" action="/" method="post">
    First name:<input type="text" name="firstname"></br>
    <input type="submit" value="Submit">
</form>
</html>
'''

# Read picture, put into HTTP format
f = open('test.jpg','rb')
pic_content = '''
HTTP/1.x 200 OK  
Content-Type: image/jpg

'''

pic_content = pic_content.encode()+ f.read()     #用encode()-->转化为bytes类型才可以发送
print(type(pic_content))
f.close()

# Configure socket
s    = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((HOST, PORT))

# infinite loop, server forever
while True:
    # 3: maximum number of requests waiting
    s.listen(3)
    conn, addr = s.accept()
    request    = conn.recv(2048).decode()         #转为string类型   
    method    = request.split(' ')[0]
    src       = request.split(' ')[1]

    print ('Connected by', addr)
    print ('Request is:', request)
    print("----------------")

    # deal with GET method
    if method == 'GET':
        # ULR    
        if src == '/test.jpg':
            content = pic_content
        else: content = text_content.encode()     #转换为bytes类型以便sendall()发送!       
        conn.sendall(content)                     #python3中 socket.send()或socket.sendall() 传递的数据必须是bytes。

    if method=='POST':
        form=request.split('\r\n')
        idx=form.index('')                       #在form中找到'',即一空行
        print("idx",idx)#idx=13
        entry = form[idx:]                       #entry=form的13行到最后一行
        value = entry[-1].split('=')[-1]         #entry的最后一行为firstname=444--》以等号分隔,获得value=444
        conn.sendall((text_content + '\n <p>' + value + '</p>').encode())


    # close connection
    conn.close()

从浏览器看到的POST请求为:

POST / HTTP/1.1
 
Host: 192.168.1.106:8005
 
Connection: keep-alive
 
Content-Length: 13
 
Cache-Control: max-age=0
 
Origin: http://192.168.1.106:8005
 
Upgrade-Insecure-Requests: 1
 
User-Agent: Mozilla/5.0 (Linux; U; Android 7.1.1; zh-cn; OPPO R11s Build/NMF26X) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/53.0.2785.134 Mobile Safari/537.36 OppoBrowser/4.4.1
 
Content-Type: application/x-www-form-urlencoded
 
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,/;q=0.8
 
Referer: http://192.168.1.106:8005/
 
Accept-Encoding: gzip, deflate
 
Accept-Language: zh-CN,zh;q=0.8
 
 
 
firstname=444

网页的部分截图:
网页的部分截图

使用SocketServer


注意:*注意,在Python 3.x中,BaseHTTPServer, SimpleHTTPServer, CGIHTTPServer整合到http.server包,SocketServer改名为socketserver,请注意查阅官方文档。

import socketserver

HOST = ''
PORT = 8002

text_content = '''HTTP/1.x 200 OK  
Content-Type: text/html

<head>
<title>WOW</title>
</head>
<html>
<p>Wow, Python Server</p>
<IMG src="test.jpg"/>
<form name="input" action="/" method="post">
    First name:<input type="text" name="firstname"></br>
    <input type="submit" value="Submit">
</form>
</html>
'''

# Read picture, put into HTTP format
f = open('test.jpg','rb')
pic_content = '''
HTTP/1.x 200 OK  
Content-Type: image/jpg

'''

pic_content = pic_content.encode()+ f.read()                #用encode()-->转化为bytes类型才可以发送

class MyTCPHandler(socketserver.BaseRequestHandler):
    def handle(self):
        # self.request is the TCP socket connected to the client
        request=self.request.recv(1024).decode()

        print('Connected by',self.client_address[0])
        print('Request is',request)

        method=request.split(' ')[0]
        src   =request.split(' ')[1]

        if method == 'GET':
            if src == '/test.jpg':
                content = pic_content
            else: 
                content = text_content.encode()

            self.request.sendall(content)

        if method == 'POST':
            form = request.split('\r\n')
            idx = form.index('')             # Find the empty line
            entry = form[idx:]               # Main content of the request

            value = entry[-1].split('=')[-1]
            self.request.sendall((text_content + '\n <p>' + value + '</p>').encode())
            ######
            # More operations, such as put the form into database
            # ...
            ######

server=socketserver.TCPServer((HOST,PORT),MyTCPHandler)

server.serve_forever()

我建立了一个TCPServer对象,即一个使用TCP socket的服务器。在建立TCPServe的同时,设置该服务器的IP地址和端口。使用server_forever()方法来让服务器不断工作(就像原始程序中的while循环一样)。

我们传递给TCPServer一个MyTCPHandler类。这个类定义了如何操作socket。MyTCPHandler继承自BaseRequestHandler。改写handler()方法,来具体规定不同情况下服务器的操作。

在handler()中,通过self.request来查询通过socket进入服务器的请求 (正如我们在handler()中对socket进行recv()和sendall()操作),还使用self.address来引用socket的客户端地址。

经过SocketServer的改造之后,代码还是不够简单。 我们上面的通信基于TCP协议,而不是HTTP协议。因此,我们必须手动的解析HTTP协议。我们将建立基于HTTP协议的服务器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值