网络编程基础
一、 IP地址
IP地址的概念:IP地址就是表示网络中的设备的一个地址
1.1 IP地址的表现形式
- IP地址分为两类:IPv4(十进制组成)和IPv6
1.2 IP地址的作用
IP地址的作用是表示网络中共唯一的一台设备的,也就是说通过IP地址能过找到网络中某台设备
- IP地址的效果图
1.3 查看IP地址
- Linux和mac OS使用 ifconfig 这个命令
- Windows 使用 ipconfig 这个命令
1.4 检查网络是否正常
- 检查网络是否正常使用 ping 命令:
ping ip地址
- 检查是否能上公网:
ping www.baidu.com
- 检查是否再同一个局域网内
ping 当前局域网的ip地址
- 检查本地网卡是否正常:
ping 127.0.0.0.1
二、端口和端口号的介绍
2.1 端口
端口是传输数据的通道,好比教室的门,是数据传输必经之路。
- 效果图
2.2 什么是端口号
操作系统为了统一管理这么多端口,就对端口进行了编号,这就是端口号,端口号其实就是一个数字,一共有65536个。
通信流程:通过ip地址找到对应的设备,通过端口号找到对应的端口,然后通过端口把数据传输给应用程序
2.21端口号的分类
- 知名端口号:范围0-1023
这些端口固定分配给一些服务,比如21端口发呢配给FTP(文件传输协议)服务,25端口分配给SMTP(简单邮件传输协议)服务,80分配给HTTP服务 - 动态端口号:范围(1024-65535)
三、TCP介绍
3.1 网络应用程序之间的通信流程
数据在发送之前需要选中一个对应的传输协议,保证程序之间按照指定的传输规则进行数据的通信。
3.2 TCP的概念
TCP简称传输控制协议,它是一种面向连接的、可靠的、基于字节流的传输层通信协议。
- TCP通信步骤
1、创建连接
2、传输数据
3、关闭连接
3.3 TCP的特点
1、面向连接
- 通信双方必须先建立连接才能进行数据的传输,数据阐述完成后,双方必须断开此连接,以释放系统资源
2、可靠传输
- TCP采用发送应答机智
- 超时重传
- 错误校验
- 流量控制和阻塞管理
四、socket的介绍
4.1 socket的概念
socket(套接字)是进程之间通信的一个工具,进程之间进行网络通信需要基于socket。
socket效果图:
4.2 socket的作用
负责进程直接按的网络数据传输,数据的搬运工
五、TCP网络应用程序开发流程
TCP网络应用程序开放分为:
- TCP客户端程序开发
- TCP服务端程序开发
5.1 TCP客户端程序开发流程的介绍
步骤说明:
1、创建客户端套接字对象
2、和服务端套接字建立连接
3、发送数据
4、接收数据
5、关闭客户端套接字
5.2 TCP服务端程序开发流程的介绍
步骤说明:
1、创建服务端套接字对象
2、绑定端口号
3、设置监听
4、等待接收客户端的连接请求
5、接收数据
6、发送数据
7、关闭套接字
六、TCP通信
6.1 TCP客户端程序开发
6.11 socket 类的介绍
- 导入socket模块:
import socket
- 创建客户端socket对象:
socket.socket(AddressFamily,Type)
参数说明:
AddressFamily:表示IP地址类型,分为IPv4和IPv6
Type:表示传输协议类型
方法说明:
- connect(host,port):表示和服务端套接字建立连接
- send(data)表示发送数据,data是二进制数据
- recv(buffersize):表示接收数据,buffersize是每次接收数据的长度
工具:网络调试助手模拟服务端
客户端代码:
import socket
if __name__ == '__main__':
#1.创建tcp客户端套接字
#AF_INET:IPv4地址类型
#SOCK_STREAM:TCP传输协议类型
tcp_client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#2.和服务端建立连接
tcp_client_socket.connect(("172.18.1.27",9090))
send_content = "你好,我是客户端小白!!!"
#对字符串进程编码成为二进制数据
# send_data= send_content.encode("utf-8")
send_data= send_content.encode("gbk")
#3.发送数据到服务端
#windows里面的网络调试助手使用的gbk编码
#linux 里面的网络台哦是助手使用的utf-8编码
tcp_client_socket.send(send_data)
#4.接收服务端的数据
recv_data = tcp_client_socket.recv(1024)
#对二进制数据进行解码
recv_content = recv_data.decode("gbk")
print("接收服务端的数据为:",recv_content)
#5.关闭套接字
tcp_client_socket.close()
网络调试助手下载地址:http://free.cmsoft.cn/download/cmsoft/assistant/netassist5.0.2.zip
6.2 TCP服务端程序开发示例
6.21 socket 类的介绍
- 导入socket模块:
import socket
- 创建服务端socket对象:
socket.socket(AddressFamily,Type)
参数说明:
AddressFamily:表示IP地址类型,分为IPv4和IPv6
Type:表示传输协议类型
方法说明:
- bind(host,port):表示绑定端口号,port是端口号,ip地址一般不指定,表示本机任何一个IP地址都可以
- listen(backlog):表示设置监听,backlog参数表示等待建立连接的个数
- accept():表示等待接收客户端的连接请求
- send(data)表示发送数据,data是二进制数据
- recv(buffersize):表示接收数据,buffersize是每次接收数据的长度
服务端代码
import socket
if __name__ == '__main__':
#1、创建tcp服务端套接字
#AF_INET:ipv4,AF_INET6:ipv6
tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#2、绑定端口号
#第一个参数表示ip地址,
#第二个参数表示端口号
tcp_server_socket.bind(("",9090))
#3、设置监听
#128:表示最大等待建立连接的个数
tcp_server_socket.listen(128)
#4、等待接收客户端的连接请求
#注意点:每次当客户端和服务端建立连接成功会返回一个新的套接字
# result = tcp_server_socket.accept()
new_client,ip_port = tcp_server_socket.accept()
# tcp_server_socket只负责等待接收客户端的来凝结请求,收发消息不适用该套接字
print("客户端的ip和端口号为:",ip_port)
#5、接收客户端的数据
#收发消息都使用返回的这个新套接字
recv_data = new_client.recv(1024)
#对二进制数据进行解码变为字符串
recv_content = recv_data.decode("gbk")
print("接收客户端的数据为:",recv_content)
#6、发送数据到客户端
send_content = "问题正在处理中。。。"
#对字符串进行编码
send_data = send_content.encode("gbk")
new_client.send(send_data)
#关闭服务与客户端套接字,表示和客户端终止通信
#7、关闭服务端套接字, 表示服务端以后不再等待接收客户端的连接请求
tcp_server_socket.close()
6.3 解决端口号复用问题
当客户端和服务端建立连接后,服务端程序退出后端口号不会理解释放,需要等待大概1-2分钟
解决办法:
- 更换端口号
- 设置端口号复用,让服务端程序退出后端口号立即释放
1): 表示当前套接字
2): 设置端口号复用
3): 设置端口号复用选项对应的值
tcp_server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,True)
七、案例
7.1 tcp服务端服务于多个客户端
多人版tcp服务端程序代码
import socket
import threading
# 处理客户端请求的认为
def handle_client_request(ip_port,new_client):
print("客户端的ip和端口号为:", ip_port)
# 5、接收客户端的数据
# 收发消息都使用返回的这个新套接字
#循环接收客客户端的消息
while True:
recv_data = new_client.recv(1024)
if recv_data:
print("接收到的数据长度为:",len(recv_data))
# 对二进制数据进行解码变为字符串
recv_content = recv_data.decode("gbk")
print("接收客户端的数据为:", recv_content,ip_port)
# 6、发送数据到客户端
send_content = "问题正在处理中。。。"
# 对字符串进行编码
send_data = send_content.encode("gbk")
new_client.send(send_data)
else:
# 客户端下线了
print("客户端下线了",ip_port)
break
# 关闭服务与客户端套接字,表示和客户端终止通信
new_client.close()
if __name__ == '__main__':
#1、创建tcp服务端套接字
#AF_INET:ipv4,AF_INET6:ipv6
tcp_server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#设置端口号复用,让服务端程序退出后端口号立即释放
# 1): 表示当前套接字
# 2): 设置端口号复用
# 3): 设置端口号复用选项对应的值
tcp_server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True)
#2、绑定端口号
#第一个参数表示ip地址,
#第二个参数表示端口号
tcp_server_socket.bind(("",9090))
#3、设置监听
#128:表示最大等待建立连接的个数
tcp_server_socket.listen(128)
#4、等待接收客户端的连接请求
#注意点:每次当客户端和服务端建立连接成功会返回一个新的套接字
# result = tcp_server_socket.accept()
#循环等待客户端的连接请求
while True:
new_client,ip_port = tcp_server_socket.accept()
# tcp_server_socket只负责等待接收客户端的来凝结请求,收发消息不适用该套接字
# 当客户端和服务端建立连接成功创建子线程,让子线程专门负责接收客户端的消息
sub_thread = threading.Thread(target=handle_client_request,args=(ip_port,new_client))
#设置守护主线程,主线程退出子线程直接销毁
sub_thread.setDaemon(True)
sub_thread.start()
#7、关闭服务端套接字, 表示服务端以后不再等待接收客户端的连接请求
# 因为服务端的程序需要一直运行,所以关闭服务端套接字的代码可以省略不写
tcp_server_socket.close()
八、socket之send和recv原理剖析
8.1 TCP socket的发送和接收缓冲区
当创建一个TCP socke对象的时候会有一个发送缓冲区和一个接收缓冲区,这个发送和接收缓冲区指的是内存中的一个空间。
8.2 send原理剖析
必须通过网卡发送数据,需要调用操作系统接口,应用程序把发送的数据先写入发送缓冲区,由操作系统控制网卡把发送缓冲区的数据发送到服务端网卡
8.3 recv原理剖析
软件无法通过网卡接收数据,需要调用操作系统接口,由操作系统通过网卡接收数据,把接收的数据写入到接收缓冲区,应用程序再从接口缓存区获取客户端发送的数据
8.4 send和recv原理剖析图
说明:
- 发送数据是发送到发送缓冲区
- 接收数据是从接收缓冲区获取