内网版交互日记系统
写在前面
听了大妈2wd4的公开课挺有感触的,感想稍后补上。
简而言之,准备在这次任务上进行如下改进
- 用最小成本解决问题
- 先记录思考和计划,再开始码代码
- 把姿态放低,记录自己的困惑和情绪
- 清晰地记录5W1H
背景
- 系统:OS X 10.11
- python版本:Python 2.7.10
- 编辑器:Sublime Text 3
- 终端:iterm2
- shell:zsh
任务要求
在上周开发基础上, 完成 极简交互式笔记的网络版本 需求如下:
- 每次运行时合理的打印出过往的所有笔记
- 一次接收输入一行笔记
- 在服务端保存为文件:
- 在所有访问的客户端可以获得历史笔记
- 支持多个客户端同时进行笔记记录
任务分析
先仔细看芝麻星3w卡片的正反面,提取出一些关键提示
★3w Net
- UDP 协议
- 网络数据传输
- C/S 架构实现
- 网络应用调试技巧
- (网络抓包分析)
- MyDailyNet 私人记事本内网CLI版
参考:
提取关键词
- C/S 架构
- UDP
- socket (好几个卡片背后都指向它)
我在深挖官方文档前,喜欢先google一下关键词,然后简单地浏览一下,好在脑中对这个关键词形成大致印象,知道网上关于这个关键词都有怎么样的文章。
搜索了这三个关键词之后,形成的印象是
- C/S:应该就是指的client和server之间的交互
- UDP:没太看明白
- socket:python的一个库,跟网络连接有关
然后去看大妈的演示demo,公开课大妈演示3w任务demo的时候,我的页面黑屏了,切备用线路恢复过来的时候,大妈也差不多演示完了囧,还好这些演示都能在邮件里找到 益rz。
在demo中观察到的点:
- 虽然都是运行
main.py
- 但猜测server上调用了
md4srv.py
- client上调用了
md2client.py
- 但猜测server上调用了
- usage里提示参数可以输入s或者c
- 那应该server上和client上的
main.py
是一样的 - 使用参数解析
- 如果是's',调用
md4srv.py
- 如果是'c',调用
md2client.py
- 如果是's',调用
- 那应该server上和client上的
- log文件只保存在server上
总结一下要做的任务:
- 写一个本地的client脚本
- 写一个server脚本
- 将server脚本部署到服务器上
要怎么部署到服务器上,没多大概念,网上搜了下也没太理清楚。
算了,先stay calm & go to sleep
2015-10-29 end
2015-10-30
虽然分析了任务,但其实还是没太想通,没想出详细可执行的计划。
正好前几天coursera上有一门《使用 Python 访问网络数据》开课了,先去看看。
第一部分讲正则,跳过。
第二部分讲network
,相关的,看下吧。
三个视频加起来半个多小时吧,看完之后了解了
- HTTP协议,如何发送GET请求
socket
:就像电话听筒,两个socket连接之后可以传输信息
还练习了一段简单的代码
import socket
mysock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 建立一个socket对象,类似于f=open(...)建立file对象
mysock.connect(('www.py4inf.com', 80)) #将socket连接到某个host和port
mysock.send('GET http://www.py4inf.com/code/romeo.txt HTTP/1.0\n\n') #发送GET请求
while True:
data = mysock.recv(512) # 一次接受512字节的信息
if ( len(data) < 1 ) :
break
print data;
mysock.close()
看完这几个MOOC视频之后就想明白了,这周任务的关键是socket
然后网上再去搜索socket
,在这篇文章里看到了一句运行程序时,启动两个终端,然后每个终端运行一个程序就可以了
啥? 服务端的代码不需要部署到服务器,只需要终端运行就行了?
启动两个终端试了一下文章里的代码,确实可以运行。突然想起芝麻星卡片里写着MyDailyNet 私人记事本内网CLI版
,既然说了内网,那应该就是不用部署到服务器的。
上面文章的示例代码是
server端
import socket
port=8081
s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#从指定的端口,从任何发送者,接收UDP数据
s.bind(('',port))
print('正在等待接入...')
while True:
#接收一个数据
data,addr=s.recvfrom(1024)
print 'Received:',data,'from',addr
client端
import socket
port=8081
host='localhost'
s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
s.sendto(b'hello,this is a test info !',(host,port))
在测试上面代码时,发现socket.socket()里第二个参数是socket.SOCK_DGRAM,跟我之前coursera上例子里用的socket.SOCK_STREAM不太一样,查了一下发现前者对应UDP协议,后者对应HTTP协议。ok,大妈的任务里提示最好用UDP,那ok,我先主要关注UDP相关的。
此外,从上面代码学到了怎么从client发送信息给server,那我就可以先通过client发送日记信息给server,然后server接收到信息之后把信息写入log文件里。
改写、测试、成功。具体代码记录在代码repo的git历史里。
那之后需要做的就是怎么让server端发回信息给client端。
之后再用python socket UDP
进行搜索,搜到了这篇文章,里面示例了echo server和echo client,就是从socket收到了什么信息,就把同样的信息传回去。
Echo Server
import socket
import sys
# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# Bind the socket to the port
server_address = ('localhost', 10000)
print >>sys.stderr, 'starting up on %s port %s' % server_address
sock.bind(server_address)
while True:
print >>sys.stderr, '\nwaiting to receive message'
data, address = sock.recvfrom(4096)
print >>sys.stderr, 'received %s bytes from %s' % (len(data), address)
print >>sys.stderr, data
if data:
sent = sock.sendto(data, address)
print >>sys.stderr, 'sent %s bytes back to %s' % (sent, address)
Echo Client
import socket
import sys
# Create a UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_address = ('localhost', 10000)
message = 'This is the message. It will be repeated.'
try:
# Send data
print >>sys.stderr, 'sending "%s"' % message
sent = sock.sendto(message, server_address)
# Receive response
print >>sys.stderr, 'waiting to receive'
data, server = sock.recvfrom(4096)
print >>sys.stderr, 'received "%s"' % data
finally:
print >>sys.stderr, 'closing socket'
sock.close()
在这个例子的基础上进行改写,就能实现从server端将日记信息返回给client端。
但是server端一直是处于监听状态,要怎么主动把日记信息发到client端呢。
- client连接上server之后,是否改变了server的一些变量呢,根据这个变量写判断语句可以让client连上之后server就将日记信息发给它
- client先发一个特定信息给server,server判断出这是特定信息之后就把日记历史发给client
想了一下,后者更容易实现,那就用后面这个方法吧,那接下来的问题就是,怎么选择特定信息呢。
- 尝试空信息''
- 后来想想,用户可能会想输入空行,这样行不通
- 用
q
吧,用户输入q
就退出了client端的程序,所以在用户输入过程中client不会把q
传到server端- ok,那就让client已连接上就发个
q
给server,然后接受和打印server端传来的日记历史信息 - 然后server收到
q
就把日记历史信息传给client
- ok,那就让client已连接上就发个
码好代码之后,测试,基本完成任务。
那就先把任务提交了吧,之后再去深入学习官方文档。
- 开始写README文档
- 录一个演示视频,好让别人看到实现的效果
- 之前是用七牛在做图床,测试下是否能用它储存视频文件,成功。
- 搜索怎么在Markdown中引用视频
2015-10-31
现在想的的问题:
- 传输信息过大怎么办,我现在设置的是能接受4096个字节,超了怎么办?
- 多个客户端同时发送信息,服务端该怎么处理
- 如何根据客户端信息进行单独的信息处理和传输
暂时没深入思考这些问题,先给自己放个小假,学会numpy。
油管上看了一个视频,介绍websockets的,里面还提到了Wireshark这个工具。
下载安装了Wireshark,准备研究抓包。
使用截屏
学习资源:
有时间的话再研究下websockets
小插曲:
打开server端程序之后,打开client端程序,这时server端报错,看了一下,发现是我把log文件删了,然后server端程序里没写初始化log文件的功能,遂补上。
2015-11-2
找到了一个mooc视频,讲解了一些socket知识,里面对client和server中socket的行为讲解的很清楚。
client ------------ server
| |----1: socket
1: socket----| |----2: bind
| |----3: listen
5: connect*----|-----connect-------|----4: accept*
| |
7: send----|-----request-------|----6: recv*
| |
8: recv*----|-----reply---------|----9: send
| |
10: close----|----disconnect-----|----10: close
*星号的代表程序在这个步骤会停止下来,直到等到对方的信息再继续
想了想根据不同客户端进行回复的问题
- 虽然server和client连的是同一个host和port,但每次具体连接都用的是不同的端口点,所以用address来定制回复信息不太行得通。
- 有一个比较简单的解决办法,就是client先让用户报上自己名字,然后发给server,然后server根据姓名来进行回复
- server里能储存一个有效用户姓名的列表