网络编程,是指让在不同的电脑上的软件能够进行数据传递,即进程之间的通信。
例如有:队列、同步(互斥锁、条件变量等)等,这些通信方式都是一台机器上不同进程之间的通信方式。
首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!
在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的。
其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。
这样利用ip地址、协议、端口
就可以标识网络的进程了,网络中的进程通信就可以利用这个标志与其它进程进行交互。
socket(简称套接字
)是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的。例如我们每天浏览网页、QQ 聊天、收发 email 等等。
在 Python 中使用 socket 模块的函数 socket :
socket.socket(AddressFamily, Type)
函数 socket.socket 创建一个 socket,返回该 socket 的描述符,该函数带有两个参数:
示例:
1 >>> import socket 2 >>> # 创建了一个TCP Socket 3 >>> tcp_s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 4 >>> tcp_s 5 <socket.socket fd=548, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0> 6 >>> # 创建了一个UDP Socket 7 >>> udp_s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 8 >>> udp_s 9 <socket.socket fd=620, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0>
UDP(User Data Protocol)——用户数据报协议,是一个无连接的简单的面向数据报的运输层协议。
UDP是面向消息的协议,通信时不需要建立连接,数据的传输自然是不可靠的,UDP一般用于多点通信和实时的数据业务,比如
UDP操作简单,而且仅需要较少的监护,因此通常用于局域网高可靠性的分散系统中client/server应用程序。例如视频会议系统,并不要求音频视频数据绝对的正确,只要保证连贯性就可以了,这种情况下显然使用UDP会更合理一些。
创建一个udp客户端程序的流程较为简单,具体步骤如下:
示例:
1 import socket 2 3 # 1. 创建套接字 4 udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 5 6 # 2. 准备接收方的地址 7 sendAddr = ("192.168.3.4", 8080) 8 9 # 3. 从键盘获取要发送的数据 10 sendData = input("请输入要发送的数据:") 11 12 # 4. 发送数据到指定的电脑上 13 udpSocket.send(sendData.encode(), sendAddr) 14 # encode():将 str 转为 bytes。 15 # decode():将 bytes 转为 str。如我们从网络或磁盘上读取了字节流,那么读到的数据就是 bytes。 16 17 # 5. 等待接收方发送过来的数据 18 recvData = udpSocket.recv(1024) # 1024表示本次接收数据的最大字节数 19 20 # 6. 显示对方发送的数据 21 print(recvData) 22 23 # 7. 关闭套接字 24 udpSocket.close()
执行效果:
会变的端口号:
重新运行多次脚本,然后在“网络调试助手”中,看到的现象如下:
一般情况下,在一台电脑上运行的网络程序会有很多,而各自用的端口号很多情况下也不知道。为了不与其他的网络程序占用同一个端口号,往往在编程中,udp的端口号一般不绑定。
但是如果需要做成一个接收方的程序的话,是需要绑定的。正如如果报警电话每天都在变,想必世界就会乱了。所以一般服务性的程序,往往需要一个固定的端口号,这就是所谓的端口绑定。
示例:
1 import socket 2 3 # 创建套接字 4 udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 5 6 # 绑定本地的相关信息,如果一个网络程序不绑定,系统就会随机分配 7 binAddr = ("", 7788) # IP地址和端口号,IP一般不用写,表示本机的任何一个IP 8 udpSocket.bind(binAddr) 9 10 # 接收方的地址信息 11 sendAddr = ("192.168.234.1", 8080) 12 13 # 发送数据 14 sendData = udpSocket.sendto("haha".encode(), sendAddr) 15 16 # 等待接收方发送过来的数据 17 recvData = udpSocket.recvfrom(1024) # 1024表示本次接收数据的最大字节数 18 19 # 显示对方发送的数据 20 print(recvData) 21 22 # 关闭套接字 23 udpSocket.close()
执行效果:
1 import socket 2 import time 3 4 5 # 创建套接字 6 udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 7 8 # 绑定本地的相关信息,如果一个网络程序不绑定,系统就会随机分配 9 binAddr = ("", 7788) # IP地址和端口号,IP一般不用写,表示本机的任何一个IP 10 udpSocket.bind(binAddr) 11 12 # 接收方的地址信息 13 sendAddr = ("192.168.234.1", 8080) 14 15 num = 1 16 17 def repeat(): 18 # 等待对方发送来的数据 19 recvData = udpSocket.recvfrom(1024) # 1024即本次接收数据的最大字节数 20 # 将接收的数据再次发送回去 21 udpSocket.sendto(recvData[0], sendAddr) 22 # 统计信息 23 print("已经将接收到的第%d个信息返回给对方,数据内容为:%s" % (num, recvData[0].decode())) 24 25 # 若不使用 try..except... 重试机制,而会出现“远程主机强制关闭连接”的报错 26 while True: 27 try: 28 repeat() 29 except: 30 repeat() 31 32 # 关闭套接字 33 udpSocket.close()
注意,若出现“ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接”:出现这种原因代表远程过于频繁,所以远程怀疑是恶意攻击。
可以使用 try...except...重试,在报错时重新调用该方法使其重新抓取,直至抓取成功。
执行效果:
模拟一个聊天室,显示所有接收到的数据。
1 import socket 2 import time 3 4 5 # 创建套接字 6 udpSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 7 8 # 绑定本地的相关信息,如果一个网络程序不绑定,系统就会随机分配 9 binAddr = ("", 7788) # IP地址和端口号,IP一般不用写,表示本机的任何一个IP 10 udpSocket.bind(binAddr) 11 12 # 接收方的地址信息 13 sendAddr = ("192.168.234.1", 8080) 14 15 16 def repeat(): 17 # 等待对方发送来的数据 18 recvData = udpSocket.recvfrom(1024) # 1024即本次接收数据的最大字节数 19 # 打印消息记录 20 print("【%s】%s:%s"%(time.ctime(), recvData[1][0], recvData[0].decode())) 21 22 23 while True: 24 try: 25 repeat() 26 except: 27 repeat() 28 29 30 # 关闭套接字 31 udpSocket.close()
执行效果:
原文:https://www.cnblogs.com/juno3550/p/12439910.html