什么是网络
- 使用网络能把多方链接在一起,进行数据传递
- 网络编程就是让不同的电脑上的软件能够进行数据传递,即进程之间的通信
ip地址
- 用来标记主机,信息寻址
端口
- 端口就好像一个房子的门,是出入这间房子的必经之路,软件收发网络数据必须要使用端口
- 端口号,范围0-65535
- 知名端口(0-1024),动态端口(1024以上)
- 查看端口
- netstart -an 查看端口状态
- lsof -i [tcp/udp]:2425
总结
端口有什么用呢 ? 我们知道,一台拥有IP地址的主机可以提供许多服务,比如HTTP(万维网服务)、FTP(文件传输)、SMTP(电子邮件)等,这些服务完全可以通过1个IP地址来实现。那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP地址与网络服务的关系是一对多的关系。实际上是通过“IP地址+端口号”来区分不同的服务的。 需要注意的是,端口并不是一一对应的。比如你的电脑作为客户机访问一台WWW服务器时,WWW服务器使用“80”端口与你的电脑通信,但你的电脑则可能使用“3457”这样的端口。
socket(套接字)
- 不同进程之间的通信(能做到不同主机)
- 通过ip+端口号+协议来标识
- 所谓进程指的是:运行的程序以及运行时用到的资源这个整体称之为进程
- 所谓进程间通信指的是:运行的程序之间的数据共享
- 大部分的服务都是基于socket
创建socket
import socket
socket.socket(AddressFamily,type)
说明:
函数 socket.socket 创建一个 socket,该函数带有两个参数:
- Address Family:可以选择 AF_INET(用于 Internet 进程间通信) 或者AF_UNIX(用于同一台机器进程间通信),实际工作中常用AF_INET
- Type:套接字类型,可以是 SOCK_STREAM(流式套接字,主要用于 TCP 协议)或者SOCK_DGRAM(数据报套接字,主要用于 UDP 协议)
创建一个tcp socket(tcp套接字)
import socket
# 创建套接字
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 使用套接字……(省略)
# 不用的时候关闭套接字
s.close()
创建一个udp socket(udp套接字)
import socket
# 创建套接字
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 使用套接字……(省略)
# 不用的时候关闭套接字
s.close()
说明
- 套接字的使用流程和文件的使用流程很相似
- 创建套接字
- 使用套接字收/发数据
- 关闭套接字
udp网络程序-发送、接收数据
- 创建一个基于udp的网络程序流程很简单,如下
- 创建套接字
- 使用套接字收/发数据
- 关闭套接字
发送数据
代码如下:
# coding=utf-8
import socket
# 创建socket套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 准备接收方的地址
# '192.168.3.11' 标识目标ip地址,要求字符串类型
# 7890 标识目标的端口,要求int类型
# 注意,此处需要元祖,因为一会调用的方法只接受元祖,因为元祖具有不可变的性质
dest_addr = ('192.168.3.11', 8080)
# 从键盘获取数据
send_data = input('请输入要发送的数据:')
# 发送数据到指定电脑的指定端口中
# 对键盘输入的数据进行编码encode('utf-8'),不然会报错
udp_socket.sendto(send_data.encode('utf-8'), dest_addr)
# 关闭套接字
udp_socket.close()
发送、接收数据
代码如下:
import socket
# 创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 准备接收方地址
dest_addr = ('192.168.3.11', 8080)
# 从键盘获取数据
send_data = input('请输入要发送的数据')
# 发送数据到指定的电脑上
udp_socket.sendto(send_data.encode('utf-8'), dest_addr)
# 接收数据
recv_data = udp_socket.recvfrom(1024)
# 1024表示这次接收数据的最大字节数为1024个字节,多了不收
# 显示对方发送的数据
# 收到的数据recv_data是一个元祖(类似我们发送的数据格式)
# 第一个元素是对方发送的数据
# 第二个元素是对方使用的ip和port
print(recv_data[0].decode('gbk')) # 接收到的数据为二进制数据,要解码
print(recv_data[1])
# 关闭套接字
udp_socket.close()
编码转换
str -> bytes :encode编码
bytes -> str :decode编码
其中decode()与encode()方法可以接受参数,其声明分别为:
bytes.decode(encoding="utf-8", errors="strict")
str.encode(encoding="utf-8", errors="strict")
其中的encoding是指在解码编码过程中使用的编码(此处指“编码方案”是名词),errors是指错误的处理方案。
详细的可以参照官方文档:
端口绑定
- 发送数据不需要绑定端口
- 操作系统会随机分配一个端口给当前网络程序,直到socket关闭之前,都会保持这个端口。
- 接收数据需要绑定端口
- 这个网络程序在运行的过程中,这个就唯一标识这个程序,所以如果其他电脑上的网络程序如果想要向此程序发送数据,那么就需要向这个数字(即端口)标识的程序发送即可
import socket
# 创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定本地地址
local_addr = ('',7890) #ip和port,ip一般不写,表示本机的ip
udp_socket.bind(local_addr)
# 等待接受对方的数据
recv_data = udp.socket.recvfrom(1024)
# 显示接收到的数据
print(recv_data[0].decode('gbk'))
# 关闭套接字
udp_socket.close()
- 总结
- 一个udp网络程序,可以不绑定,此时操作系统会随机进行分配一个端口,如果重新运行此程序端口可能会发生变化
- 一个udp网络程序,也可以绑定信息(ip地址,端口号),如果绑定成功,那么操作系统用这个端口号来进行区别收到的网络数据是否是此进程的
udp聊天器(半双工版,多任务章会实现全双工)
- 一个程序,两个功能
- 获取键盘数据,发送给对方
- 接受并显示数据
- 可以进行功能的选择
import socket
def send_msg(udp_socket):
''' 获取键盘数据,并将其发送给对方'''
# 从键盘获取要发送的数据
send_msg = input('请输入要发送的数据')
dest_ip = input('请输入ip')
dest_port = int(input('请输入port'))
# 发送数据到指定的电脑上
udp_socket.sendto(send_msg.encode('utf-8'), (dest_ip, dest_port))
def recv_msg(udp_socket):
'''接收数据并显示'''
# 接收数据
recv_msg = udp_socket.recvfrom(1024)
# 解码
recv_ip = recv_msg[1]
recv_data = recv_msg[0].decode('utf-8')
# 显示接收到的数据
print('>>>{}:{}'.format(str(recv_ip), recv_data))
def main():
# 创建套接字
udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定本地信息
local_addr = ('', 7891)
udp_socket.bind(local_addr)
while True:
# 选择功能
print('1:发送消息')
print('2:接收消息')
op_num = input('请输入要操作的功能:')
if op_num == '1':
# 发送信息
send_msg(udp_socket)
elif op_num == '2':
# 接受信息
recv_msg(udp_socket)
else:
print('选择错误,请重新输入')
if __name__ == '__main__':
main()