凡科建站公司百度seo网站优化
1.TCP。
TCP叫做传输控制协议,是OSI七层参考模型中传输层使用的协议,传输文档和文件的协议几乎都是使用TCP。
2.TCP工作原理。
TCP不像UDP一样提供尽最大可能的交付,它提供的是可靠传输。以下是TCP提供可靠传输的基本原理:
- 每个TCP数据包都有一个序列号,接收方通过序列号将响应数据包正确排序。也可通过该序列号发现传输序列中丢弃的数据包,并请求重传。
- TCP并不使用顺序的整数作为序列号,而是通过一个计数器来记录发送的字节数。
- 一个优秀的TCP实现中,初始序列号是随机选择的。这样就给伪造数据包加大了难度。
- TCP无须等待响应就能一口气发送多个数据包。在某一时刻发送方希望同时传输的数据量叫做TCP窗口的大小。
- 接收方的TCP实现可以通过控制发送方的窗后大小来减缓或短暂暂停连接。这叫作流量控制。这使得接收方在输入缓冲区已满时可以禁止更多数据包的传输。此时如果还有数据到达的话,那么这些数据也会被丢弃。
- 如果TCP认为数据包被丢弃了,它会假定网络正在变得拥挤,然后减少每秒发送的数据量。
在两台主机之间建立TCP连接需要三个数据包,这三个数据包组成了一个序列——SYN,SYN-ACK,ACK。
- SYN:“我想进行通信,这是数据包的初始序列号。”
- SYN-ACK:“好的,这是我向你发送数据包的初始序列号”
- ACK:“好的”
这就是俗称的TCP的三次握手。
3.TCP套接字。
- 使用TCP这样支持状态的流协议,connect()调用是后续所有网络通信所依赖的首要步骤。只有操作系统的网络栈成功完成TCP三次握手,TCP流的双方才算做好了通信的准备。
- TCP的标准POSIX接口实际上包含两种截然不同的套接字类型:“被动”监听套接字和主动“连接”套接字。
-
被动套接字又叫做监听套接字。服务器通过该套接字来接受连接请求,但是该套接字不能用于发送和接受任何数据,也不表示任何实际的网络会话。而是由服务器指示被动套接字通知操作系统首先使用哪个特定的TCP端口号来接受连接请求。
-
主动套接字又叫作连接套接字。他将一个特定的IP地址及端口号和某个与其进行远程会话的主机绑定。该套接字只用于与该特定远程主机进行通信,可以通过该套接字发送或接受数据。
-
被动套接字由接口IP地址和正在监听的端口号来唯一标识,因此任何其他应用程序都无法在使用相同的IP地址和端口。但是,多个主动套接字是可以共享一个本地套接字名的。
4.一个简单的TCP客户端和服务器。
代码:
#!/usr/bin/python
#coding:utf-8
import argparse,socket
#编写接收数据函数得功能使其能设置接受特定长度数据的的功能
def recvall(sock,length):data = b''while len(data) < length:more = sock.recv(length - len(data))if not more:raise EOFError('was expecting %d bytes but only received'' %d bytes before the socket closed'% (length,len(data)))data += morereturn datadef server(interface,port):sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)sock.bind((interface,port))#设置最大连接数sock.listen(1)print('Listening at',sock.getsockname())while True:print('Waiting to accept a new connection')#accept()方法唯一目的就是用于支持TCP套接字的监听功能sc,sockname = sock.accept()print('We have accepted a connection from',sockname)#打印套接字本地的端点地址print(' Socket name:', sc.getsockname())# 打印与套接字连接的远程地址print(' Socket peer:', sc.getpeername())#接收数据,接受十六字节的数据message = recvall(sc,16)print(' Incoming sixteen-octet message:',repr(message))#使用sendall()python线程在所有数据发送完之前不会竞争资源sc.sendall(b'Farewell, client')sc.close()print(' Reply sent,socket closed')def client(host,port):sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)sock.connect((host,port))print('Client has been assigned socket name',sock.getsockname())sock.sendall(b'Hi there, server')reply = recvall(sock,16)#repr()方法可以观察转义字符,不会翻译转义字符print('The server said',repr(reply))sock.close()if __name__ == '__main__':choices = {'client':client,'server':server}parser = argparse.ArgumentParser(description='Send and receive over TCP')parser.add_argument('role',choices=choices,help='Which role to play')parser.add_argument('host',help='interface the server listens at;''host the client sends to')parser.add_argument('-p',metavar='PORT',type=int,default=1060,help='TCP port (default 1060)')args = parser.parse_args()function = choices[args.role]function(args.host,args.p)
首先服务器调用bind()来声明一个特定的端口,它还没决定该程序到底会作为服务器还是客户端。接着调用listen()该程序通过调用它,表明希望套接字进行监听,此时才真正决定了程序要作为服务器。listen()调用对套接字是无法撤销的,而且调用之后该套接字再也不能用于发送和接收数据,该套接字此时只能通过accept()方法来接受连接请求。
测试结果:
5.地址已被占用。
服务器在绑定端口之前进行了这句代码:
sock.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
通过设定套接字选项SO_REUSEADDR,可以指明,应用程序能够使用一些网络客户端之前的连接正在关闭的端口,否则上一个套接字将无法立即消失导致连接失败。
6.死锁。
当两个程序共享有限资源时,由于糟糕的计划,只能一直等待对方结束资源占用,这种情况就是死锁,TCP栈使用了缓冲区,这样就可以在应用程序准备好读取数据前存放接收到的数据,也可以在网络硬件准备好发送数据包前存放要发送的数据。但是这些缓冲区大小是有限制的,如果有太多服务器和客户端未来得及处理的数据就会造成麻烦。下面是一个可能造成死锁的TCP服务器和客户端。
代码:
#!/usr/bin/python
#coding:utf-8
import argparse,sys,socketdef server(host,port,bytecount):sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)sock.setsockopt(socket.SOL_SOCKET,socket.SOCK_STREAM,1)sock.bind((host,port))sock.listen(1)print('Listening at',sock.getsockname())while True:sc,sockname = sock.accept()print('Processing up to 1024 bytes at a time from',sockname)n = 0while True:data = sc.recv(1024)if not data:breakoutput = data.decode('ascii').upper().encode('ascii')sc.sendall(output)n += len(data)print('\r %d bytes processed so far' % (n,), end=' ')#刷新输出这句代码只在Linux系统下起作用sys.stdout.flush()print()sc.close()print(' Socket closed')def client(host,port,bytecount):sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)# // 表示商取整如7//2=3bytocount = (bytecount + 15) // 16 * 16message = b'capitalize this!'print('Sending', bytecount, 'bytes of data,in chunks of 16 bytes')sock.connect((host,port))sent = 0while sent < bytecount:sock.sendall(message)sent += len(message)print('\r %d bytes sent' % (sent,), end=' ')sys.stdout.flush()print()#表示客户端不再向套接字写入数据,而服务端也不会再读取任何数据并认为遇到了文件结束符sock.shutdown(socket.SHUT_WR)print('Receiving all the data the server sends back')received = 0while True:data = sock.recv(42)if not received:#repr()方法可以观察转义字符,不会翻译转义字符print(' The first data received says',repr(data))if not data:breakreceived += len(data)print('\r %d bytes recevied' % (received,), end=' ')print()sock.close()if __name__ == '__main__':choices = {'client':client,'server':server}parser = argparse.ArgumentParser(description='Get deadlocked over TCP')parser.add_argument('role',choices=choices,help='Which role to play')parser.add_argument('host',help='interface the server listens at;''host the client sends to')parser.add_argument('bytecount', type=int,nargs='?',default=16,help='number of bytes for client to send (default 16)')parser.add_argument('-p',metavar='PORT',type=int,default=1060,help='TCP port (default 1060)')args = parser.parse_args()function = choices[args.role]function(args.host,args.p,args.bytecount)
该服务器的任务是将任意数量的文本转换为大写形式。由于客户端的请求量可能非常的大,如果试图读取整个输入流再做处理的话,可能会耗尽系统内存。因此,该服务器每次只读取并处理1024字节的小型数据块。
测试结果:
如果设置发送很大的数据将造成客户端和服务器的死锁。
7.已关闭连接,半开连接。
”半关”,即在一个方向上永久关闭通信连接,但不销毁套接字。这种状态下服务器再也不会读取任何数据,但它能继续向客户端发送剩余数据。shutdown()调用解决了服务器在遇到文件结束符之前永远读取数据,而客户端不会在套接字上进行完整的close(),也防止了运行很多recv()调用来接受服务器响应。下面是shutdown()的常用参数:
- SHUT_WR:表示调用方将不再向套接字写入数据,而通信对方也不会再读取任何数据并认为遇到了文件结束符。
- SHUT_RD:用来关闭接受方向的套接字流。
- SHUT_RDWR:将套接字两个方向的通信都关闭。