First, TCP is unwieldy for protocols where clients want to send single, small requests to a server, and then are done and will not talk to it further. It takes three packets for two hosts to set up a TCPpython
connection—the famous sequence of SYN, SYN-ACK, and ACK (which mean 「I want to talk, here is the packet sequence number I will be starting with」; 「okay, here’s mine」; 「okay!」)—and then another three or four to shut the connection back down (either a quick FIN, FIN-ACK, ACK, or a slightly longer pair of separate FIN and ACK packets). That is six packets justto send a single request! Protocol designers quickly turn to UDP in such cases.git
若是網卡處在空閒狀態或者緩衝區足夠,那麼就會馬上返回,返回值是要發送的數據長度。api
若是網卡處於忙碌狀態,而且緩衝區也已經滿了,那麼程序將會掛起,直到須要發送的數據被網卡或者緩衝區所接收。服務器
若是網卡處於忙碌狀態,而且緩衝區不足以存放全部須要發送的數據,將只會緩衝從頭開始的能夠容納的數據量,剩下的數據沒法再緩衝了,返回已經被緩衝的數據長度。socket
若是沒有數據到達,則recv操做則會讓程序掛起,直到有數據到達。tcp
若是緩衝區裏面有大量的數據,那麼recv會返回程序所請求的數據量。oop
若是緩衝區裏面有數據,可是小於所請求的數據量,那麼recv會返回現有的數據。學習
若是對面鏈接已經關閉,那麼recv直接返回0.ui
127.0.0.1時,只會接收connecet的IP地址爲127.0.0.1的鏈接this
局域網IP地址,只會接收connecet的IP地址爲對應的局域網IP地址的鏈接
0.0.0.0時,至關於綁定本地的全部網卡的IP地址,包括127.0.0.1
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
這裏面的SO_REUSEADDR端口可重用,能夠重複bind()相同的IP地址和端口號,可是真正可以正常運行的只有一個。
原理:暫時不清楚,應該是爲了防止該端口上次使用時,沒有關閉乾淨,對面沒有發送回覆的數據包,從而使端口處於CLOSE-WAIT或者 TIME-WAIT狀態,使用這個標記用於表示等狀態正常時馬上使用該端口。FIN ACK是什麼東東?
收到的數據若是沒有被即時取出,操做系統就會將其放到緩衝區裏面,緩衝區的大小不是無限的。假如發送端一直髮送數據,而接收端不從接收數據,就有可能形成緩衝區滿, 這個時候接收端就會告知發送端,緩衝區已經滿了,請不要再發了,在阻塞模式下調用sendall()就會致使程序被阻塞,掛起。
當接收數據讀取到了end-of-file時,read返回0/False,這個時候就表示,遠端已經關閉鏈接。
Close()用來完整的關閉一個鏈接
Shutdown()經過設置緩衝區的可讀仍是可寫標記來半關閉一個鏈接,標記位有SHUT_WR, SHUT_RD, SHUT_RDWR,其中SHUT_RDWR可讓全部引用該套接字的地方都不能再收發數據,而close()只是關閉當前模塊的,而不會去管理其餘共享模塊的引用。
半開鏈接的功能,能夠在只須要發送數據或者只須要處理接收的數據的地方使用。當套接字建立時,馬上調用shutdown()。
在python中,文件對象能夠read()和write(), 字能夠send()和recv(),可是沒有任何對象能夠同時執行這兩對操做。
Socket的makefile()操做,可讓使其能夠像文件流同樣進行read()和write()操做。
>>> import socket >>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) >>> hasattr(s, 'read') False >>> f = s.makefile() >>> hasattr(f, 'read') True
1.代碼片斷,學習python
hasattr, 查看對象是否有某個屬性
>>> import socket >>> s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) >>> hasattr(s, 'read') False >>> f = s.makefile() >>> hasattr(f, 'read') True
String.upper所有轉爲大寫,string.title每一個單詞的第一個字母大寫,join()鏈接全部的子字符串爲一個
>>> message = 'the tragedy of macbeth' >>> blocks = message[:16], message[16:] >>> ''.join( b.upper() for b in blocks ) # works fine 'THE TRAGEDY OF MACBETH' >>> ''.join( b.title() for b in blocks ) # whoops 'The Tragedy Of MAcbeth'
List的使用,argv的讀取
import sys print(sys.argv[1:]) print(sys.argv[1])
C:\Users\winter\Desktop>python test.py server 1 ['server', '1'] #獲得的是一個List Server #獲得的是單個元素
if sys.argv[1:] == ['server']: #這個就代表只有一個參數,而且參數的值是’server’
2.接收的bytes轉爲string,發送的string轉爲bytes
message = sc.recv(1024) if not message: break message = message.decode('utf-8') sc.sendall(message.encode('utf-8'))
3.TCP使用的接口總結
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #建立一個TCP套接字 s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) #將端口設置爲能夠重複使用 s.bind((HOST, PORT)) #綁定IP地址和端口, TCP仍是UDP上面已經肯定 s.listen(1) #開始進行監聽, 參數的意思代表最大的等待鏈接數,不是指現有的鏈接數量 sc, sockname = s.accept() #接收一個鏈接,sc是套接字, sockname 是IP地址和端口號 sc.sendall('Farewell, client') #發送數據,直到全部的數據都被髮送 sc.close() #關閉套接字,該對象將不能發送和數據 s.recv(42) #接收數據,數據大小小於或者等於參數的值 s.connect((HOST, PORT)) #鏈接指定的IP地址和端口號, TCP或者UCP由s建立的時候定義 sc.getsockname() #獲取套接字本地所使用的IP地址和端口號 sc.getpeername() #獲取對端的IP地址和端口號
4.該注意的地方
處理阻塞套接字時,須要想辦法解決緩衝區滿的問題,多進程或者線程讀寫緩衝區。
簡單的Tcp服務器和客戶端
#!/usr/bin/env python # Foundations of Python Network Programming - Chapter 3 - tcp_sixteen.py # Simple TCP client and server that send and receive 16 octets import socket,sys s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) HOST = sys.argv.pop() if len(sys.argv) == 3 else '127.0.0.1' PORT = 1060 def recv_all(sock,length): data = '' while len(data) < length: more = sock.recv(length - len(data)) if not more: raise EOFError('socket closed %d bytes into a %d-byte message'%(len(data),length)) data += more.decode('utf-8') return data if sys.argv[1:] == ['server']: s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((HOST,PORT)) s.listen(1) while True: print('Listening at', s.getsockname()) sc,sockname = s.accept() print('We have accepted a connection from', sockname) print('Socket connects', sc.getsockname(), 'and', sc.getpeername()) message = recv_all(sc,16) print('The incoming sixteen-octet message says', repr(message)) sc.sendall(b'Farewell, client') sc.close() print('Reply sent, socket closed') elif sys.argv[1:] == ['client']: s.connect((HOST,PORT)) print('Client has been assigned socket name', s.getsockname()) s.sendall(b'Hi thers, server') reply = recv_all(s,16) print('The server said', repr(reply)) s.close() else: print('usage:tcp_local.py server|client [host]', file=sys.stderr)
簡單的Tcp服務器和客戶端,不能夠重複bind
#!/usr/bin/env python # Foundations of Python Network Programming - Chapter 3 - tcp_sixteen.py # Simple TCP client and server that send and receive 16 octets import socket,sys s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) HOST = sys.argv.pop() if len(sys.argv) == 3 else '127.0.0.1' PORT = 1060 def recv_all(sock,length): data = '' while len(data) < length: more = sock.recv(length - len(data)) if not more: raise EOFError('socket closed %d bytes into a %d-byte message'%(len(data),length)) data += more.decode('utf-8') return data if sys.argv[1:] == ['server']: #s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((HOST,PORT)) s.listen(1) while True: print('Listening at', s.getsockname()) sc,sockname = s.accept() print('We have accepted a connection from', sockname) print('Socket connects', sc.getsockname(), 'and', sc.getpeername()) message = recv_all(sc,16) print('The incoming sixteen-octet message says', repr(message)) sc.sendall(b'Farewell, client') sc.close() print('Reply sent, socket closed') elif sys.argv[1:] == ['client']: s.connect((HOST,PORT)) print('Client has been assigned socket name', s.getsockname()) s.sendall(b'Hi thers, server') reply = recv_all(s,16) print('The server said', repr(reply)) s.close() else: print('usage:tcp_local.py server|client [host]', file=sys.stderr)
緩衝區滿形成死鎖的例子
python tcp_deadlock.py server
python tcp_deadlock.py client 1073741824 #發送超大量的數據
#!/usr/bin/env python # Foundations of Python Network Programming - Chapter 3 - tcp_deadlock.py # TCP client and server that leave too much data waiting import socket,sys s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) HOST= '127.0.0.1' PORT = 1060 if sys.argv[1:] == ['server']: s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR,1) s.bind((HOST,PORT)) s.listen(1) while True: print('Listen at', s.getsockname()) sc,sockname = s.accept() print('Processing up to 1024 bytes at a time from', sockname) n= 0 while True: message = sc.recv(1024) if not message: break n += len(message) message = message.decode('utf-8') message = message.upper() sc.sendall(message.encode('utf-8')) #send it back uppercase print('\r%d bytes processed so far' % (n)) sys.stdout.flush() print() sc.close() print('Completed processing') elif len(sys.argv) == 3 and sys.argv[1] == 'client' and sys.argv[2].isdigit(): bytes = (int(sys.argv[2]) + 15) // 16*16 #round up to//16 message = 'capitalize this!' #16-byte message to repate over and over message = message.encode('utf-8') print('Sending',bytes,'bytes of data, in chunk of 16 bytes') s.connect((HOST,PORT)) sent = 0 while sent < bytes: s.sendall(message) sent += len(message) print('\r%d bytes sent' % (sent)) sys.stdout.flush() print() s.shutdown(socket.SHUT_WR) print('Receiving all the data the server sends back') received = 0 while True: data = s.recv(42) if not received: print('The first data received says', repr(data)) received += len(data) if not data: break print('\r%d bytes received' % (received)) s.close() else: print('usage:tcp_deadlock.py server | clinet <bytes>', file = sys.stderr)