python基础知识已经学习结束,今天开始将进行网络编程的学习,加油,you are great!
1.网络编程基本概念
1.1网络通信概述
网络是辅助双方或者多方连接在一起的工具。
使用网络的目的:联通多方然后进行通信用的,即把数据从一方传递到另外一方。
为了让在不同的电脑上运行的软件,之间能够互相传递数据,就需要使用网络。
总结:
1. 使用网络可以将多方连接在一起,然后进行数据传递。
2.所谓的网络编程,让在不同电脑上运行的软件进行数据传递,即进程之间的通信。
1.2 IP地址
1.什么是地址?
地址是用来标记地点的。
2.什么叫IP地址及IP地址的作用?
用来标记网络上的一台电脑。类似192.168.243.1,在本地局域网上是唯一的。
1.3Linux、Windows查看网卡信息
查看网卡信息
linux: ifconfig
windows:ipconfig/ 也可以直接在界面看
关闭网卡:
Linux:sudo ifconfig ens40 down (ens40为命令框左侧的网卡名称)
开启网卡:
Linux:sudo ifconfig ens40 up (ens40为命令框左侧的网卡名称)
1.4 IP地址的分类IPv4和IPv6介绍
IPV4: 一串数字xxx.xxx.xxx.xxx,共有256*256*256*256
ipv6:量级很大,号称地球上的每一粒沙都可以有一个IP,目前还在发展中。
每一个IP地址包括两个部分,网络地址和主机地址。
IP地址分成5类
A类IP:前1个数字是网络号,后3个数字是主机号(其中0和255不可用)
B类IP:前两个数字是网络号,后两个数字是主机号。
C类IP:前三个数字是网络号,后1个数字是主机号
D类IP:用于多点广播。第一个字节是1110开头。
E类IP:保留地址。
1.5端口
IP标示主机,端口来表示主机上的程序。
1.6端口分类:知名端口、动态端口
知名端口:大家都默认使用的端口,范围从0~1023
如80端口分配给http服务
21端口分配给FTP
一个程序 要用知名端口,一定要有root权限。
动态端口:范围是1024~65535
动态分配是指当一个系统或应用程序需要网络通信时,它向主机申请一个端口,主机从
可用的端口中分配一个供它使用,当程序关闭时,同时释放所占用的端口号。
怎么查看端口号?
用“netstat -an"查看端口状态
lsof -i[tcp/udp]:2425
1.7socket介绍
一台电脑上可以通过进程号pid唯一标识一个进程,网络中的进程则需要IP,协议,端口号一起来标识。
socket(简称套接字)是进程间通信的一种方式,它与其他进程间通信的一个主要不同:
它能实现不同主机间的进程间通信,网络上各种各样的服务大多是基于socket来完成通信的。
套接字的使用流程与文件的使用流程类似
创建套接字;使用套接字收/发数据;关闭套接字
python中使用socket模块的函数socket完成。
import socket socket.socket(AddressFamily,Type)
socket函数参数解释
AddressFamily:可以选择AF_INET(就是IPv4)或者AF_UNIX
Type:套接字类型,可以是SOCK_STREAM(流式套接字,主要用于TCP协议)或者SOCK_DGRAM(数据套接字,主要用于UDP协议)
2.UDP
2.1 UDP发送数据demo
通过python的socket模块创建套接字,使用udp发送数据。
import socket def main(): # 创建一个udp套接字 udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 可以使用套接字收发数据 # udp_socket.sendto("hahahah", 对方的ip以及port) udp_socket.sendto(b"hahahah------1----", ("192.168.33.53", 8080)) # 关闭套接字 udp_socket.close() if __name__ == "__main__": main()
桥接:在这里的作用,主要是将两个IP的网络号变成一样,只有网络号一样,才能进行通信。
选择桥接之后,可以在命令框里输入sudo dhclient(请求别人分享一个IP)
2.2关闭VMware的虚拟网卡
开启vm,会有虚拟IP产生,如果要进行网络调试的话,可以将虚拟网卡进行禁用。
2.3UDP发送数据的强调
主要是python的语法错误,如缩进等,此处省略。
2.4运行python程序以及python交互模式,encode编码,发送udp的练习
1.python3要用sendto发送数据,必须是bytes类型,所以在字符串前面加一个b
发送任意数据
import socket def main(): # 创建一个udp套接字 udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 从键盘获取数据 send_data = input("请输入要发送的数据:") # 可以使用套接字收发数据 # udp_socket.sendto("hahahah", 对方的ip以及port) # udp_socket.sendto(b"hahahah------1----", ("192.168.33.53", 8080)) udp_socket.sendto(send_data.encode("utf-8"), ("192.168.33.53", 8080)) # 关闭套接字 udp_socket.close() if __name__ == "__main__": main()
带有退出功能的循环发送:
import socket def main(): # 创建一个udp套接字 udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) while True: # 从键盘获取数据 send_data = input("请输入要发送的数据:") # 如果输入的数据是exit,那么就退出程序 if send_data == "exit": break # 可以使用套接字收发数据 # udp_socket.sendto("hahahah", 对方的ip以及port) # udp_socket.sendto(b"hahahah------1----", ("192.168.33.53", 8080)) udp_socket.sendto(send_data.encode("utf-8"), ("192.168.33.53", 8080)) # 关闭套接字 udp_socket.close() if __name__ == "__main__": main()
2.5接收udp数据
循环接收并显示数据
import socket def main(): # 1. 创建套接字 udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 2. 绑定一个本地信息 localaddr = ("", 7788) udp_socket.bind(localaddr) # 必须绑定自己电脑的ip以及port,其他的不行 # 3. 接收数据 while True: recv_data = udp_socket.recvfrom(1024) # recv_data这个变量中存储的是一个元组(接收到的数据,(发送方的ip, port)) recv_msg = recv_data[0] # 存储接收的数据 send_addr = recv_data[1] # 存储发送方的地址信息 # 4. 打印接收到的数据 # print(recv_data) # print("%s:%s" % (str(send_addr), recv_msg.decode("utf-8"))) print("%s:%s" % (str(send_addr), recv_msg.decode("gbk"))) # 5. 关闭套接字 udp_socket.close() if __name__ == "__main__": main()
2.6端口绑定的问题
绑定端口后在循环发送,否则每次打开都是用系统随机端口进行发送
import socket def main(): # 创建一个udp套接字 udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 绑定本地信息 udp_socket.bind(("", 7890)) while True: # 从键盘获取数据 send_data = input("请输入要发送的数据:") # 可以使用套接字收发数据 # udp_socket.sendto("hahahah", 对方的ip以及port) # udp_socket.sendto(b"hahahah------1----", ("192.168.33.53", 8080)) udp_socket.sendto(send_data.encode("utf-8"), ("192.168.33.53", 8080)) # 关闭套接字 udp_socket.close() if __name__ == "__main__": main()
2.7网络中的重要概念复习
1.IP类补充——私有ip
只能在局域网使用,不能在公网使用的IP。
A类:10.0.0.0~10.255.255.255
B类:172.16.00~172.31.255.255
C类:192.168.0.0~192.168.255.255
2.8输入对方IP,port,全双工,半双工,单工等
同一个套接字既可以收也可以发
import socket def main(): # 创建一个udp套接字 udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 获取对方的ip/port dest_ip = input("请输入对方的ip:") dest_port = int(input("请输入对方的port:")) # 从键盘获取数据 send_data = input("请输入要发送的数据:") # 可以使用套接字收发数据 # udp_socket.sendto("hahahah", 对方的ip以及port) # udp_socket.sendto(b"hahahah------1----", ("192.168.33.53", 8080)) # udp_socket.sendto(send_data.encode("utf-8"), ("192.168.33.53", 8080)) udp_socket.sendto(send_data.encode("utf-8"), (dest_ip, dest_port)) # 接收回送过来的数据 recv_data = udp_socket.recvfrom(1024) # 套接字是一个可以同时 收发数据 print(recv_data) # 关闭套接字 udp_socket.close() if __name__ == "__main__": main()
recvfrom在没有收到消息时,会出现堵塞。
单工:只能单向,要么发,要么只能收。
半双工:可以发,也可以收,但是同一时刻只能发或者收。
全双工:同一时刻既可以发也可以收。socket就属于全双工。
2.9案例:udp聊天器
import socket def send_msg(udp_socket): """发送消息""" # 获取要发送的内容 dest_ip = input("请输入对方的ip:") dest_port = int(input("请输入对方的port:")) send_data = input("请输入要发送的消息:") udp_socket.sendto(send_data.encode("utf-8"), (dest_ip, dest_port)) def recv_msg(udp_socket): """接收数据""" recv_data = udp_socket.recvfrom(1024) print("%s:%s" % (str(recv_data[1]), recv_data[0].decode("utf-8"))) def main(): # 创建套接字 udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 绑定信息 udp_socket.bind(("", 7788)) # 循环循环来进行处理事情 while True: print("-----xxx聊天器-----") print("1:发送消息") print("2:接收消息") print("0:退出系统") op = input("请输入功能:") if op == "1": # 发送 send_msg(udp_socket) elif op == "2": # 接收并显示 recv_msg(udp_socket) elif op == "0": break else: print("输入有误请重新输入...") if __name__ == "__main__": main()
3.TCP
3.1 tcp介绍
tcp就是传输控制协议。
tcp通信需要经过创建链接、数据传送、终止链接三个步骤。
tcp通信模型中,在通信开始之前,一定要先建立相关的链接,才能发送数据,类似于“打电话”。
udp通信模型中,在通信开始之前,不需要建立相关的链接,只需要发送数据即可,类似生活中的“写信”。不太安全。
3.1.1TCP特点
a. 面向链接(这种连接是一对一的tcp不适用于广播的应用程序,基于广播的使用udp协议)
b.可靠传输(发送应答机制、超时重传、错误校验、流量控制和阻塞管理)
3.2 TCP与UDP的不同点
a.面向连接(确认创建三方交握,链接已创建才能传输)
b.有序的数据传输
c.重发丢失的数据包
d.舍弃重复的数据包
e.无差错的数据传输
f.阻塞与流量控制
(TCP通信模型UDP通信模型)
3.2 tcp客户端
import socket def main(): # 1. 创建tcp的套接字 tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 2. 链接服务器 # tcp_socket.connect(("192.168.33.11", 7890)) server_ip = input("请输入要链接的服务器的ip:") server_port = int(input("请输入要链接的服务器的port:")) server_addr = (server_ip, server_port) tcp_socket.connect(server_addr) # 3. 发送数据/接收数据 send_data = input("请输入要发送的数据:") tcp_socket.send(send_data.encode("utf-8")) # 4. 关闭套接字 tcp_socket.close() if __name__ == "__main__": main()
3.3 tcp服务器
tcp服务器流程
a.socket创建一个套接字
b.bind绑定IP和port
c.listen使套接字变为可以被动链接
d.accept等待客户端的链接
e.接收/发送数据
import socket def main(): # 1. 买个手机(创建套接字 socket) tcp_server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 2. 插入手机卡(绑定本地信息 bind) tcp_server_socket.bind(("", 7890)) # 3. 将手机设置为正常的 响铃模式(让默认的套接字由主动变为被动 listen) tcp_server_socket.listen(128) print("-----1----") # 4. 等待别人的电话到来(等待客户端的链接 accept) new_client_socket, client_addr = tcp_server_socket.accept() print("-----2----") print(client_addr) # 接收客户端发送过来的请求 recv_data = new_client_socket.recv(1024) print(recv_data) # 回送一部分数据给客户端 new_client_socket.send("hahahghai-----ok-----".encode("utf-8")) # 关闭套接字 new_client_socket.close() tcp_server_socket.close() if __name__ == "__main__": main()
注:监听套接字只为接收客户端,接收到之后,会指派一个新的套接字用于与客户端的通信。
3.4 案例:下载文件(待补充)
3.5知识点快速总结,listen里的参数(待补充)
3.6 QQ不绑定端口、昨日知识点复习
TCP注意点:
1)tcp服务器一般情况下都需要绑定,否则客户端找不到这个服务器。
2)tcp客户端一般不绑定,因为是主动链接服务器,所以只要确定好服务器的ip,port等信息就好,本地
客户端可以随机。
3)tcp服务器中通过listen可以将socket创建出来的主动套接字变为被动,这是做tc p服务器时必须要做的。
4)当客户端需要链接服务器时,就需要使用connect进行链接,udp是不需要链接而是直接发送,但是tcp必须
先链接,只有链接成功才能通信。
5)当一个TCP客户端连接服务器时,服务器会有一个新的套接字,这个套接字用来标记这个客户端,单独为这个
客户端服务
6)listen后的套接字是被动套接字,用来接收新的客户端的链接请求的,而accept返回的新套接字是标记这个新客户端的
7)关闭listen后的套接字意味着被动套接字关闭了,会导致新的客户端不能够链接服务器,但是之前已经链接成功的客户端
正常通信。
8)关闭accept返回的套接字意味着这个客户端已经服务完毕
9)当客户端的套接字调用close后,服务器端会recv解堵塞,并且返回的长度为0,因此服务器可以通过
返回数据的长度来区别客户端是否已经下线。