-
s.bind(address) 將套接字綁定到地址。address地址的格式取決於地址族。在AF_INET下,以元組(host,port)的形式表示地址。
-
s.listen(backlog) 開始監聽傳入連接。backlog指定在拒絕連接之前,操作系統可以掛起的最大連接數量。該值至少爲1,大部分應用程序設爲5就可以了。
-
s.connect(address) 連接到address處的套接字。一般,address的格式爲元組(hostname,port),如果連接同一臺機器上的服務器,可以將hostname設爲‘localhost’。如果連接出錯,返回socket.error錯誤。
-
s.connect_ex(adddress) 功能與connect(address)相同,但是成功返回0,失敗返回errno的值。
-
s.accept() 接受連接並返回(conn,address),其中conn是新的套接字對象,可以用來接收和發送數據。address是連接客戶端的地址。
-
s.close() 關閉套接字。
-
s.fileno() 返回套接字的文件描述符。
-
s.getpeername() 返回連接套接字的遠程地址。返回值通常是元組(ipaddr,port)。
-
s.getsockname() 返回套接字自己的地址。通常是一個元組(ipaddr,port)
-
s.getsockopt(level,optname[.buflen]) 返回套接字選項的值。
-
s.gettimeout() 返回當前超時期的值,單位是秒,如果沒有設置超時期,則返回None。
-
s.recv(bufsize[,flag]) 接受套接字的數據。數據以字符串形式返回,bufsize指定要接收的最大數據量。flag提供有關消息的其他信息,通常可以忽略。
-
s.recvfrom(bufsize[.flag]) 與recv()類似,但返回值是(data,address)。其中data是包含接收數據的字符串,address是發送數據的套接字地址。
-
s.send(string[,flag]) 將string中的數據發送到連接的套接字。返回值是要發送的字節數量,該數量可能小於string的字節大小。
-
s.sendall(string[,flag]) 將string中的數據發送到連接的套接字,但在返回之前會嘗試發送所有數據。成功返回None,失敗則拋出異常。
-
s.sendto(string[,flag],address) 將數據發送到套接字,address是形式爲(ipaddr,port)的元組,指定遠程地址。返回值是發送的字節數。該函數主要用於UDP協議。
-
s.setblocking(flag) 如果flag爲0,則將套接字設爲非阻塞模式,否則將套接字設爲阻塞模式(默認值)。非阻塞模式下,如果調用recv()沒有發現任何數據,或send()調用無法立即發送數據,那麼將引起socket.error異常。
-
s.setsockopt(level,optname,value) 設置給定套接字選項的值。
-
s.settimeout(timeout) 設置套接字操作的超時期,timeout是一個浮點數,單位是秒。值爲None表示沒有超時期。一般,超時期應該在剛創建套接字時設置,因爲它們可能用於連接的操作(如connect())普通的非套接字實例的函數
-
getdefaulttimeout()返回默認的套接字超時時間(以秒爲單位)。None表示不設置任何超時時間。
-
gethostbyname(hostname) 將主機名(如“www.baidu.com”)轉換爲IPv4地址,IP地址將以字符串的形式返回,如“8.8.8.8”。不支持IPv6
-
gethostname() 返回本地機器的主機名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
Server.py 服務端 #Echo server program import socket HOST = ‘’ #空代表0.0.0.0 PORT = 50007 #監聽端口 s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) # 生成socket tcp通信實例, s.bind((HOST,PORT)) #綁定ip和端口,注意bind只接受一個參數,(HOST,PORT) 做成一個元祖傳進去 s.listen( 1 ) #開始監聽,裏面的數字是代表服務端在拒絕新連接之前可以最多掛起多少連接,不過實驗過了沒啥用,所以寫個1就好了 conn,addr = s.accept() #接受連接,並返回兩個變量,conn代表每個新連接進入後服務端都會爲其生成一個新實例,後面可以用這個實例進行發送和接收,addr是連接進來的客戶端的地址,accept()方法在有新連接進入時就會返回conn,addr這兩個變量,但如果沒有連接時,此方法就會阻塞直至有新連接過來。 print 'Connected by' , addr while True : data = conn.recv( 1024 ) #接收1024字節數據 if not data: break #如果收不到客戶端數據了(代表客戶端斷開了),就斷開 conn.sendall(data.upper()) #將收到的數據全變成大寫再發給客戶端 conn.close() #關閉連接 |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
Client.py 客戶端 import socket HOST = '192.168.3.1' # 遠程socket服務器ip PORT = 50007 # 遠程socket服務器端口 s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) #實例化socket s.connect((HOST,PORT)) #連接socket服務器 while True : msg = raw_input ( "Your msg::" ).strip() #讓用戶輸入消息,去除回車和空格 if len (msg) = = 0 : continue s.sendall(msg) #向服務器發送消息 data = s.recv( 1024 ) #接收服務器的消息 print 'Received:' , data s.close() |
1
|
if not data: break |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#Echo server program import socket HOST = '' #空代表0.0.0.0 PORT = 50007 #監聽端口 s = socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.bind((HOST,PORT)) s.listen( 1 ) while True : conn,addr = s.accept() print 'Connected by' , addr while True : data = conn.recv( 1024 ) #接收1024字節數據 if not data: break #如果收不到客戶端數據了(代表客戶端斷開了),就斷開 conn.sendall(data.upper()) #將收到的數據全變成大寫再發給客戶端 conn.close() #關閉此客戶端的連接實例 |
1
|
conn,addr = s.accept() |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
import SocketServer class MyTCPHandler(SocketServer.BaseRequestHandler): #繼承BaseRequestHandler基類,然後必須重寫handle方法,並且在handle方法裏實現與客戶端的所有交互 def handle( self ): while True : data = self .request.recv( 1024 ) #接收1024字節數據 if not data: break self .request.sendall(data.upper()) if __name__ = = "__main__" : HOST, PORT = "localhost" , 50007 # 把剛纔寫的類當作一個參數傳給ThreadingTCPServer這個類,下面的代碼就創建了一個多線程socket server server = SocketServer.ThreadingTCPServer((HOST, PORT), MyTCPHandler) # 啓動這個server,這個server會一直運行,除非按ctrl-C停止 server.serve_forever() |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
#_*_coding:utf-8_*_ __author__ = 'jieli' import socket import os class FtpClient( object ): def __init__( self ,host,port ): self .sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self .sock.connect((host, port)) #連接服務器 def start( self ): #實例化客戶端類後,需要調用此方法啓動客戶端 self .interactive() #跟用戶的交互都在這個方法裏 def interactive( self ): while True : user_input = raw_input ( ">>:" ).strip() if len (user_input) = = 0 : continue user_input = user_input.split() #用戶輸入的指令進行拆分,第一個參數是指要進行什麼動作,比如get remote_filename if hasattr ( self ,user_input[ 0 ]): #判斷類中是否有get或其它輸入的方法 func = getattr ( self ,user_input[ 0 ]) #通過字符串獲取類中對應方法的內存對象 func(user_input) #調用此內存對象 else : print "\033[31;1mWrong cmd usage\033[0m" def get( self ,msg): #從服務器端下載文件 print '--get func---' ,msg if len (msg) = = 2 : file_name = msg[ 1 ] instruction = "FileTransfer|get|%s" % file_name #告訴服務器端要下載什麼文件 self .sock.send(instruction) feedback = self .sock.recv( 100 ) #等待服務器端的消息確認 print '-->' ,feedback if feedback.startswith( "FileTransfer|get|ready" ): #代表服務器上文件存在,並且服務器已經準備好了發送此文件到客戶端 file_size = int (feedback.split( "|" )[ - 1 ]) # 服務器端發回來的確認消息中,最後面一個值是文件大小,必須知道文件大小才知道一共要收多少內容 self .sock.send( "FileTransfer|get|recv_ready" ) #告訴服務器端已經準備好了接收 recv_size = 0 # 因爲文件可能會比較大,一次收不完,所以要循環收,每收到一次,就計個數 f = file ( 'client_recv/%s' % os.path.basename(file_name), 'wb' ) #在本地創建一個新文件來存這個要下載的文件內容 print '--->' ,file_name while not file_size = = recv_size: #只要文件總大小和已收到的大小不想等,就代表還沒收完 if file_size - recv_size> 1024 : #文件總大下減已收到的大小等於還剩下沒收到的大小,如果這個數大於1024,代表一次肯定收不完,那就還得多循環幾次 data = self .sock.recv( 1024 ) #這次收1024字節,但實際上收到的可能比1024小,所以需要以實際收到的數爲準 recv_size + = len (data) # 已收到的大小加上這一次循環收到的實際大小 else : # 如果最後剩下的少於1024,那就一次性把剩下的都收過來 data = self .sock.recv(file_size - recv_size) #recv_size = file_size #不能這麼寫,因爲這一次依然不一定能一次性收完,因爲實際收到的數據可能比你規定的數據要少, 所以需要按下面這行的方式寫 recv_size + = (file_size - recv_size) f.write(data) #收到的內容寫入文件 print file_size,recv_size else : print '---recv file:%s---' % file_name f.close() else : print feedback else : print "\033[31;1mWrong cmd usage\033[0m" def put( self ): pass def ls( self ): pass def cd( self ): pass def delete( self ): pass if __name__ = = "__main__" : f = FtpClient( 'localhost' , 9002 ) f.start() |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
#_*_coding:utf-8_*_ import SocketServer import os class MyTCPHandler(SocketServer.BaseRequestHandler): def handle( self ): while True : instruction = self .request.recv( 1024 ).strip() #接收客戶端命令 if not instruction : break instruction = instruction.split( '|' ) #將客戶端發過來的消息按|來拆分,消息類似這種格式 “FileTransfer|get|file_name” if hasattr ( self ,instruction[ 0 ]): #判斷類中是否有這個方法 func = getattr ( self , instruction[ 0 ]) #獲取這個方法的內存對象 func(instruction) #調用此方法 def FileTransfer( self ,msg): #負責文件的發送和接收 print '---filetransfer---' ,msg if msg[ 1 ] = = 'get' : #如果客戶端發來的指令是get,那就是下載文件 print "client wants to download file:" , msg[ 2 ] if os.path.isfile(msg[ 2 ]): #判斷客戶端發的文件名是否存在並是個文件 file_size = os.path.getsize(msg[ 2 ]) # 獲取文件大小 res = "ready|%s" % file_size # 把文件大小告訴客戶端 else : res = "file doesn't exist" #文件也有可能不存在 send_confirmation = "FileTransfer|get|%s" % res self .request.send(send_confirmation) # 發送確認消息給客戶端 feedback = self .request.recv( 100 ) #等待客戶端確認, 如果這時不等客戶端確認就立刻給客戶端發文件內容,因爲爲了減少IO操作,socket發送和接收是有緩衝區的,緩衝區滿了纔會發送,那上一條消息很有可能會和文件內容的一部分被合併成一條消息發給客戶端,這就行成了粘包,所以這裏等待客戶端的一個確認消息,就把兩次發送分開了,不會再有粘包 if feedback = = 'FileTransfer|get|recv_ready' : #如果客戶端說準備好接收了 f = file (msg[ 2 ], 'rb' ) send_size = 0 #發送的邏輯跟客戶端循環接收的邏輯是一樣的 while not file_size = = send_size : if file_size - send_size > 1024 : data = f.read( 1024 ) send_size + = 1024 else : #left data less than 1024 data = f.read(file_size - send_size) send_size + = (file_size - send_size) self .request.send(data) print file_size,send_size else : print '---send file:%s done----' % msg[ 2 ] f.close() elif msg[ 1 ] = = 'put' : pass if __name__ = = '__main__' : HOST, PORT = "", 9002 server = SocketServer.ThreadingTCPServer((HOST, PORT), MyTCPHandler) server.serve_forever() |