Socket 編程html
Socket通信原理描述:python
套接字是爲特定網絡協議(例如TCP/IP,ICMP/IP,UDP/IP等)套件對上的網絡應用程序提供者提供當前可移植標準的對象。它們容許程序接受並進行鏈接,如發送和接受數據。爲了創建通訊通道,網絡通訊的每一個端點擁有一個套接字對象極爲重要。和大多數語言同樣,Python 支持面向鏈接和無鏈接,實現接口功能與步驟也大體相同。react
面向鏈接即須要先鏈接而後通信, 面向鏈接主要協議就是傳輸控制協議(tcp),要建立tcp套接字時須要指定套接字類型爲 SOCK_STRAM,表達了他做爲流套接字的特色。linux
無鏈接,顧名思義無需創建鏈接就能夠進行通信,這時數據到達順序、可靠性就沒法保證了。實現這種鏈接的協議就是用戶數據包協議(udp)。建立UDP時須要指定套接字類型爲 SOCK_DGRAM。web
服務器端一般是用來處理數據的,客戶端是用來請求數據的。服務端能夠同時處理多條數據。編程
同一個80端口windows
TCP服務器端:服務器
1. 第一步是建立socket對象。調用socket構造函數。如:網絡
socket = socket.socket( family, type )多線程
family參數表明地址家族,可爲AF_INET或AF_UNIX。AF_INET家族包括Internet地址,AF_UNIX家族用於同一臺機器上的進程間通訊。
type參數表明套接字類型,可爲SOCK_STREAM(流套接字)和SOCK_DGRAM(數據報套接字)。
2. 第二步是將socket綁定到指定地址。這是經過socket對象的bind方法來實現的:
socket.bind( address )
由AF_INET所建立的套接字,address地址必須是一個雙元素元組,格式是(host,port)。host表明主機,port表明端口號。若是端口號正在使用、主機名不正確或端口已被保留,bind方法將引起socket.error異常。
3. 第三步是使用socket套接字的listen方法接收鏈接請求。
socket.listen( backlog )
backlog指定最多容許多少個客戶鏈接到服務器。它的值至少爲1。收到鏈接請求後,這些請求須要排隊,若是隊列滿,就拒絕請求。
4. 第四步是服務器套接字經過socket的accept方法等待客戶請求一個鏈接。
connection, address = socket.accept()
調 用accept方法時,socket會時入「waiting」狀態。客戶請求鏈接時,方法創建鏈接並返回服務器。accept方法返回一個含有兩個元素的 元組(connection,address)。第一個元素connection是新的socket對象,服務器必須經過它與客戶通訊;第二個元素 address是客戶的Internet地址。
5. 第五步是處理階段,服務器和客戶端經過send和recv方法通訊(傳輸 數據)。服務器調用send,並採用字符串形式向客戶發送信息。send方法返回已發送的字符個數。服務器使用recv方法從客戶接收信息。調用recv 時,服務器必須指定一個整數,它對應於可經過本次方法調用來接收的最大數據量。recv方法在接收數據時會進入「blocked」狀態,最後返回一個字符 串,用它表示收到的數據。若是發送的數據量超過了recv所容許的,數據會被截短。多餘的數據將緩衝於接收端。之後調用recv時,多餘的數據會從緩衝區 刪除(以及自上次調用recv以來,客戶可能發送的其它任何數據)。
6. 傳輸結束,服務器調用socket的close方法關閉鏈接
僞代碼大體以下:
1 建立套接字,綁定套接字到當地地址,而後開始監聽鏈接。就是socket,bind,listen。
2 進入循環,不斷接受客戶端的鏈接請求,而後接收傳來的數據,固然也能夠發送給對方數據。就是accept一個鏈接,而後recv數據。
3 接收完畢能夠關閉套接字,close。
ss.socket(Socket.AF_INET,Socket.SOCK_STRAM) #建立服務器套接字
ss.bind() #把本地地址綁到套接字上
ss.listen() #監聽鏈接
inf_loop: #服務器無限循環
cs=ss.accept() #接受客戶端的鏈接
comm._loop: #通訊循環
cs.recv()/cs.send() #對話
cs.close() #關閉客戶套接字
ss.close() #關閉服務器套接字
TCP客戶端:
1. 第一步是建立一個socket以鏈接服務器:socket = socket.socket( family, type )
2. 第二步是使用socket的connect方法鏈接服務器。對於AF_INET家族,鏈接格式以下:
socket.connect( (host,port) )
host表明服務器主機名或IP,port表明服務器進程所綁定的端口號。如鏈接成功,客戶就可經過套接字與服務器通訊,若是鏈接失敗,會引起socket.error異常。
3. 第三步是處理階段,客戶和服務器將經過send方法和recv方法通訊。
4. 傳輸結束,客戶經過調用socket的close方法關閉鏈接。
僞代碼以下:
1 建立套接字,而後鏈接遠端地址,socket ,connect。
2 創建鏈接以後開始發送數據。Send(data),固然能夠從緩衝區讀取服務器發來的數據。Recv(BUFF)
3 完畢後,關閉套接字。Close
cs=socket(Socket.AF_INET,Socket.SOCK_DGRAM)
#建立客戶套接字
cs.connect() #嘗試鏈接服務器
comm._loop: #通訊循環
cs.send()/cs.recv() #對話
cs.close() #關閉套接字
簡單的server 端和客戶端(tcp協議)
服務端
import socket #socket模塊
import commands #執行系統命令模塊
HOST='127.0.0.1'
PORT=8085
s= socket.socket(socket.AF_INET,socket.SOCK_STREAM) #定義socket類型,網絡通訊,TCP協議,前面是ipv4協議,後面的tcp協議。
s.bind((HOST,PORT)) #套接字綁定的IP與端口,host用0.0.0.1就會包含本機的全部ip
s.listen(1) #開始TCP監聽,listen中的參數表示能夠多少客戶端來進行鏈接
while 1:
conn,addr=s.accept() #接受TCP鏈接,並返回新的套接字與IP地址
print'Connected by',addr #輸出客戶端的IP地址
while 1:
data=conn.recv(1024) #把接收的數據實例化
print data
conn.sendall('got message from server:'+data.upper())
conn.close() #關閉鏈接
客戶端:
import socket
HOST='127.0.0.1'
PORT=8085
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #定義socket類型,網絡通訊,TCP
s.connect((HOST,PORT)) #要鏈接的IP與端口
while 1:
cmd=raw_input("Please input cmd:") #與人交互,輸入命令
s.sendall(cmd) #把命令發送給對端
data=s.recv(1024) #把接收的數據定義爲變量
print data #輸出變量
s.close() #關閉鏈接
變形:把上面的代碼改爲只能發3次
netstat -ano|findstr "1104" 查看windows上某個端口是否被佔用
netstat -anp|grep 8080 linux使用的
服務端:
import socket #socket模塊
import commands #執行系統命令模塊
HOST='127.0.0.1'
PORT=8085
s= socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#定義socket類型,網絡通訊,TCP
s.bind((HOST,PORT)) #套接字綁定的IP與端口
s.listen(5)
#開始TCP監聽,listen中的參數表示能夠多少客戶端來進行鏈接
while 1:
conn,addr=s.accept() #接受TCP鏈接,並返回新的套接字與IP地址
print'Connected by',addr #輸出客戶端的IP地址
while 1:
try:
data=conn.recv(1024) #把接收的數據實例化
print data
conn.sendall('got message from server:'+data.upper())
except Exception:
conn.close() #關閉鏈接
break
客戶端:
import socket
HOST='127.0.0.1'
PORT=8085
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#定義socket類型,網絡通訊,TCP
s.connect((HOST,PORT)) #要鏈接的IP與端口
times=3
while times>0:
cmd=raw_input("Please input cmd:") #與人交互,輸入命令
s.sendall(cmd) #把命令發送給對端
data=s.recv(1024) #把接收的數據定義爲變量
print data #輸出變量
times-=1
s.close() #關閉鏈接
稍微複雜一點的客戶端例子
服務端:
import time
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 8009))
sock.listen(5)
while True:
time.sleep(0.1)
connection,address = sock.accept()
while 1:
try:
connection.settimeout(5)
buf = connection.recv(1024)
print "got message from client:",buf
if buf == '1':
print "1"
connection.send('welcome to server!')
elif buf == '2':
connection.send('please go out!')
elif buf == "close":
connection.send('bye!')
connection.close()
break
except socket.timeout:
print 'time out'
connection.close()
break
except Exception,e:
print e
connection.close()
break
客戶端:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 8009))
import time
time.sleep(2)
sock.send('2')
print sock.recv(1023)
sock.send('1')
print sock.recv(1024)
sock.send('close')
print sock.recv(1024)
print "Done!"
sock.close()
變形練習:服務端把客戶端傳來的消息所有存入文件中,而後給客戶端回傳一句話。
本身:
服務端:
import time
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 10015))
sock.listen(5)
while True:
time.sleep(0.1)
connection,address = sock.accept()
while 1:
try:
connection.settimeout(5)
buf = connection.recv(1024)
print "got message from client:",buf
fp = open("e:\\test1.txt", "a")
if buf == "close":
connection.send('bye!')
connection.close()
break
else:
fp.write(buf+"\n")
#fp.write("\n")
fp.close()
except socket.timeout:
print 'time out'
connection.close()
break
except Exception,e:
print e
connection.close()
break
客戶端:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 10015))
import time
time.sleep(2)
sock.send('2')
sock.send('1')
sock.send('close')
print "Done!"
sock.close()
老師:
>>> import traceback
>>> try:
... 1/0
... except:
... print "error"
...
error
>>> try:
... 1/0
... except Exception,e:
... print e
...
integer division or modulo by zero
>>> try:
... 1/0
... except:
... traceback.print_exc()
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero
服務端:
# encoding=utf-8
import time
import random
import traceback
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 8009))
sock.listen(5)
AI_robot =[u'吃了麼?',u"最近漲工資了麼?",u"記得找對象",u"記得減肥"]
while True:
time.sleep(0.1)
connection,address = sock.accept()
while 1:
try:
connection.settimeout(5)
buf = connection.recv(1024)
fp=open("e:\\chat_record.txt","a")
print "got message from client:",buf.decode("utf-8")
if buf == "close":
connection.send('bye!')
connection.close()
break
elif buf=='q':
connection.close()
break
else:
fp.write(buf+"\n")
connection.send(str(time.ctime())+AI_robot[random.randint(0,3)].encode("utf-8"))
fp.close()
except socket.timeout:
print 'time out'
traceback.print_exc()
connection.close()
break
except Exception,e:
traceback.print_exc()
print e
connection.close()
break
客戶端:
# encoding=utf-8
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 8009))
import time
while 1:
try:
data=raw_input("say to server:")
sock.send(data.decode("gbk").encode("utf-8"))
print sock.recv(1023).decode("utf-8")
if data == "q":
sock.close()
break
except:
print "exception"
print "Done!"
實現一個相似ssh的功能(僅支持Linux操做系統)
ip:39.106.41.11
username:student
password:gloryroad987!
服務器端:
import commands
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 8008))
sock.listen(5)
while True:
connection,address = sock.accept()
try:
connection.settimeout(5)
command = connection.recv(1024)
print command
code,result=commands.getstatusoutput(command)
print code
print result
except socket.timeout:
print 'time out'
connection.close()
客戶端:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 8008))
import time
time.sleep(2)
sock.send('ls -al /home/wxh')
print sock.recv(1024)
sock.close()
服務器端:
import commands
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('localhost', 8001))
sock.listen(5)
while True:
connection,address = sock.accept()
try:
connection.settimeout(5)
command = connection.recv(1024)
print command
code,result=commands.getstatusoutput(command)
print code
print result
connection.send(str(code))
connection.send(result)
except socket.timeout:
print 'time out'
connection.close()
客戶端:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 8001))
import time
time.sleep(2)
sock.send('ls -al /home/wxh')
print "command status:",sock.recv(1)
print "command result:",sock.recv(1024)
sock.close()
使用Socket 傳送一個文件
服務器端
#-*- coding: UTF-8 -*-
import socket,time,SocketServer,struct,os,thread
host='127.0.0.1'
port=12307
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #定義socket類型
s.bind((host,port)) #綁定須要監聽的Ip和端口號,tuple格式
s.listen(1)
def conn_thread(connection,address):
while True:
try:
connection.settimeout(600) #設置鏈接時間
fileinfo_size=struct.calcsize('128sl') #文件信息
buf = connection.recv(fileinfo_size) #接收此文件的信息在buf裏面
if buf: #若是不加這個if,第一個文件傳輸完成後會自動走到下一句,須要拿到文件大小信息才能夠繼續執行
filename,filesize =struct.unpack('128sl',buf) #解包,解開後是兩個值
filename_f = filename.strip('\00') #解出文件名,去掉C語言中的字符串\00結束
filenewname = os.path.join('e:\\',('new_'+ filename_f))
print 'file new name is %s, filesize is %s' %(filenewname,filesize)
recvd_size = 0 #定義接收了的文件大小
file = open(filenewname,'wb')
print 'stat receiving...'
while not recvd_size == filesize: #判斷收到的和原有文件是否是同樣大,不同繼續接收
if filesize - recvd_size > 1024:
rdata = connection.recv(1024)
recvd_size += len(rdata)
else:
rdata = connection.recv(filesize - recvd_size) #接收到的文件和告知的同樣大了,中止接收,斷定爲接收完畢
recvd_size = filesize
file.write(rdata)
file.close()
print 'receive done'
#connection.close()
except socket.timeout:
connection.close()
while True:
connection,address=s.accept()
print('Connected by ',address)
#thread = threading.Thread(target=conn_thread,args=(connection,address)) #使用threading也能夠
#thread.start()
thread.start_new_thread(conn_thread,(connection,address))
s.close()
客戶端
#-*- coding: UTF-8 -*-
import socket,os,struct
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('127.0.0.1',12307))
while True:
filepath = raw_input('Please Enter chars:\r\n')
if os.path.isfile(filepath):
fileinfo_size=struct.calcsize('128sl') #定義打包規則
#定義文件頭信息,包含文件名和文件大小
fhead = struct.pack('128sl',os.path.basename(filepath),os.stat(filepath).st_size)
s.send(fhead)
print 'client filepath: ',filepath
# with open(filepath,'rb') as fo: 這樣發送文件有問題,發送完成後還會發一些東西過去
fo = open(filepath,'rb')
while True:
filedata = fo.read(1024)
if not filedata:
break
s.send(filedata)
fo.close()
print 'send over...'
#s.close()
SocketServer模塊的Fork方式(進程模式)linux 下執行
Server 端代碼
#!/usr/bin/python
#encoding=utf-8
from SocketServer import TCPServer, ForkingMixIn, StreamRequestHandler
import time
class Server(ForkingMixIn, TCPServer): #自定義Server類 ,以進程模式啓動
pass
class MyHandler(StreamRequestHandler):
def handle(self): #重載handle函數
addr = self.request.getpeername()
print 'Get connection from', addr #打印客戶端地址
data= self.rfile.readline().strip() #客戶端發送的信息必須帶有回車,不然會一直等待客戶端繼續發送數據
print data
time.sleep(1) #休眠5秒鐘
if data:
self.wfile.write('This is a ForkingMixIn tcp socket server') #給客戶端發送信息
host = '127.0.0.1'
port = 8009
server = Server((host, port), MyHandler)
server.serve_forever() #開始偵聽並處理鏈接
客戶端:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 8009))
import time
time.sleep(2)
sock.send('ls -al /home/wxh'+"\n")
print sock.recv(1024)
sock.close()
註釋:
多個鏈接同時到達服務器端的時候,每一個鏈接主進程都生成一個子進程專門用來處理此鏈接,而主進程則依舊保持在偵聽狀態。因主進程和子進程是同時進行的,因此不會阻塞新的鏈接。但因爲生成進程消耗的資源比較大,這種處理方式在有不少鏈接的時候會帶來性能問題。Server類須繼承ForkingMixIn和TCPServer兩個類。
SocketServer模塊的Fork方式(線程模式)linux 下執行
服務器端代碼:
#!/usr/bin/python
#encoding=utf-8
from SocketServer import TCPServer, ThreadingMixIn, StreamRequestHandler
import time
class Server(ThreadingMixIn, TCPServer): #自定義Server類 ,以線程方式啓動
pass
class MyHandler(StreamRequestHandler):
def handle(self): #重載handle函數
addr = self.request.getpeername()
print 'Get connection from', addr #打印客戶端地址
data= self.rfile.readline().strip() #客戶端發送的信息必須帶有回車,不然會一直等待客戶端繼續發送數據
print data
time.sleep(1) #休眠5秒鐘
if data:
self.wfile.write('This is a ForkingMixIn tcp socket server') #給客戶端發送信息
host = '127.0.0.1'
port = 8001
server = Server((host, port), MyHandler)
server.serve_forever() #開始偵聽並處理鏈接
線程是一種輕量級的進程,比Fork消耗的資源更少,並且主線程和子線程之間具備相同的地址空間,處理效率高。但大量的使用線程會帶來線程之間的數據同步問題,處理很差可能使服務程序失去響應。上述與Fork方式中代碼基本相同,僅僅是採用的ThreadingMixIn類不一樣。
SocketServer模塊的Threading線程池方式
import SocketServer
import threading
class MyTCPHandler(SocketServer.BaseRequestHandler):
def handle(self):
while True:
self.data = self.request.recv(1024).strip()
cur_thread = threading.current_thread()
print cur_thread
if not self.data:
print "client:%s leave!" % self.client_address[0]
break
print "%s wrote:%s" % (self.client_address[0], self.data)
self.request.sendall(self.data.upper())
if __name__ == "__main__":
HOST, PORT = "127.0.0.1",8044
server = SocketServer.ThreadingTCPServer((HOST, PORT), MyTCPHandler)
server.serve_forever()
if __name__ == "__main__":
HOST, PORT = "127.0.0.1",8001
server = SocketServer.ThreadingTCPServer((HOST, PORT), MyTCPHandler)
server.serve_forever()
#服務器能夠自動判斷客戶端是否還會發送數據
客戶端程序:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 8044))
import time
time.sleep(2)
sock.send('ls -al /home/wxh'+"\n")
print sock.recv(1024)
sock.close()
使用socketserver 傳送一個文件
服務端:
#-*- coding: UTF-8 -*-
import socket,time,SocketServer,struct,os
host='127.0.0.1'
port=12307
ADDR=(host,port)
class MyRequestHandler(SocketServer.BaseRequestHandler):
def handle(self):
print('connected from:', self.client_address)
while True:
fileinfo_size=struct.calcsize('128sl') #定義文件信息。128s表示文件名爲128bytes長,l表示一個int或log文件類型,在此爲文件大小
self.buf = self.request.recv(fileinfo_size)
if self.buf: #若是不加這個if,第一個文件傳輸完成後會自動走到下一句
self.filename,self.filesize =struct.unpack('128sl',self.buf) #根據128sl解包文件信息,與client端的打包規則相同
print 'filesize is: ',self.filesize,'filename size is: ',len(self.filename) #文件名長度爲128,大於文件名實際長度
self.filenewname = os.path.join('e:\\',('new_'+ self.filename).strip('\00')) #使用strip()刪除打包時附加的多餘空字符
print self.filenewname,type(self.filenewname)
recvd_size = 0 #定義接收了的文件大小
file = open(self.filenewname,'wb')
print 'stat receiving...'
while not recvd_size == self.filesize:
if self.filesize - recvd_size > 1024:
rdata = self.request.recv(1024)
recvd_size += len(rdata)
else:
rdata = self.request.recv(self.filesize - recvd_size)
recvd_size = self.filesize
file.write(rdata)
file.close()
print 'receive done'
#self.request.close()
tcpServ = SocketServer.ThreadingTCPServer(ADDR, MyRequestHandler)
print('waiting for connection...' )
tcpServ.serve_forever()
客戶端:
#-*- coding: UTF-8 -*-
import socket,os,struct
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
s.connect(('127.0.0.1',12307))
while True:
filepath = raw_input('Please Enter chars:\r\n')
if os.path.isfile(filepath):
fileinfo_size=struct.calcsize('128sl') #定義打包規則
#定義文件頭信息,包含文件名和文件大小
fhead = struct.pack('128sl',os.path.basename(filepath),os.stat(filepath).st_size)
s.send(fhead)
print 'client filepath: ',filepath
# with open(filepath,'rb') as fo: 這樣發送文件有問題,發送完成後還會發一些東西過去
fo = open(filepath,'rb')
while True:
filedata = fo.read(1024)
if not filedata:
break
s.send(filedata)
fo.close()
print 'send over...'
#s.close()
Select :
I/O多路複用是在單線程模式下實現多線程的效果,實現一個多I/O併發的效果。
進程指定內核監聽哪些文件描述符(最多監聽1024個fd)的哪些事件,當沒有文件描述符事件發生時,進程被阻塞;當一個或者多個文件描述符事件發生時,進程被喚醒。
當咱們調用select()時:
1、上下文切換轉換爲內核態
2、將fd從用戶空間複製到內核空間
3、內核遍歷全部fd,查看其對應事件是否發生
4、若是沒發生,將進程阻塞,當設備驅動產生中斷或者timeout時間後,將進程喚醒,再次進行遍歷
5、返回遍歷後的fd
6、將fd從內核空間複製到用戶空間
一個任務:
1 www.sohu.com
2 www.sogou.com
3 www.gloryroad.cn
4 www.baidu.com
單進程或者單線程:
1->2->3-4
多進程或者多線程:
一個線程、一個進程:去執行一個訪問
select:
1 個線程:
1 www.sohu.com
5秒
2 www.sogou.com
4秒
3 www.gloryroad.cn
3秒
4 www.baidu.com
2秒 select不會等,一直髮請求,哪一個先返回用哪一個。
fd:file descriptor 文件描述符
fd_r_list, fd_w_list, fd_e_list = select.select(rlist, wlist, xlist, [timeout])
參數: 可接受四個參數(前三個必須)
rlist: wait until ready for reading
wlist: wait until ready for writing(通常不使用)
xlist: wait for an 「exceptional condition」
timeout: 超時時間
Server端:
import socket
import select
s = socket.socket()
s.bind(('127.0.0.1',8888))
s.listen(5)
r_list = [s,]
num = 0
while True:
rl, wl, error = select.select(r_list,[],[],10) #r_list讀的句柄,[][]後面兩個是異常的句柄,10是超時
執行過程:
#第一次執行循環體:客戶端創建的鏈接的時候,rl和r_list分別是[s,]和[s,]
# 執行鏈接以後,r_list變爲了[s,conn]
#第二次執行循環體:rl和r_list分別是[conn,]和[s,conn]
num+=1
print('counts is %s'%num)
print("rl's length is %s"%len(rl))
for fd in rl:
if fd == s:
conn, addr = fd.accept()
r_list.append(conn)
msg = conn.recv(200)
conn.sendall(('first----%s'%conn.fileno()).encode())
else:
try:
msg = fd.recv(200)
fd.sendall('second'.encode())
except ConnectionAbortedError:
r_list.remove(fd)
s.close()
客戶端:
import socket
flag = 1
s = socket.socket()
s.connect(('127.0.0.1',8888))
while flag:
input_msg = input('input>>>')
if input_msg == '0':
break
s.sendall(input_msg.encode())
msg = s.recv(1024)
print(msg.decode())
s.close()
#注意客戶端輸入的數據須要外帶引號,例如:’hi’
Twisted 框架
服務器端:
# coding=utf-8
from twisted.internet.protocol import Protocol
from twisted.internet.protocol import Factory
from twisted.internet.endpoints import TCP4ServerEndpoint
from twisted.internet import reactor
clients = []
class Spreader(Protocol):
def __init__(self, factory):
self.factory = factory
def connectionMade(self):
self.factory.numProtocols = self.factory.numProtocols + 1
self.transport.write(
"歡迎來到Spread Site, 你是第%s個客戶端用戶!\n" % (self.factory.numProtocols)
)
print "new connect: %d" % (self.factory.numProtocols)
clients.append(self)
def connectionLost(self, reason):
self.factory.numProtocols = self.factory.numProtocols - 1
clients.remove(self)
print "lost connect: %d" % (self.factory.numProtocols)
def dataReceived(self, data):
print "received data:",data
self.transport.write(data)
class SpreadFactory(Factory):
def __init__(self):
self.numProtocols = 0
def buildProtocol(self, addr):
return Spreader(self)
endpoint = TCP4ServerEndpoint(reactor, 8007)
endpoint.listen(SpreadFactory())
reactor.run()
客戶端:
# coding=utf-8
from twisted.internet.protocol import Protocol, ClientFactory
from twisted.internet import reactor
import threading
import time
import sys
import datetime
class Echo(Protocol):
def __init__(self):
self.connected = False
def connectionMade(self):
self.connected = True
def connectionLost(self, reason):
self.connected = False
def dataReceived(self, data):
print "data from server:",data.decode("utf-8")
class EchoClientFactory(ClientFactory):
def __init__(self):
self.protocol = None
def startedConnecting(self, connector):
print "Start to Connect..."
def buildProtocol(self, addr):
print "Connected..."
self.protocol = Echo()
return self.protocol
def clientConnectionLost(self, connector, reason):
print "Lost connection. Reason: ", reason
def clientConnectionFailed(self, connector, reason):
print "Connection is failed, Reason: ", reason
bStop = False
def routine(factory):
while not bStop:
if factory.protocol and factory.protocol.connected:
factory.protocol.transport.write("hello, I'm %s %s" % (
sys.argv[0], datetime.datetime.now()
))
print sys.argv[0], datetime.datetime.now()
time.sleep(5)
host = '127.0.0.1'
port = 8007
factory = EchoClientFactory()
reactor.connectTCP(host, port, factory)
threading.Thread(target=routine, args=(factory,)).start()
reactor.run()
bStop = True
——————————————————————————————————————
#!/usr/bin/python
#encoding=utf-8
from twisted.internet import reactor
from twisted.internet.protocol import Protocol, Factory
class EchoServer(Protocol):
def connectionMade(self): #鏈接創建的時候
print 'Get connection from', self.transport.client
self.factory.numProtocols = self.factory.numProtocols+1
if self.factory.numProtocols > 2: #當鏈接超過2個的時候,斷開鏈接
self.transport.write("Too many connections, try later\n")
self.transport.loseConnection()
return
print 'Get connection from', self.transport.client
def connectionLost(self, reason): #斷開鏈接
self.factory.numProtocols = self.factory.numProtocols-1
def dataReceived (self, data): #將收到的數據返回給客戶端
self.transport.write(data)
print data
self.transport.loseConnection() #收到數據後就斷開
factory = Factory()
factory.numProtocols = 0
factory.protocol = EchoServer
port = 1200
reactor.listenTCP(port, factory)
reactor.run() #進入循環
客戶端程序:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 8001))
import time
time.sleep(2)
sock.send('ls -al /home/wxh'+"\n")
print sock.recv(1024)
sock.close()
twisted 官網的例子,保存爲echoServ.py:
#!/usr/bin/env python
# coding: utf-8
from twisted.internet.protocol import Protocol
from twisted.internet.protocol import Factory
from twisted.internet import reactor
class Echo(Protocol):
'''協議類實現用戶的服務協議,例如 http,ftp,ssh 等'''
def __init__(self, factory):
self.factory = factory
def connectionMade(self):
'''鏈接創建時被回調的方法'''
self.factory.numProtocols = self.factory.numProtocols + 1
self.transport.write("Welcome! There are currently %d open connections.\n" %(self.factory.numProtocols,))
def connectionLost(self, reason):
'''鏈接關閉時被回調的方法'''
self.factory.numProtocols = self.factory.numProtocols - 1
def dataReceived(self, data):
'''接收數據的函數,當有數據到達時被回調'''
print data
self.transport.write(data)
class EchoFactory(Factory):
'''協議工廠類,當客戶端創建鏈接的時候,建立協議對象,協議對象與客戶端鏈接一一對應'''
numProtocols = 0
#protocol = Echo
def buildProtocol(self, addr):
return Echo(self)
if __name__ == '__main__':
# 建立監聽端口
FACTORY = EchoFactory()
reactor.listenTCP(1200, FACTORY)
# 開始監聽事件
reactor.run()
協議工廠繼承自twisted.internet.protocol.Factory,需實現buildProtocol方法,協議工廠負責實例化協議類,不該該保存於鏈接相關的狀態信息,由於協議工廠類僅建立一個。協議類繼承自twisted.internet.protocol.Protocol,需實現dataReceived等方法,在協議類中實現應用協議,每個客戶端鏈接都會建立一個新的協議類對象。transport就是鏈接對象,經過它進行網絡寫數據。
客戶端:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 1200))
import time
time.sleep(2)
sock.send('ls -al /home/wxh'+"\n")
print sock.recv(1024)
sock.send('ls -al /home/wxh'+"\n")
print sock.recv(1024)
sock.close()
使用守護進程來運行服務器端程序
使用守護進程的方式運行服務,須要提供一個tac配置文件(這就是一個python文件,只是擴展名不一樣),而且在這個文件中須要建立一個應用程序對象,對象名必須是application。
首先建立一個echo.tac文件:
#!/usr/bin/env python
# coding: utf-8
from twisted.application import service, internet
import sys
sys.path.append("e:\\") #echoServ.py保存在哪一個目錄,就把這個路徑設定到哪裏
from echoServ import EchoFactory
# 建立應用程序對象
application = service.Application('Echo 服務程序')
# 建立 service 對象
myServices = internet.TCPServer(1200, EchoFactory())
# 設置 application 爲 service 的父元素
myServices.setServiceParent(application)
而後用守護進程方式運行服務,運行命令:#twistd -y echo.tac。
若想要中止服務:#kill -15 (pid)。
客戶端程序:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 1200))
import time
time.sleep(2)
sock.send('ls -al /home/wxh'+"\n")
print sock.recv(1024)
sock.close()
聊天的服務器版本:
服務器端:
from twisted.internet import protocol, reactor
class Echo(protocol.Protocol):
def dataReceived(self, data):
# As soon as any data is received, write it back
self.transport.write(data)
print data
class EchoFactory(protocol.Factory):
def buildProtocol(self, addr):
return Echo()
reactor.listenTCP(8000, EchoFactory())
reactor.run()
客戶端:
from twisted.internet import reactor, protocol
class EchoClient(protocol.Protocol):
def connectionMade(self):
self.transport.write("hello, world!")
def dataReceived(self, data):
print "Server said:", data
self.transport.loseConnection()
def connectionLost(self, reason):
print "connection lost"
class EchoFactory(protocol.ClientFactory):
def buildProtocol(self, addr):
return EchoClient()
def clientConnectionFailed(self, connector, reason):
print "Connection failed - goodbye!"
reactor.stop()
def clientConnectionLost(self, connector, reason):
print "Connection lost - goodbye!"
reactor.stop()
reactor.connectTCP("localhost", 8000, EchoFactory())
reactor.run()
寫一個聊天的服務器:
服務器端:
#!/usr/bin/python
#encoding=utf-8
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor
class Chat(LineReceiver):
def __init__(self, users):
self.users = users
self.name = None
self.state = "GETNAME"
def connectionMade(self):
self.sendLine("What's your name?\n")
def connectionLost(self, reason):
if self.users.has_key(self.name):
del self.users[self.name]
def lineReceived(self, line):
if self.state == "GETNAME":
self.handle_GETNAME(line)
else:
self.handle_CHAT(line)
def handle_GETNAME(self, name):
if self.users.has_key(name):
self.sendLine("Name taken, please choose another."+"\n")
return
self.sendLine("Welcome, %s!\n" % (name,))
self.name = name
self.users[name] = self
self.state = "CHAT"
def handle_CHAT(self, message):
message = "<%s> %s\n" % (self.name, message)
for name, protocol in self.users.iteritems():
if protocol == self:
self.sendLine(message+"\n")
class ChatFactory(Factory):
def __init__(self):
self.users = {} # maps user names to Chat instances
def buildProtocol(self, addr):
return Chat(self.users)
if __name__ == '__main__':
reactor.listenTCP(1200, ChatFactory())
reactor.run()
客戶端1:
#socket client end
from socket import *
s=socket(AF_INET,SOCK_STREAM)
remote_host=gethostname()
print 'remote_host:',remote_host
port=1200
s.connect((remote_host,port)) #發起鏈接
print("Connected from",s.getsockname()) ##返回本地IP和端口
print("Connected to",s.getpeername()) ##返回服務端IP和端口
s.send('Jack\r\n')#發送一行字符串(以\r\n 結束)到服務器端
s.send('hi\r\n')
s.send('hello\r\n')
print 'the msg i got from select server is:'
print s.recv(1200)
客戶端2:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 1200))
import time
time.sleep(2)
sock.send('fosterwu\r\n')
print sock.recv(1020)
sock.send('hi\r\n')
print sock.recv(1020)
sock.send('good\r\n')
print sock.recv(120)
sock.send('gloryroad\r\n')
print sock.recv(120)
sock.close()
Twisted框架中對文件的操做
服務端:
工廠有startFactory和stopFactory兩種方式來執行相關應用的建立與銷燬,下述代碼從客戶端接收到的信息都會被寫入文件中。
#!/usr/bin/env python
# coding: utf-8
from twisted.internet.protocol import Factory
from twisted.protocols.basic import LineReceiver
from twisted.internet import reactor
class LoggingProtocol(LineReceiver):
def lineReceived(self, line):
self.factory.fp.write(line+'\n')
self.factory.fp.flush()
self.sendLine("got it!")
class LogfileFactory(Factory):
protocol = LoggingProtocol
def __init__(self, fileName):
self.file = fileName
def startFactory(self):
self.fp = open(self.file, 'a')
def stopFactory(self):
self.fp.close()
print "close"
if __name__ == '__main__':
# 建立監聽端口
FACTORY = LogfileFactory("e:\\a.txt")
reactor.listenTCP(8007, FACTORY)
# 開始監聽事件
reactor.run()
客戶端:
if __name__ == '__main__':
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('localhost', 8007))
import time
time.sleep(2)
sock.send('fosterwu\r\n')
print sock.recv(1020)
sock.send('hi\r\n')
print sock.recv(1020)
sock.send('good\r\n')
print sock.recv(120)
sock.send('gloryroad\r\n')
print sock.recv(120)
sock.close()
Udp Server 端:
from socket import *
from time import ctime
HOST = ''
PORT = 1200
BUFSIZ = 128
ADDR = (HOST, PORT)
#建立一個服務器端UDP套接字
udpServer = socket(AF_INET, SOCK_DGRAM)
#綁定服務器套接字
udpServer.bind(ADDR)
while True:
print 'waiting for message...'
#接收來自客戶端的數據
data, addr = udpServer.recvfrom(BUFSIZ)
print "got message:",data
#向客戶端發送數據
udpServer.sendto('[%s] %s' % (ctime(), data), addr)
print '...received from and returned to:', addr
udpServer.close()
Udp 的客戶端:
#encoding=utf-8
from socket import *
HOST = 'localhost'
PORT = 1200
BUFSIZ = 128
ADDR = (HOST, PORT)
# 建立客戶端UDP套接字
udpClient = socket(AF_INET, SOCK_DGRAM)
while True:
data = raw_input('>')
if not data:
break
# 向服務器端發送數據
udpClient.sendto(data, ADDR)
# 接收來自服務器端的數據
data, ADDR = udpClient.recvfrom(BUFSIZ)
print data
if not data:
break
udpClient.close()
參考文獻:
http://blog.csdn.net/taiyang1987912/article/details/40376067
http://www.javashuo.com/article/p-fjrqjhce-d.html
基於廣播的聊天程序
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
SO_LINGER選項用來設置當調用closesocket時是否立刻關閉socket;
SO_REUSEADDR用於對TCP套接字處於TIME_WAIT狀態下的socket,才能夠重複綁定使用。server程序老是應該在調用bind()以前設置SO_REUSEADDR套接字選項。TCP,先調用close()的一方會進入TIME_WAIT狀態
服務器端:Linux能夠執行
import socket, select
#Function to broadcast chat messages to all connected clients
def broadcast_data (sock, message):
#Do not send the message to master socket and the client who has send us the message
for socket in CONNECTION_LIST:
if socket != server_socket and socket != sock :
try :
socket.send(message)
except :
# broken socket connection may be, chat client pressed ctrl+c for example
socket.close()
CONNECTION_LIST.remove(socket)
if __name__ == "__main__":
# List to keep track of socket descriptors
CONNECTION_LIST = []
RECV_BUFFER = 4096 # Advisable to keep it as an exponent of 2
PORT = 6001
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# this has no effect, why ?
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.bind(("0.0.0.0", PORT))
server_socket.listen(10)
# Add server socket to the list of readable connections
CONNECTION_LIST.append(server_socket)
print "Chat server started on port " + str(PORT)
while 1:
# Get the list sockets which are ready to be read through select
read_sockets,write_sockets,error_sockets = select.select(CONNECTION_LIST,[],[])
for sock in read_sockets:
#New connection
if sock == server_socket:
# Handle the case in which there is a new connection recieved through server_socket
sockfd, addr = server_socket.accept()
CONNECTION_LIST.append(sockfd)
print "Client (%s, %s) connected" % addr
broadcast_data(sockfd, "[%s:%s] entered room\n" % addr)
#Some incoming message from a client
else:
# Data recieved from client, process it
try:
#In Windows, sometimes when a TCP program closes abruptly,
# a "Connection reset by peer" exception will be thrown
data = sock.recv(RECV_BUFFER)
if data:
broadcast_data(sock, "\r" + '<' + str(sock.getpeername()) + '> ' + data)
except:
broadcast_data(sock, "Client (%s, %s) is offline" % addr)
print "Client (%s, %s) is offline" % addr
sock.close()
CONNECTION_LIST.remove(sock)
continue
server_socket.close()
客戶端:
import socket, select, string, sys
def prompt() :
sys.stdout.write('<You> ')
sys.stdout.flush()
#main function
if __name__ == "__main__":
if(len(sys.argv) < 3) :
print 'Usage : python telnet.py hostname port'
sys.exit()
host = sys.argv[1]
port = int(sys.argv[2])
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(6)
# connect to remote host
try :
s.connect((host, port))
except :
print 'Unable to connect'
sys.exit()
print 'Connected to remote host. Start sending messages'
prompt()
while 1:
rlist = [sys.stdin, s]
#s.connect((host, port))
# Get the list sockets which are readable
read_list, write_list, error_list = select.select(rlist , [], [])
for sock in read_list:
#incoming message from remote server
if sock == s:
data = sock.recv(4096)
if not data :
print '\nDisconnected from chat server'
sys.exit()
else :
#print data
sys.stdout.write(data)
prompt()
#user entered a message
else :
msg = sys.stdin.readline()
s.send(msg)
prompt()
執行:
須要多開幾個客戶端,才能看到廣告效果,執行以下:
python client.py 127.0.0.1 6001
Unix下是sock,而windows下是winsock,中間語法有不少是不一樣的,因此windows運行client程序有問題