socket在OSI模型中的位置
在所有的编程语言中socket和网络协议相关概念都是一样的,仅仅是在不同的编程语言中所用到
的函数名称不一样而已。
# 1.导入socket模块 import socket # 2.创建socket对象 函数定义如下 socket.socket(socket_family,socket_type,protocal=0) #socket_family 可以是 AF_UNIX 或 AF_INET。 #socket_type 可以是 SOCK_STREAM表示TCP协议 或 SOCK_DGRAM表示UDP协议。 #protocol 一般不填,默认值为 0。 # 2.1获取TCP 套接字 tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 或者 后面的参数都有默认值,可以不写,默认创建的是TCP协议socket tcpSock = socket.socket() # 2.2获取udp/ip套接字 udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) #由于 socket 模块中有太多的属性。可以使用'from module import *'语句。使用 'from socket import *',把 socket 模块里的所有属性都导入当前命名空间里了,这样能大幅减短代码。 #例如:tcpSock = socket(AF_INET, SOCK_STREAM)
不管是服务器还是客户端使用的都是socket对象。
socket 是已经被封装好的接口模块,我们只要根据通讯的流程来编写代码即可。
在图中可以看出,划分为两块区域,服务器和客户端。
首先我们要跟着流程创建一个通讯设备,第二部获取ip地址和端口号,监听设备,然后等待
客户端的连接请求发送过来,到请求发送过来之前不会向下执行,accept 起阻塞的作用,当有客户端给服务器发起请求之后,才会进行对信息进行读取,回应数据,并与客户端进行数据数据之间交互。当客户端与服务器完成四次挥手之后正常结束进程。
tcp 的通讯流程与接打电话的过程十分相似,当只有接收到电话请求时才会进行通话。
拥有手机 == socket()
装手机卡 == 获得自己的ip和端口号 bind()
打开手机允许别人给我打电话,规定一次接一个电话 == listen()
电话来了接听电话 == accept()
开始对话 == send()发出信息,recv()接收信息
import socket ip_port=('127.0.0.1',9000) #电话卡 BUFSIZE=1024 #收发消息的尺寸 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机 s.bind(ip_port) #手机插卡 s.listen(5) #手机待机 conn,addr=s.accept() #手机接电话 print('接到来自%s的电话' %addr[0]) msg=conn.recv(BUFSIZE) #听消息,听话 print(msg,type(msg)) conn.send(msg.upper()) #发消息,说话 conn.close() #挂电话 s.close() #手机关机 服务端
import socket ip_port=('127.0.0.1',9000) BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect_ex(ip_port) #拨电话 s.send('linhaifeng nb'.encode('utf-8')) #发消息,说话(只能发送字节类型) feedback=s.recv(BUFSIZE) #收消息,听话 print(feedback.decode('utf-8')) s.close() #挂电话
在tcp中必须先启动服务器后再启动客户端,否则客户端由于无法连接到服务器,会直接报错。
在上边的代码中只是实现了,单次信息的交互,在实际生活中,信息的交互并不是一次性的,所以要给信息交互的代码加上循环。
import socket ip_port=('127.0.0.1',8081)#电话卡 BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机 s.bind(ip_port) #手机插卡 s.listen(5) #手机待机 while True: #新增接收链接循环,可以不停的接电话 conn,addr=s.accept() #手机接电话 # print(conn) # print(addr) print('接到来自%s的电话' %addr[0]) while True: #新增通信循环,可以不断的通信,收发消息 msg=conn.recv(BUFSIZE) #听消息,听话 print(msg,type(msg)) conn.send(msg.upper()) #发消息,说话 conn.close() #挂电话 s.close() #手机关机
import socket ip_port=('127.0.0.1',8081) BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.connect_ex(ip_port) #拨电话 while True: #新增通信循环,客户端可以不断发收消息 msg=input('>>: ').strip() if len(msg) == 0:continue s.send(msg.encode('utf-8')) #发消息,说话(只能发送字节类型) feedback=s.recv(BUFSIZE) #收消息,听话 print(feedback.decode('utf-8')) s.close() #挂电话
在调试过程中,可能会遇见以下错误:
问题发生原因:
1.可能是由于你已经启动了服务器程序,却又再次启动了服务器程序,同一个端口不能被多个进程使用导致!
2.三次握手或四次挥手时,发生了异常导致对方程序已经结束而服务器任然处于time_wait状态导致!
3.在高并发的场景下,由于链接的客户端太多,也会产生大量处于time_wait状态的链接
解决的方案:
第1种原因,很简单关闭之前运行的服务器即可
第2,3中原因导致的问题,有两种解决方案:
2.强行关闭链接
(发生错误演示,运行上面的改进版的服务器与客户端,链接成功后直接停止客户端程序)
当客服端与服务器链接成功后,如果一方没有执行close,而是直接强行终止程序(或是遇到异常被迫终止),都会导致另一方发送问题,
在windows下,接收数据的一方在recv函数处将抛出异常
#### 2.强行关闭链接 (发生错误演示,运行上面的改进版的服务器与客户端,链接成功后直接停止客户端程序) 当客服端与服务器链接成功后,如果一方没有执行close,而是直接强行终止程序(或是遇到异常被迫终止),都会导致另一方发送问题, 在windows下,接收数据的一方在recv函数处将抛出异常 ```python Traceback (most recent call last): File "C:/Users/jerry/PycharmProjects/untitled/TCP/server.py", line 9, in <module> conn.recv(1024) ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。 ``` linux下,不会抛出异常会导致接收数据的一方,recv方法不断的收到空消息,造成死循环 要使应用程序能够在不同平台正常工作,那需要分别处理这两个问题 解决方案如下: ```python import socket ip_port=('127.0.0.1',8081) BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.bind(ip_port) s.listen(5) while True: conn,addr=s.accept() while True: try: msg=conn.recv(BUFSIZE) #linux不会抛出异常,会接收到空消息,这里加以判断 if not msg: conn.close() break print(msg,type(msg)) conn.send(msg.upper()) except ConnectionResetError: #只要异常发生则意味着对方以及关闭了,服务器也相应的关闭该链接 conn.close() break conn.close() s.close() ``` 至此TCP通讯模板程序就完成了,可以不断的接收新的链接,不断的收发消息,并且不会因为客户端强制关闭而异常退出!
linux下,不会抛出异常会导致接收数据的一方,recv方法不断的收到空消息,造成死循环
要使应用程序能够在不同平台正常工作,那需要分别处理这两个问题
解决方案如下:
#### 2.强行关闭链接 (发生错误演示,运行上面的改进版的服务器与客户端,链接成功后直接停止客户端程序) 当客服端与服务器链接成功后,如果一方没有执行close,而是直接强行终止程序(或是遇到异常被迫终止),都会导致另一方发送问题, 在windows下,接收数据的一方在recv函数处将抛出异常 ```python Traceback (most recent call last): File "C:/Users/jerry/PycharmProjects/untitled/TCP/server.py", line 9, in <module> conn.recv(1024) ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接。 ``` linux下,不会抛出异常会导致接收数据的一方,recv方法不断的收到空消息,造成死循环 要使应用程序能够在不同平台正常工作,那需要分别处理这两个问题 解决方案如下: ```python import socket ip_port=('127.0.0.1',8081) BUFSIZE=1024 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) s.bind(ip_port) s.listen(5) while True: conn,addr=s.accept() while True: try: msg=conn.recv(BUFSIZE) #linux不会抛出异常,会接收到空消息,这里加以判断 if not msg: conn.close() break print(msg,type(msg)) conn.send(msg.upper()) except ConnectionResetError: #只要异常发生则意味着对方以及关闭了,服务器也相应的关闭该链接 conn.close() break conn.close() s.close() ``` 至此TCP通讯模板程序就完成了,可以不断的接收新的链接,不断的收发消息,并且不会因为客户端强制关闭而异常退出!
至此TCP通讯模板程序就完成了,可以不断的接收新的链接,不断的收发消息,并且不会因为客户端强制关闭而异常退出!