了解TCP,UDP
UDP:使用IP地址和端口号进行标识,从此将数据包发送至目标地址
TCP:基于字节流的传输层通信协议
初识套接字
套接字用(IP地址:端口号)表示。在python中使用socket.socket对象来方便的表示套接字。该对象内部维护了操作系统标识套接字的整数。
再识socket.socket对象
在有了套接字后,我们开始继续了解socket.socket对象。就像传统的寄信一样,除了要知道我们递送信息的地址,还要有信息的实体的内容,发送和接收信息的动作。而这一切都可以通过操作这个对象来完成。
首先是发送信息
sock.sendto(data,address)
然后是接收信息
data,address = sock.recvform()
UDP
下面是一个简单的回复客户端发送字符长度的服务器端。
def server(interface,port):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((interface, port)) #绑定接口等
print('Listening at {}'.format(sock.getsockname()))
while True: #不断接收信息
data, address = sock.recvfrom(MAX_BYTES)
if random.random() < 0.5:
print('pretending drop')
continue #模拟丢包
text = data.decode('ascii')
print('The client at {} says {!r}'.format(address, text)) #打印客户端说了啥
text = 'Your data was {} bytes long'.format(len(data))
data = text.encode('ascii')
sock.sendto(data, address) #发送信息
首先我们注意到的是socket.socket本身也有很多参数,地址族,套接字类型。感觉有点麻烦啊,然而实际的编程过程中是不会出现像AF_INET等真实的符号。而是可以使用一个强大的工具getaddrinfo()来操作涉及地址的工作。比如说
infolist = socket.getaddrinfo('127.0.0.1','smtp',0,socket.SOCK_STREAM,0)
得到的infolist是一个元组列表,可以通过操作这个列表得到我们目标的所要构造socket.socket的参数。
客户端的写法和服务器端类似就不再赘述了。客户端最重要的是进行异常处理,比如丢包和服务器端识别。丢包处理我们可能会很自然的想到超过一定时间没有收到回复就放弃等待或者是重新发送。然而面对判断发送信息方的真实身份时,udp协议就不太好使了。
可以看到的是这样的写法可以是任何人都可以发送给服务器端,相似的任何人也可以构造数据包来发送给客户端。在服务器端响应信息之前,第三方构造的信息发送到了客户端,如果没有判断是否是真正的服务器端发送的信息,可能就会把第三方的信息当作是服务器的响应信息了。
处理这种问题比较自然的想法是直接判断响应方的IP和发送目的方的IP是否相等,然而事情并没有这样简单,伪造数据包可以成功通过这样的地址检测
TCP
又是套接字:
tcp实际上包含了两种不同的套接字,“被动”监听套接字,“主动”连接套接字。顾名思义,一个拿来的监听,一个拿来连接。为什么tcp的事情这么多呢?因为tcp基于字节流的传输层通信协议。监听套接字用来维护“套接字名”,指示操作系统首先使用哪个特定的tcp端口号来接收请求连接。
def server(interface, port):
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.bind((interface, port))
sock.listen(1) #监听,参数是指等待连接的最大数
print('listening at :',sock.getsockname())
while True:
print('Waiting to accept a new connection')
sc,sockname = sock.accept() #等待客户端,然后返回新的连接套接字sc
print('We have accepted a connection from', sockname)
message = recv(sc)
print('client says:', repr(message))
sc.sendall(b'Farewell, client')
sc.close() #关闭连接
print('reply sent,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 = recv(sock)
print('server reply:', repr(reply))
sock.close()