作者: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协议的服务器。
492

被折叠的 条评论
为什么被折叠?



