Ch03 TCP(Blocked)

TCP通信流程

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


調用Send所產生的結果

  1. 若是網卡處在空閒狀態或者緩衝區足夠,那麼就會馬上返回,返回值是要發送的數據長度。api

  2. 若是網卡處於忙碌狀態,而且緩衝區也已經滿了,那麼程序將會掛起,直到須要發送的數據被網卡或者緩衝區所接收。服務器

  3. 若是網卡處於忙碌狀態,而且緩衝區不足以存放全部須要發送的數據,將只會緩衝從頭開始的能夠容納的數據量,剩下的數據沒法再緩衝了,返回已經被緩衝的數據長度。socket


調用Recv所產生的結果

  1. 若是沒有數據到達,則recv操做則會讓程序掛起,直到有數據到達。tcp

  2. 若是緩衝區裏面有大量的數據,那麼recv會返回程序所請求的數據量。oop

  3. 若是緩衝區裏面有數據,可是小於所請求的數據量,那麼recv會返回現有的數據。學習

  4. 若是對面鏈接已經關閉,那麼recv直接返回0.ui

 

綁定127.0.0.1,或者局域網IP地址,或者0.0.0.0的區別

   127.0.0.1時,只會接收connecetIP地址爲127.0.0.1的鏈接this

   局域網IP地址,只會接收connecetIP地址爲對應的局域網IP地址的鏈接

  0.0.0.0時,至關於綁定本地的全部網卡的IP地址,包括127.0.0.1


TCP地址和端口已經被使用,重複bind

 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()

 

TCP流當作文件流使用

     python中,文件對象能夠read()write(),  字能夠send()recv(),可是沒有任何對象能夠同時執行這兩對操做。

     Socketmakefile()操做,可讓使其能夠像文件流同樣進行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)
相關文章
相關標籤/搜索