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)