python编程16章_Python核心编程——Chapter16
好吧,在拜讀完《Python網絡編程基礎》之后,回頭再搞一搞16章的網絡編程吧。
Let‘s go!
16.4.修改書上示例的TCP和UDP客戶端,使得服務器的名字不要在代碼里寫死,要允許用戶指定一個主機名和端口。只有兩個值都沒有輸入是才使用默認值。
難度不大。
#tsTclnt.py#!/usr/bin/env python#-*-coding:utf-8-*-
from socket import *
importsysif(len(sys.argv) < 3):
HOST= 'localhost'PORT= 21567
else:
HOST= sys.argv[1]
PORT= int(sys.argv[2])
BUFSIZ= 1024ADDR=(HOST,PORT)
tcpCliSock=socket(AF_INET,SOCK_STREAM)
tcpCliSock.connect(ADDR)whileTrue:
data= raw_input('>')if notdata:breaktcpCliSock.send(data)
data=tcpCliSock.recv(BUFSIZ)if notdata:break
printdata
tcpCliSock.close()
#tsUclnt.py#!/usr/bin/env python#-*- coding:utf-8 -*-
from socket import *
importsysif(len(sys.argv) < 3):
HOST= 'localhost'PORT= 21567
else:
HOST= sys.argv[1]
PORT= sys.argv[2]#print sys.argv[1],sys.argv[2]
BUFSIZ= 1024ADDR=(HOST,int(PORT))
udpCliSock=socket(AF_INET,SOCK_DGRAM)whileTrue:
data= raw_input('>')if notdata:breakudpCliSock.sendto(data,ADDR)
data,ADDR=udpCliSock.recvfrom(BUFSIZ)if notdata:break
printdata
udpCliSock.close()
16.5.修改服務器示例代碼,使得它能根據不同的請求而作出應答。
這道題目本質上也不難,只需要在服務器的死循環程序加上條件判斷即可。
版本1:純TCP服務器端修改。
#!/usr/bin/env python#-*- coding:utf-8 -*-
from socket import *
importtime,os
HOST= ''PORT= 21567BUFSIZ= 1024ADDR=(HOST,PORT)
tcpSerSock=socket(AF_INET,SOCK_STREAM)
tcpSerSock.bind(ADDR)
tcpSerSock.listen(5)whileTrue:print 'waiting for connection...'tcpCliSock,addr=tcpSerSock.accept()print '...connected from:',addrwhileTrue:
data=tcpCliSock.recv(BUFSIZ)
data=data.strip()if notdata:break
if data == "date":
tcpCliSock.send('%s' %(time.ctime(time.time())))elif data == "os":
tcpCliSock.send('%s' %(os.name))elif data == "ls":
tcpCliSock.send('%s' %(os.listdir(os.curdir)))else:
tcpCliSock.send('[%s] %s' %(ctime(),data))
tcpCliSock.close()
tcpSerSock.close()
版本2:使用現成的服務器框架進行修改。
#!/usr/bin/env python#-*-coding:utf-8-*-
from SocketServer importThreadingMixIn,TCPServer,StreamRequestHandlerimporttime,os,reclassTxRequestHandler(StreamRequestHandler):defhandle(self):
req=self.rfile.readline().strip()
m= re.match('(\w+)\s+([\w+/]+)',req)if req == "date":
result=time.ctime(time.time())elif req == "os":
result=os.nameelif req == "ls":
result=str(os.listdir(os.curdir))elif m is not None and m.lastindex == 2 and m.group(1) == 'ls':
result= str(os.listdir(m.group(2)))else:
result= """date -- 服務器將返回它的當前時間
os -- 得到操作系統的信息
ls -- 得到當前目錄的文件列表"""self.wfile.write(result+"\n")classTxServer(ThreadingMixIn,TCPServer):
allow_reuse_address= 1serveraddr= ('',8765)
srvr=TxServer(serveraddr,TxRequestHandler)
srvr.serve_forever()
16.7.實現一個半雙工的聊天方式,其中一個人是服務端,另一個人是客戶端。
其實也不難,只是將回射服務器做部分修改就可以。
服務器端:
#!/usr/bin/env python#-*- coding:utf-8 -*-
from socket import *
from time importctime
HOST= ''PORT= 21567BUFSIZ= 1024ADDR=(HOST,PORT)
tcpSerSock=socket(AF_INET,SOCK_STREAM)
tcpSerSock.bind(ADDR)
tcpSerSock.listen(5)whileTrue:print 'waiting for connection...'tcpCliSock,addr=tcpSerSock.accept()print '...connected from:',addrwhileTrue:
rdata=tcpCliSock.recv(BUFSIZ)if notrdata:break
printrdata
sdata= raw_input(">")
tcpCliSock.send('[%s] %s' %(ctime(),sdata))
tcpCliSock.close()
tcpSerSock.close()
客戶端:
#!/usr/bin/env python#-*-coding:utf-8-*-
from socket import *
importsysif(len(sys.argv) < 3):
HOST= 'localhost'PORT= 21567
else:
HOST= sys.argv[1]
PORT= int(sys.argv[2])
BUFSIZ= 1024ADDR=(HOST,PORT)
tcpCliSock=socket(AF_INET,SOCK_STREAM)
tcpCliSock.connect(ADDR)whileTrue:
data= raw_input('>')if notdata:breaktcpCliSock.send(data)
data=tcpCliSock.recv(BUFSIZ)if notdata:break
printdata
tcpCliSock.close()
16.8.實現一個全雙工的聊天方式,即兩個人可以獨立地發送和接收消息。
全雙工其實原理也不太難,但是我一直傻逼了,一直用寫服務器端的方法去實現客戶端的程序。
我所使用的方法也是最常用的技術——多線程。以下是服務器端和客戶端的編寫思路:
服務器端:在服務器端我主要用了一個主線程來accept客戶端的鏈接,沒接受了一個連接,就新建兩個線程,一個用于發送消息,一個用于接收消息。
客戶端:思路完全不一樣,由于客戶端面對的只有服務器,所以主線程只能是接收消息(發送消息),然后發送消息(接收消息)作為一個線程來并發。
由于這里的數據并不是共享的,所以這里的線程寫起來就很輕松,不用考慮同步問題。
服務器端:
#!/usr/bin/env python#-*- coding:utf-8 -*-
importsocket,traceback,osfrom threading import *host= ''port= 51423 #監聽所有的接口
#接受消息的線程
defhandlerecv(clientsock):print "New child",currentThread().getName()print "Got connection from",clientsock.getpeername()whileTrue:
data= clientsock.recv(4096)if notlen(data):break
printdata
clientsock.close()#發送消息的線程
defhandlesend(clientsock):whileTrue:
data= raw_input(">")
data= data + "\n"; #加上換行,好看一點。
clientsock.sendall(data)#關閉連接
clientsock.close()#建立套接字
s =socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
s.bind((host,port))
s.listen(1)whileTrue:try:
clientsock,clientaddr=s.accept()exceptKeyboardInterrupt:raise
except:
traceback.print_exc()continuet= Thread(target = handlerecv,args=[clientsock])
t.setDaemon(1)
t.start()
r= Thread(target = handlesend,args=[clientsock])
r.setDaemon(1)
r.start()
客戶端:
#!/usr/bin/env python#-*-coding:utf-8-*-
from socket import *
importsysfrom threading import *
if(len(sys.argv) < 3):
HOST= 'localhost'PORT= 51423
else:
HOST= sys.argv[1]
PORT= int(sys.argv[2])
BUFSIZ= 1024ADDR=(HOST,PORT)defhandlesend(tcpCliSock):whileTrue:
sdata= raw_input('>')if notsdata:breaktcpCliSock.send(sdata)
tcpCliSock.close()
tcpCliSock=socket(AF_INET,SOCK_STREAM)
tcpCliSock.connect(ADDR)#建立發送消息的線程
s = Thread(target = handlesend,args=[tcpCliSock])
s.setDaemon(1)
s.start()whileTrue:
rdata=tcpCliSock.recv(BUFSIZ)if notrdata:break
printrdata
tcpCliSock.close()
16.10.實現一個多房間全雙工的聊天方式,把聊天服務器改成支持多用戶版本。
其實思路差不多,只不過過程有點繁瑣,在服務器端要建立字典,作為房間,然后每個套接字存在字典當中,然后作適當的分發。
另外一種方法使用廣播不知道可不可以了。
這里直接貼人家寫的代碼學習一下:
服務器端:
#!/usr/bin/env python#_*_ coding: utf8 _*_
from socket import *
from time importctimeimportthreadingfrom string importsplit
HOST= ''PORT= 21567BUFSIZE= 1024ADDR=(HOST, PORT)defDeal(sck, username, room):whileTrue:
data=sck.recv(BUFSIZE)for i inclients[room].iterkeys():if i <>username:if data <> "quit":
clients[room][i].send("[%s] %s: %s" %(ctime(), username, data))else:
clients[room][i].send("用戶%s在%s退出房間%s" %(username, ctime(), room ))if data == "quit":delclients[room][username]
sck.send(data)
sck.close()breakchatSerSock=socket(AF_INET, SOCK_STREAM)
chatSerSock.bind(ADDR)
chatSerSock.listen(5)
clients= {"":{},}whileTrue:print 'waiting for connection...'chatCliSock, addr=chatSerSock.accept()print "...connected romt:", addr
data=chatCliSock.recv(BUFSIZE)
username, room=split(data)printusernameif notclients.has_key(room):
clients[room]={}ifclients[room].has_key(username):
chatCliSock.send("reuse")
chatCliSock.close()else:
chatCliSock.send("success")
clients[room][username]=chatCliSock
t= threading.Thread(target=Deal, args=(chatCliSock, username, room))
t.start()
chatSerSock.close()
客戶端:
#!/usr/bin/env python#_*_ coding: utf8 _*_
from socket import *
from time importctimeimportthreadingimportrandomfrom sys importargv, exit, stdoutfrom getopt importgnu_getopt, GetoptError
help_info= ["cs.py [ -h | --help | -u | --username] username","\t-h or --help\t顯示幫助信息","\t-u or --username\指定用戶名","\t-r or --room\t指定房間"]defhelp():for i inhelp_info:printidefSend(sck, test):whileTrue:
data= raw_input('>')
sck.send(data)if data == "quit":break
defRecieve(sck, test):whileTrue:
data=sck.recv(BUFSIZ)if data == "quit":
sck.close()breakstr= "\n" + data + "\n>"stdout.write(str)
HOST= 'localhost'PORT= 21567BUFSIZ= 1024ADDR=(HOST, PORT)
threads=[]if __name__ == "__main__":#解析命令行參數
try:
opts, args= gnu_getopt(argv[1:], "hu:r:", ["help", "username=", "room="])exceptGetoptError, err:printstr(err)
help()
exit(2)
username= ""room= ""
for o, a inopts:if o in ("-h", "--help"):
help()
exit(0)elif o in ("-u", "--username"):
username=aelif o in ("-r", "--room"):
room=aelse:print "未知選項"help()
exit(2)if not username or notroom:
help()
exit(2)
chatCliSock=socket(AF_INET, SOCK_STREAM)
chatCliSock.connect(ADDR)
chatCliSock.send("%s %s" %(username, room))
data=chatCliSock.recv(BUFSIZ)if data == "reuse":print "用戶%s已登錄房間%s" %(username, room)
raw_input()
exit(1)elif data == "success":print "用戶%s成功登錄房間%s" %(username, room)
t= threading.Thread(target=Send, args =(chatCliSock, None))
threads.append(t)
t= threading.Thread(target=Recieve, args =(chatCliSock, None))
threads.append(t)for i inrange(len(threads)):
threads[i].start()
threads[0].join()
16.11.寫一個網頁客戶端。自己新建一個套接字,然后發送http請求。
感覺難度不大。
#!/usr/bin/env python#-*-coding:utf-8-*-
importsocket,sys
website= sys.argv[1]
port= 80
printwebsiteprint "Creating socket...",
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)print "done."
print "Connecting to ...",
s.connect((website,port))print "done."s.send("GET /index.html HTTP/1.0\r\n\r\n")
data= s.recv(4096)print data
16-12.休眠服務器,在服務器端上面加個sleep(num)就好,在這里就不貼了。
16-13.功能太過復雜,本人渣渣,沒精力寫。
16-14.考慮的東西很多,水平有限,不做了。
16-15.異步和SocketServer。這個用多線程可以實現,在《python網絡編程基礎》上面直接有源碼。使用方法都有教。
#!/usr/bin/env python#-*- coding:utf-8 -*-
from SocketServer importThreadingMixIn,TCPServer,StreamRequestHandlerimporttimeclassTimeRequestHandler(StreamRequestHandler):defhandle(self):
req=self.rfile.readline().strip()if req == "asctime":
result=time.asctime()elif req == "seconds":
result=str(int(time.time()))elif req == "rfc822":
result= time.strftime("%a, %d %b %Y %H:%M:%S +0000",time.gmtime())else:
result= """Unhandled request.Send a line with one of the following words:
asctime -- for human-readable time
seconds -- seconds since the Unix Epoch
rfc822 -- data/time in format used for mail and news posts"""self.wfile.write(result+ "\n")classTimeServer(ThreadingMixIn,TCPServer):
allow_reuse_address= 1serveraddr= ('',8765)
srvr=TimeServer(serveraddr,TimeRequestHandler)
srvr.serve_forever()
多多指教!謝謝!
總結
以上是生活随笔為你收集整理的python编程16章_Python核心编程——Chapter16的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python入门之控制结构顺序与选择结构
- 下一篇: java ee 期末考试_JAVA EE