程序需要一步步改进,解决bug,尽量从源头判断,并给出处理措施。
1.客户端执行一次,程序就退出,
2.客户端空值,错误命令,程序会死掉
3.收发缓冲区大小,即recv(1024)的问题,如果收一个100M的文件就又是问题
4.多线程问题,同时能处理多个客户端的请求问题
第一次创建了原型
socket单进程服务器 [root@localhost ~]# cat echoserver.py import socket host='192.168.10.101' port=9657 s=socket.socket() s.bind((host,port)) s.listen(1) conn,addr=s.accept() print 'connected by',addr while 1: data=conn.recv(20) if not data:break conn.sendall(data) s.close() python echoserver.py fgy@fgy-QTH6:~/Documents/python$ cat echoclient.py import socket host='192.168.10.101' port=9657 s=socket.socket() s.connect((host,port)) s.sendall('hello,world') data=s.recv(1024) print 'received',repr(data) s.close() python echoclient.py
第二次修改,加入了循环,并持续接收并返回数据
[root@mysql python]# cat echoserver.py import socket,time,os host='192.168.10.114' port=9657 s=socket.socket() s.bind((host,port)) s.listen(1) while 1: conn,addr=s.accept() while 1: print 'connected by',addr print time.ctime() data=conn.recv(1024) if not data:break result=os.popen(data).read() conn.sendall(result) s.close() fgy@fgy-QTH6:~/Documents/python$ cat echoclient.py import socket,time,os host='192.168.10.114' port=9657 s=socket.socket() s.connect((host,port)) while 1: cmd=raw_input('your cmd: ').strip() s.sendall(cmd) data=s.recv(1024) print time.ctime() print data # time.sleep(5) s.close()
os.system(df)不能保存,所以需要用下面的这个
aa=os.popen(df).read()
system(...)
system(command) -> exit_status
Execute the command (a string) in a subshell.
popen(...)
popen(command [, mode='r' [, bufsize]]) -> pipe
Open a pipe to/from a command returning a file object.
>>> ab=os.popen('free')
>>> ab.read()
' total used free shared buff/cache available\nMem: 3992388 1881176 579816 390708 1531396 1436396\nSwap: 3998716 0 3998716\n'
>>> os.system('free')
total used free shared buff/cache available
Mem: 3992388 1879964 581108 390580 1531316 1437732
Swap: 3998716 0 3998716
0
>>> ac=os.system('free')
total used free shared buff/cache available
Mem: 3992388 1880560 576808 394256 1535020 1433448
Swap: 3998716 0 3998716
第三次,客户端输空值,错误命令的解决办法
针对客户端空值问题
在客户端判断或者在服务器端判断 空值不发过去(在客户端判断),加入下面着色一行就行了
while 1:
cmd=raw_input('your cmd: ').strip()
if len(cmd) == 0:continue
s.sendall(cmd)
data=s.recv(1024)
错误命令问题分析
在server端,发现下面这个语句不好使
# result=os.popen(data).read()
所以就换成下面这个模块的一个方法,非常方便简单。
>>> import commands
>>> commands.getstatusoutput('df')
(0, 'Filesystem 1K-blocks Used Available Use% Mounted on\nudev 1976928 0 1976928 0% /dev\ntmpfs 399240 6432 392808 2% /run\n/dev/sda5 49082176 13552096 33013756 30% /\ntmpfs 1996192 24840 1971352 2% /dev/shm\ntmpfs 5120 4 5116 1% /run/lock\ntmpfs 1996192 0 1996192 0% /sys/fs/cgroup\n/dev/sda6 967320 56100 844868 7% /boot\ntmpfs 399240 80 399160 1% /run/user/1000')
>>> cm=commands.getstatusoutput('df')
>>> cm[0]
0
>>> cm[1]
'Filesystem 1K-blocks Used Available Use% Mounted on\nudev 1976928 0 1976928 0% /dev\ntmpfs 399240 6432 392808 2% /run\n/dev/sda5 49082176 13552096 33013756 30% /\ntmpfs 1996192 24840 1971352 2% /dev/shm\ntmpfs 5120 4 5116 1% /run/lock\ntmpfs 1996192 0 1996192 0% /sys/fs/cgroup\n/dev/sda6 967320 56100 844868 7% /boot\ntmpfs 399240 80 399160 1% /run/user/1000'
>>> cm=commands.getstatusoutput('dfef')
>>> cm[0]
32512
>>> cm[1]
'sh: 1: dfef: not found'
>>> status,result=commands.getstatusoutput('dfef')
>>> status
32512
>>> result
'sh: 1: dfef: not found'
>>>
针对错误命令问题,所以服务端修改为
data=conn.recv(1024)
if not data:break
# result=os.popen(data).read()
status,result=commands.getstatusoutput(data)
conn.sendall(result)
针对客户端如果创建文件或目录的话,客户端程序会死掉,所以服务端修改为
加入以下着色部分就可以了
if not data:break
# result=os.popen(data).read()
status,result=commands.getstatusoutput(data)
if len(result) != 0:
conn.sendall(result)
else:
conn.sendall('DONE')
commands.getstatusoutput(data)解决了一些问题,
但是不能执行诸如vmstat 1,top,等有持续输出的命令。
所以还需要考虑另外一些方法
最后成型的程序
fgy@fgy-QTH6:~/Documents/python$ cat echoclient.py import socket,time,os,commands host='192.168.10.114' port=9657 s=socket.socket() s.connect((host,port)) while 1: cmd=raw_input('your cmd: ').strip() if len(cmd) == 0:continue s.sendall(cmd) data=s.recv(1024) print time.ctime() print data # time.sleep(5) s.close() [root@mysql python]# cat echoserver.py import socket,time,os,commands host='192.168.10.114' port=9657 s=socket.socket() s.bind((host,port)) s.listen(1) while 1: conn,addr=s.accept() while 1: print 'connected by',addr print time.ctime() data=conn.recv(1024) if not data:break # result=os.popen(data).read() status,result=commands.getstatusoutput(data) if len(result) != 0: conn.sendall(result) else: conn.sendall('done') s.close()
第四次,循环不等于多线程,与并发也不是一回事,所以需要修改为同时支持多个连接
python有socket模块,还有一个SocketServer多线程模块,有了这个就不需要自己去管理多线程了 RequestHandler.setup() RequestHandler.handler() BaseServer.serve_forever(poll-interval=0.5) 重新改写的服务端,还用上面的客户端。但虽然可以允许多个连接过来,但只能交互一次,随后客户端就死掉了,不能循环交互
因为服务端只做了三件事,打印两行,再将数据以大写方式返回给客户端,只能进行一次
所以还需要自己再写循环来实现多次交互。
import SocketServer class Mytcp(SocketServer.BaseRequestHandler): def handle(self): self.data=self.request.recv(1024).strip() print "{} wrote:".format(self.client_address[0]) print self.data self.request.sendall(self.data.upper()) if __name__=="__main__": host,port="192.168.10.114",9657 server=SocketServer.ThreadingTCPServer((host,port),Mytcp) server.serve_forever() __main__的用途是 当自行调用时,即在命令行主动启动执行的话,才会去执行if下面的内容 如果被当作一个python模块导入的话,不会执行if里的内容。
所以重新修改一下服务端为下面的内容,如果不加if 语句的话,在客户端都退出后,服务器会死循环,搞死服务器。
def handle(self):
while 1:
self.data=self.request.recv(1024).strip()
print "{} wrote:".format(self.client_address[0])
print self.data
if not self.data:break
self.request.sendall(self.data.upper())
第五次修改,文件的传输
下面的客服程序实现了文件下载,与2个并发连接服务器,下载同一个大小为2G的文件,会造成下面的内存报错,并且报错的那条连接下载不成功,如果大小为1G的话,则成功。
原来报错与内存有关系,服务器内存为2G,增加到4G后,3个并发成功。
192.168.10.116 wrote:
get ub
192.168.10.115 wrote:
get ub
----------------------------------------
Exception happened during processing of request from ('192.168.10.115', 55444)
Traceback (most recent call last):
File "/usr/lib64/python2.7/SocketServer.py", line 593, in process_request_thread
self.finish_request(request, client_address)
File "/usr/lib64/python2.7/SocketServer.py", line 334, in finish_request
self.RequestHandlerClass(request, client_address, self)
File "/usr/lib64/python2.7/SocketServer.py", line 649, in __init__
self.handle()
File "sock.py", line 14, in handle
self.request.sendall(ff.read())
MemoryError
然后需要做的就是上传文件与认证
[root@mysql python]# cat sock.py import SocketServer,commands,time class Mytcp(SocketServer.BaseRequestHandler): def handle(self): while 1: self.data=self.request.recv(1024).strip() print "{} wrote:".format(self.client_address[0]) print self.data if not self.data: print "client is dead %s" %self.client_address[0] break input=self.data.split() if input[0] =='get': with open(input[1],'rb') as ff: self.request.sendall(ff.read()) time.sleep(2) self.request.send('file transfer finished') continue status,result=commands.getstatusoutput(self.data) if len(result) != 0: self.request.sendall(result) else: self.request.sendall('done') #self.request.sendall(self.data.upper()) if __name__=="__main__": host,port="192.168.10.114",9657 server=SocketServer.ThreadingTCPServer((host,port),Mytcp) server.serve_forever() fgy@fgy-QTH6:~/Documents/python/ff$ cat echoclient.py import socket,time,os,commands host='192.168.10.114' port=9657 s=socket.socket() s.connect((host,port)) while 1: cmd=raw_input('your cmd: ').strip() if len(cmd) == 0:continue s.sendall(cmd) if cmd.split()[0] == 'get': with open(cmd.split()[1],'wb') as w : while 1: data=s.recv(1024) if data=="file transfer finished":break w.write(data) continue else: print time.ctime() print s.recv(1024) s.close()