我目前正在用Python编写一个HTTP服务器的小型实现。但是,在生成请求消息时,遇到一个问题,其中文件的二进制内容未正确附加到我的OK响应消息中。
这是预先格式化的好的响应:
OK = "HTTP/1.1 200 OK{} Content-Type:{}{}{}".format(CRLF, contentType,
CRLF, CRLF)
当服务器收到对某些资源(在本例中为html文件,图像或mp3)的请求时,它将解析该请求并提取该资源的名称。特别是,我在使用此功能时遇到了麻烦:
def getContents(fname):
with open(fname, 'rb') as f:
return f.read()
跟踪:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 916, in _bootstrap_inner
self.run() File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/threading.py", line 864, in run
self._target(*self._args, **self._kwargs)
File "myServer.py", line 46, in processRequest
response = getRequest(request)
File "myServer.py", line 82, in getRequest
return OK + getContents(resource)
File "myServer.py", line 136, in getContents
return OK + f.read()
TypeError: must be str, not bytes
在其他实现中,我之前已经看到过此操作(即OK + 读取文件的二进制内容),但是对于从何处进行操作,我有些困惑。 / p>
这是所有有兴趣者的全部源代码(请记住,这是有意的基本实现):
import sys, threading, os, socket, stat
from socket import *
contentType = None
CRLF = "\r\n"
METHOD_NOT_ALLOWED = "HTTP/1.1 405 METHOD NOT ALLOWED{}Allow: GET, HEAD, POST {}Connection: close{}{}".format(CRLF, CRLF, CRLF, CRLF)
OK = "HTTP/1.1 200 OK{} Content-Type:{}{}{}".format(CRLF, contentType, CRLF, CRLF)
NOT_FOUND = 'HTTP/1.1 404 NOT FOUND{}Connection: close{}{}'.format(CRLF, CRLF, CRLF)
FORBIDDEN = 'HTTP/1.1 403 FORBIDDEN{}Connection: close{}{}'.format(CRLF, CRLF, CRLF)
MOVED_PERMANENTLY = 'HTTP/1.1 301 MOVED PERMANENTLY{}Location: *redacted*{}Connection: close{}{}'.format(CRLF, CRLF, CRLF, CRLF)
contentType = None
def main():
if (len(sys.argv) == 1):
port = 9001
else:
if (len(sys.argv) == 2):
port = int(sys.argv[1])
elif (len(sys.argv) > 2):
print("Invalid number of arguments; Port number defaulted to 9001...\n")
port = 9001
host = "localhost"
#creates socket object; SOCK_STREAM for TCP
serverSock = socket(AF_INET, SOCK_STREAM)
#bind socket to host on specified port
serverSock.bind((host, port))
#listen on the socket with the maximum number of queued client connections set to 128
serverSock.listen(128)
print("Server is listening...\n")
while 1:
#block until a client connects to the designated local port
connectionSock, addr = serverSock.accept()
print("Client connection accepted; starting server thread...\n")
server = threading.Thread(target=processRequest, args=[connectionSock, addr[0]])
server.start()
def processRequest(connectionSock, srcAddress):
request = connectionSock.recv(4096).decode("utf-8")
print("######\nREQUEST:\n{}\n######".format(request))
method = ((request[0:4]).strip()).upper()
if method == "GET":
response = getRequest(request)
elif method == "POST":
response = postRequest(request)
elif method == "HEAD":
response = headRequest(request)
else:
response = METHOD_NOT_ALLOWED
connectionSock.send(bytes(response, "utf-8"))
connectionSock.shutdown(1)
connectionSock.close()
def headRequest(request):
resource = getResource(request)
path = os.path.join(".", resource)
if resource == *redacted*:
return MOVED_PERMANENTLY
elif not os.path.exists(resource):
return NOT_FOUND
elif not checkPerms(resource):
return FORBIDDEN
else:
getContentType(resource)
return OK
def getRequest(request):
headResponse = headRequest(request)
if headResponse == MOVED_PERMANENTLY:
return MOVED_PERMANENTLY + *redacted*
elif headResponse == NOT_FOUND:
return NOT_FOUND + getContents("404.html")
elif headResponse == FORBIDDEN:
return FORBIDDEN + getContents("403.html")
else:
resource = getResource(request)
getContentType(resource)
return OK + getContents(resource)
def postRequest(request):
linelist = request.strip().split(CRLF)
formInputs = linelist[-1].split("&")
eventname = formInputs[0][10:]
location = formInputs[1][9:]
starttime = (formInputs[2][10:]).replace("%3A", ":")
endtime = (formInputs[3][8:]).replace("%3A", ":")
day = formInputs[4][4:]
responseHTML = """
<html>
<head>
<title>Form Submission</title>
</head>
<body>
<h1>Following Form Data Submitted Successfully</h1>
<table>
<tr>
<td>eventname</td>
<td>{}</td>
</tr>
<tr>
<td>starttime</td>
<td type=time>{}</td>
</tr>
<tr>
<td>endtime</td>
<td type=time>{}</td>
</tr>
<tr>
<td>location</td>
<td>{}</td>
</tr>
<tr>
<td>day</td>
<td>{}</td>
</tr>
</table>
</body>
</html>
""".format(eventname, starttime, endtime, location, day)
response = OK + responseHTML
return response
def getResource(request):
linelist = request.strip().split(CRLF)
reqline = linelist[0]
rlwords = reqline.split()
return rlwords[1][1:]
def getContents(fname):
with open(fname, 'rb') as f:
return f.read()
def checkPerms(resource):
"""Returns True if resource has read permissions set on 'others'"""
stmode = os.stat(resource).st_mode
return (getattr(stat, 'S_IROTH') & stmode) > 0
def getContentType(resource):
splitResource = resource.split(".")
fileType = splitResource[1]
if fileType == "png" or fileType == "jpg":
contentType = "image/" + fileType
elif fileType == "mp3":
contentType = "audio/mpeg"
elif fileType == "css":
contentType = "text/css"
else:
contentType = "text/html"
return
main()
由于许多资源都是简单的html文件,因此我之前是读取而不是二进制读取,然后将字符串附加到OK。但是,由于明显的原因,这不适用于图像或mp3文件。我在这里相对较新,因此请原谅任何未能遵守适当礼节的人(并请务必指出失败之处!)。任何帮助将不胜感激!
答案 0 :(得分:0)
def processRequest(connectionSock, srcAddress):
request = connectionSock.recv(4096).decode("utf-8")
print("######\nREQUEST:\n{}\n######".format(request))
method = ((request[0:4]).strip()).upper()
if method == "GET":
response = getRequest(request)
elif method == "POST":
response = postRequest(request)
elif method == "HEAD":
response = headRequest(request)
else:
response = METHOD_NOT_ALLOWED
connectionSock.send(bytes(response, "utf-8"))
connectionSock.shutdown(1)
connectionSock.close()
尝试使用connectionSock.send(bytes(response))。不论出于何种原因,有时在添加额外的编码时都会遇到上述错误。
答案 1 :(得分:0)
听起来与“计算机网络:自上而下方法”中的套接字编程分配足够接近。大声笑
这个想法是,一旦建立了TCP连接,服务器就可以多次将数据发送到TCP连接中。换句话说,您不必在字节数据后附加HTTP OK消息,而是可以执行以下操作:
<div class="grid-container">
<div class="item1 item">1</div>
<div class="item2 item">2</div>
<div class="item3 item">3</div>
<div class="item4 item">4</div>
<div class="item5 item">5</div>
<div class="item6 item">6</div>
<div class="item7 item">7</div>
<div class="item8 item">8</div>
<div class="item9 item">9</div>
</div>
在客户端,您应该知道如何首先读取HTTP消息,然后处理响应正文中的字节码。