网络编程
IP地址:网络中的唯一识别地址,好比人的身份证。
局域网的ip地址可以复用,不同的局域网内的ip地址可以相同,但是对于广域网来说,所有的ip地址都是唯一的。
IP地址分类
IPV4:目前最大的IP地址格式。255.255.255.255
IPV6:以后主流的IP地址格式。号称让地球上的每一粒沙子都有IP地址
查看IP地址的方式:windows:命令行 > ipconfig
linux: 终端 > ifconfig
查看网络详细信息:ping www.baidu.com
访问ip地址也是可以访问网站的,除非对方禁止掉,或者它的ip地址比较特殊,需要加一些端口号。
端口
定义:应用程序之间的通信通道。
端口号的作用:区分不同的应用程序
分类: 知名端口:0-1023
动态端口号:1024-65536(自己开发的时候用)
TCP
稳定的、可靠的、不会丢失数据的通信传输协议,需要对方应答之后才能开始通信。
可靠传输:
1.面向连接
通信双方必须先建立好连接才能进行数据的传输,数据传输完成后,双方必须断开此连接,以释放系统资源。
2.可靠传输
TCP 采用发送应答机制
超时重传
错误校验
流量控制和阻塞管理
UDP
udp不稳定不可靠的通信协议,传输速度快,一般用于做广播。
socket(套接字)
作用:模块,只要是搞网络通信的,都要用这个模块。把我们的tcp、ip、端口号拿来使用。
总结
ip地址相当于每个电脑的详细路由地址,端口号相当于指定了哪个程序,TCP相当于通信双方之间的语言(就像人交流一样,用相同的语言才能听懂)
===================================================
TCP客户端的开发:
示例代码:
import socket
if __name__ == '__main__':
# 1.创建客户端套接字对象
# AF_INET表示的是使用IPV4 SOCKET_STREAM表示的是使用TCP协议
tcp_client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2.和服务端套接字建立连接
# connect 参数只能有一个,而且必须是元组格式的
tcp_client.connect(("192.168.88.133", 8082))
# 3.发送数据
send_date = input("请输入你想说的话:").encode("utf-8")
tcp_client.send(send_date)
# 4.接收数据
recv_date = tcp_client.recv(1024).decode("utf-8")
print("服务端:", recv_date)
# 5.关闭客户端套接字
tcp_client.close()
注意:读者在运行代码时建议百度下载网络调试助手调试看是否能够通信
windows编码要用"gbk",linux编码要用"utf-8"
===================================================
TCP服务端的开发
示例代码:
import socket
if __name__ == '__main__':
# 创建服务端端套接字对象
tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口复用
# SOL_SOCKET 安全级别:socket级别
# SO_REUSEPORT 是否设置端口重复使用 True 表示确认重复使用
tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, True)
# 绑定端口号
# 这里和connect是一样的,bind里面的参数也必须是元组,""代表本机的任意一个ip地址都可以
tcp_server.bind(("", 8087))
# 设置监听的最大连接数为8
tcp_server.listen(8)
# 等待接受客户端的连接请求
# 这里拆包得到的是一个新的套接字对象和一个客户端的ip和端口的元组,这个新的套接字是用来和客户端传输数据的,每连接一个客户端,就会产生一个新的套接字对象用来为那个客户端专门服务,直至结束通信并关闭套接字。
new_tcp_server, client_port = tcp_server.accept()
# 接收数据
recv_date = new_tcp_server.recv(1024)
print(f"收到的数据是:{recv_date.decode('utf-8')}")
print("接收到的客户端地址为:ip: %s,端口号port: %d" % client_port)
# 发送数据
send_date = "连接成功,很高兴为您服务。".encode("utf-8")
new_tcp_server.send(send_date)
# 关闭套接字
# 先关闭为每个客户端提供服务的套接字
new_tcp_server.close()
# 再关闭服务端的套接字
tcp_server.close()
注意:端口复用一般用于学习测试用的,在企业里端口一般不用变化,而且企业级服务器一般是不关机的
以上就是tcp服务器开发的主要流程,每个步骤都有详细的说明,读者可根据源码和注释进行学习。读者不必去深究socket模块的底层原理,只需要懂如何使用就好了。
TCP注意点:
1.当 TCP 客户端程序想要和 TCP 服务端程序进行通信的时候必须要先建立连接
2.TCP 客户端程序一般不需要绑定端口号,因为客户端是主动发起建立连接的。
3.TCP 服务端程序必须绑定端口号,否则客户端找不到这个 TCP 服务端程序。
4.listen 后的套接字是被动套接字,只负责接收新的客户端的连接请求,不能收发消息。
5.当 TCP 客户端程序和 TCP 服务端程序连接成功后, TCP 服务器端程序会产生一个新的套接字,收发客户端消息使用该套接字。
6.关闭 accept 返回的套接字意味着和这个客户端已经通信完毕。
7.关闭 listen 后的套接字意味着服务端的套接字关闭了,会导致新的客户端不能连接服务端,但是之前已经接成功的客户端还能正常通信。(重点)原因:因为他们之间的通信是创建了一个新的套接字,不是用的服务器的那个套接字,所以关闭服务器的套接字不能中止
他们的通信
代码示例:
new_tcp_server, client_port = tcp_server.accept()
tcp_server.close()
# 将关闭服务端的套接字放在accept方法后面
8.如何判断客户端是否下线:判断接收到的数据
if len(recv_date) > 0:
用户在线要执行的代码
else:
print("用户已下线")
break
===============================================================
TCP多任务编程
代码示例:
import socket
import threading
def client_handler(new_tcp_server):
"""
把跟客户端有关的代码都创建成一个线程函数
"""
# 接收数据
while True:
recv_date = new_tcp_server.recv(1024)
if len(recv_date) > 0:
print(f"收到的数据是:{recv_date.decode('utf-8')}")
# 发送数据
send_date = "连接成功,很高兴为您服务。".encode("utf-8")
new_tcp_server.send(send_date)
else:
print("客户端已经下线")
# 关闭套接字
new_tcp_server.close()
break
if __name__ == '__main__':
# 创建服务端端套接字对象
tcp_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 设置端口复用
tcp_server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, True)
# 绑定端口号
tcp_server.bind(("", 8080))
# 设置监听的最大连接数为8
tcp_server.listen(8)
# 创建多线程,用while True 能建立未知个线程,有多少个客户端链接成功,就创建个多少个专门为该客户端服务的套接字,也创建多少个线程。
while True:
# 等待接受客户端的连接请求
new_tcp_server, client_port = tcp_server.accept()
ip, port = client_port
print("接收到的客户端地址为:ip: %s,端口号port: %d" % (ip, port))
client_hand = threading.Thread(target=client_handler, args=(new_tcp_server,))
client_hand.setDaemon(True) #设置守护主线程,防止关闭服务器之后,已经建立的tcp链接还能继续通信
client_hand.start()
因为udp传输不可靠,数据易丢失,用处不多,所以这里不做讲解,读者若想了解,请移步至:UDP编程“廖雪峰老师的教程”