socket 介绍
socket的概念
socket又称套接字,应用程序可以通过它发送或接受数据,可对其进行像文件一样的打开、读写和关闭等操作。套接字由IP地址和端口号组成,形如(主机IP地址:端口号)
socket的分类
套接字主要有两种类型:流格式套接字和数据报格式套接字
1.流格式套接字:即Stream Sockets,在代码中用 SOCK_STREAM表示,基于TCP协议。
特点如下:
(1)数据在传输过程中不会消失
(2)数据是按照顺序传输的(较早发送的数据先到,较晚的后到)
(3)数据的发送和接受不是同步的(接收端只需根据自己的要求读取数据,不用在数据到达时立即读取数据)
例子:如浏览器使用的http协议就是面向连接的套接字
2.数据报格式套接字:即 Datagram Sockets,在代码中用SOCK_DGRAM表示,基于UDP协议。
特点如下:
(1)传输的数据可能丢失也可能损毁
(2)强调快速传输而非传输顺序
(3)数据的发送和接受是同步的
(4)限制每次传输的数据大小
例子:如qq视频聊天、语音通话,首先要保证通信的效率,减小延迟,而数据的正确性是次要的,即使丢失很小的一部分数据,视频和音频也能正常解析,最多出现噪点或杂音,不会对通信质量造成
实质的影响。
这里思考一个问题:为什么要产生一个新的socket呢?
当使用accept函数之后,服务器要继续在旧的socket上侦听是否有新的连接,所以使用新产生的socket和当前连接通信,旧socket继续侦听。当不再需要监听和接受更多的客户端连接的时候,可以关闭由socket()返回的套接字,而不会影响与客户端之间的通信。
TCP通信
TCP客户端
操作依次为:建立socket->连接远程socket->传输数据->接收数据->关闭连接
建立socket: client=socket.socket()
连接远程socket: client.connect((‘ip地址’,端口)),其中地址号和端口是一个元组
发送数据: client.send(data),传输的数据必须是字节流,所以对于字符串数据需要使用encode()
接收数据: client.recv(size),传输的数据是字节流,如果需要转成Unicode,需要使用decode()
关闭连接: client.close()
TCP服务器端
操作依次为:建立socket->绑定ip地址和端口->监听端口->等待连接->接收数据->传输数据->关闭连接
建立socket: server=socket.socket()(默认参数是socket.AF_INET和socket.SOCK_STREAM)
绑定ip地址和端口: server.bind((‘ip地址’,端口))
监听端口: server.listen()
等待连接: conn,addr=server.accept()
接收数据: conn.recv(size)
发送数据: conn.send(data)
关闭连接: server.close()
注意:
1.AF_INET是ipv4协议的套接字类型
2.服务器端要绑定ip地址和端口,而客户端的端口是随机分配的
3.客户端要在服务器端之后运行,否则connect会失败
4.代码中的ip地址可以换成localhost
遇到的问题:运行客户端程序报错,由于目标计算机积极拒绝,无法连接。
解决方案:保证服务器端程序和客户端程序运行在两个不同 IDLE上,不能由一个IDLE打开另一个IDLE。
问题思考:socket是同一计算机不同进程或不同计算机间的通信
运行结果
改进版本
前面的版本只能发送一次数据,发送完之后连接就会断开。
UDP通信
UDP通信不需要建立连接,只需要知道ip地址和端口号,就可以直接发送数据包,数据包能不能到达不能保证。
UDP客户端
UDP客户端和TCP客户端相比,不需要使用connect(),但需要指明socket类型,直接调用sendto()向服务器端发送数据,使用recvfrom 发送数据。
建立socket: client=socket.socket(type=socket.SOCK_DGRAM)
发送数据: client.sendto(data.encode(),(ip地址,端口))
接收数据: data,addr=client.recvfrom(1024)
UDP服务器端
UDP服务器端和TCP服务器端相比,不需要监听端口,不需要调用accept()方法等待连接,但需要指明socket类型。
建立socket: server=socket.socket(type=socket.SOCK_DGRAM)
绑定ip地址和端口: server.bind((ip地址,端口))
接收数据: data,addr=server.recvfrom(1024)
发送数据: server.sendto(data.encode(),addr)
运行结果