python socket实现web服务器,邮件客户端

本博客是针对,《计算机网络自顶向下方法》一书第二章后面套接字编程作业,
所有代码均已上传至我的github:https://github.com/inspurer/ComputerNetwork
所有代码均本人亲自编写,有问题欢迎评论交流;
如需转载请联系:2391527690@qq.com

作业1: Web服务器

问题描述

使用Python开发一个简单的Web服务器,它仅能处理一个请求,具体而言,你的服务器将

当一个客户(浏览器)联系时创建一个连接套接字;

这个连接接受http请求;

解释该请求以确定所请求的特定文件;

从服务器的文件系统获得请求的文件;

创建一个由请求的文件组成的HTTP响应报文,报文前有首部行;

经TCP连接向请求的浏览器发送响应;

如果文件不存在,返回404 Not Found

问题解决

主要代码

服务端代码

from socket import *
serverSocket = socket(AF_INET, SOCK_STREAM)
serverSocket.bind(("127.0.0.1",9999))
serverSocket.listen(1)
# 没有客户端链接时一直在此阻塞
connectionSocket, addr = serverSocket.accept()
while True:
    print('waiting for connection...')
    try:
        #接收1k数据
        data = connectionSocket.recv(1024)
        print(data)
        if not data:
            continue
        #data是一个get的http请求报文
        filename = data.split()[1] #filename = /HelloWorld.html
        # #print(filename[1:])
        f = open(filename[1:],encoding="utf-8") #f = HelloWorld.html
        outputdata = f.read()
        header = 'HTTP/1.1 200 OK\r\n\r\n'
        #回复报文
        connectionSocket.send(header.encode())
        for i in range(0, len(outputdata)):
            connectionSocket.send(outputdata[i].encode())
        #connectionSocket.close()
        
    except IOError:
        header = 'HTTP/1.1 404 NOT FOUND\r\n\r\n'
        connectionSocket.send(header.encode())
        connectionSocket.close()
# 浏览器键入 localhost:***/index.html会有两个请求
# index.html && favicon.ico(网站的图标)

客户端代码

from socket import *
ClientSocket = socket(AF_INET, SOCK_STREAM)
ClientSocket.connect(('localhost',9999))
while True:
    #这里的Connetction: close不同于浏览器常见的keep-alive,
    #close表示要求服务器在发送完被请求的对象后就关闭这条链接
    Head = '''GET /index.html HTTP/1.1\r\nHost: localhost:9999\r\nConnection: close\r\nUser-agent: Mozilla/5.0\r\n\r\n'''
    ClientSocket.send(Head.encode('utf-8'))
    data = ClientSocket.recv(1024)
    print(data)
    with open("response.html","wb") as f:
        f.write(data)

How to run

首先运行服务端代码WebServer.py

可以直接运行WebClient.py

此时会在工程下得到一个响应文件response.html

也可以在浏览器输入localhost:9999/index.html

浏览器方式时,需要取消对服务端代码第25行#connectionSocket.close()的注释再运行,至于为什么是这样,这是个历史遗留问题,有兴趣还是私戳我邮箱交流吧。

捕获.PNG

作业2: UDP ping程序

问题描述

使用python采用UDP协议编写一个ping程序,发送一个简单的ping报文给服务器,并确定从客户发送ping报文服务器到接受到pong报文为止的时延,称为往返时延(RTT) 。
因为UDP是一个不可靠的协议,客户发送的分组可能会丢失,为此,客户不能无限期地等待服务器的响应,等待时间至多为1s,否则,打印一条错误信息。

问题解决

主要代码

服务端代码

import random
from socket import *
#AF_INET指定使用IPv4协议,如果要用更先进的IPv6,就指定为AF_INET6
#SOCK_DGRAM指定了这个Socket的类型是UDP
serverSocket = socket(AF_INET, SOCK_DGRAM)
#用0.0.0.0绑定到所有的网络地址,还可以用127.0.0.1绑定到本机地址
serverSocket.bind(('127.0.0.1',12000))

while True:
    #产生一个0到10之间的随机数
    rand = random.randint(0, 10)
    #从套接口上读取数据,参数为缓冲区大小
    message, address = serverSocket.recvfrom(1024)
    #通过打印我们可以看到UDP客户端socket的端口是不确定,系统随机分配的
    print("收到来自 %s 的报文: (%s)" % (address,message))
    # 把接收到的信息全部转为大写
    print("随机数是: %d" % rand)
    message = message.upper()
    #如果随机数小于4,服务端无应答,客户端就会超时
    if rand < 4:
        continue
    serverSocket.sendto(message, address)

客户端代码

import time
from socket import *
serverName = '127.0.0.1' # 主机
serverPort = 12000
# 创建Socket时,AF_INET指定使用IPv4协议,如果要用更先进的IPv6,就指定为AF_INET6
# SOCK_DGRAM指定了这个Socket的类型是UDP
# SOCK_STREAM指定使用面向流的TCP协议
clientSocket = socket(AF_INET, SOCK_DGRAM)
clientSocket.settimeout(1) # 设置超时时间为1s
for i in range(0, 10):
    oldTime = time.time()
    sendTime = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(oldTime))
    # encode()把str转成bytes,传输格式要求
    message = ('package %d,client_local_time:%s' % (i + 1, sendTime)).encode()
    try:
        # 发送数据
        clientSocket.sendto(message, (serverName, serverPort))
        # 1024指定要接收的最大数据量为1kb = 1024 bytes
        # recvfrom是一个系统调用,由用户态转向系统态,从套接口上接收数据,并捕获数据发送源的地址。
        # 如果数据报大于缓冲区,那么缓冲区中只有数据报的前面部分,其他的数据都丢失了,并且recvfrom()函数返回WSAEMSGSIZE错误
        # 如果没有数据待读,那么除非是非阻塞模式,不然的话套接口将一直等待数据的到来,果没有在Timeout = 1s内接收到数据,此时将返回SOCKET_ERROR错误,错误代码是WSAEWOULDBLOCK。用select()或WSAAsynSelect()可以获知何时数据到达
        # UDP的 recvfrom() 和 TCP 的recv()不一样,具体可以看 TCP Ping项目
        modifiedMessage, serverAddress = clientSocket.recvfrom(1024)
        # 计算往返时间
        rtt = time.time() - oldTime
        # decode 把bytes转成str
        modifiedMessage = modifiedMessage.decode("utf-8")
        print('报文 %d 收到来自 %s 的应答: %s,往返时延(RTT) = %fs' % (i+1, serverName,modifiedMessage, rtt))
    except Exception as e:
        print('报文 %d: 的请求超时' % (i+1)) # 处理异常

How to Run

先运行服务端代码,再运行客户端代码,注意不要占用端口。


2.PNG

邮件客户

问题描述

使用STMP协议从一个邮箱向另一个邮箱发送邮件

问题解决

可以先了解一下:Windows下操作POP3

主要代码

#作业3:邮件客户
from smtplib import SMTP
from email.mime.text import MIMEText
from email.header import Header

mail_server = 'smtp.163.com'
#根据发送方邮箱确定邮箱服务器
#qq邮箱的服务器为smtp.qq.com;163邮箱为smtp.163.com
def get_mail_server(sender):
    key = sender[sender.index('@')+1:]
    return "smtp."+key

port = '25'  ## SMTP协议默认端口是25
sender = '2391527690@qq.com'
mail_server = get_mail_server(sender)
sender_pass = 'put your mail_code here'    #注意是授权码,而不是登录密码,需要在邮箱端先获取
receiver = 'csu_xiaotao@163.com'
mail_msg = 'this is a demo'

#第一个参数就是邮件正文,
# 第二个参数是MIME的subtype,传入'plain'表示纯文本,最终的MIME就是'text/plain',
# 最后一定要用utf-8编码保证多语言兼容性。
msg = MIMEText(mail_msg, 'plain', 'utf-8')
msg['From'] = sender
msg['To'] = receiver
#Header对象编码文本,包含utf-8编码信息和Base64编码。
msg['Subject'] = Header('来自inspurer的个人计算机', 'utf-8')
try:
    server = SMTP(mail_server, port)
    #用set_debuglevel(1),可以打印出和SMTP服务器交互的所有信息
    #server.set_debuglevel(1)
    server.login(sender, sender_pass)
    #由于可以一次发给多个人,所以传入一个list,邮件正文是一个str,as_string()把MIMEText对象变成str
    server.sendmail(sender, (receiver), msg.as_string() )
    server.quit()
    print("邮件发送成功!")
except:
    server.quit()
    print("邮件发送失败!")

How to Run

直接运行stmpDemo.py,<font size = "5" color = "red">注意代码里的邮箱授权码要填成你自己的,它和邮箱登录密码不一样,至于怎么获取百度吧,我不做搬运工</font>

3.PNG

4.PNG

作业4: 多线程Web代理服务器

写到这发现深夜了,以后更新

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容