Python学习笔记——Day07
第七天,今天看网络编程相关。前两天因工作需要,耽误了博客更新。
socket的使用
socket(简称套接字)是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:它能实现不同主机之间的进程间通信,我们网络上很多服务都是基于Socket完成通信的。
在Python中使用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,就这样写
import socket
# 创建tcp的套接字
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 功能代码 略...
...
# 当逻辑结束后,关闭socket
s.close()
创建udp套接字的话只需要将socket方法中的第二个参数,换为socket.SOCK_DGREAM
import socket
# 创建udp的套接字
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 功能代码 略...
...
# 当逻辑结束后,关闭socket
s.close()
使用套接字流程简单说就是
- 创建套接字
- 使用套接字进行接收/发送数据
- 关闭套接字
是不是有点眼熟?第四天中文件操作和这个流程有些相似,先打开文件,操作文件,然后调用close()方法关闭,那时候我只写了一个小小的例子,明天周日详细总结一份文件操作相关的内容。
创建udp的发送/接收数据
socket简单发送接收程序
循环接收代码
#!usr/bin/python
# -*- coding: utf-8 -*-
# author: 李爽
# description:socket接收
import socket
def main():
# 创建udp socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
# 绑定端口
local_addr = ("127.0.0.1", 8080)
s.bind(local_addr)
# 循环接收
while True:
# 接收数据,返回的是一个元组(接收到的数据,(发送方的地址信息))
recv_message = s.recvfrom(1024)
recv_data = recv_message[0]
recv_address = recv_message[1]
# 打印结果
print("从%s接收:%s" % (recv_address, recv_data.decode("utf-8")))
# 如果接收的是quit
if recv_data.decode("utf-8") == 'quit':
print("接收程序退出")
break
s.close()
if __name__ == "__main__":
main()
循环发送代码
#!usr/bin/python
# -*- coding: utf-8 -*-
# author: 李爽
# description:udp socket 发送
import socket
def main():
# 创建udp socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
while True:
# 键盘录入要发送的数据
send_data = input("请输入要发送的数据:")
# 发送数据,
send_addr = ("127.0.0.1", 8080)
s.sendto(send_data.encode("utf-8"), send_addr)
# 若输入quit 退出程序
if send_data == "quit":
print("发送程序退出")
break
s.close()
if __name__ == "__main__":
main()
程序运行结果
发送程序
请输入要发送的数据:你好
请输入要发送的数据:quit
发送程序退出
接收程序
从('127.0.0.1', 62189)接收:你好
从('127.0.0.1', 62189)接收:quit
接收程序退出
流程还是那套流程,创建套接字,使用套接字进行收发数据,关闭套接字,只不过加上了死循环,在输入quit的时候退出程序,需要注意的是,字符串进行了编码和字节码进行了解码,使用的str.encode(encoding="utf-8", errors="strict")``bytes.decode(encoding="utf-8", errors="strict")
,其中encoding是指在编码解码过程中使用的编码,例如我使用的是utf-8,errors是指错误的处理方案。具体的可以参照官方文档。还有就是udp接收和发送的流程有一些不一样的,接收多一步socket.bind()
参数传了一个元组,是ip和端口的元组,其实也可以不绑定端口,但这种情况下是操作系统随机分配的端口,当重新运行程序的时候端口可能会发发生变化,但作为一个服务器端的程序,端口总是变化的话,客户端的我们还要每天改端口,这就是我们做这一步端口绑定的目的。
案例-udp聊天室
#!usr/bin/python
# -*- coding: utf-8 -*-
# author: 李爽
# description:udp 聊天室
import socket
def send_msg(s):
send_ip = input("请输入要发送的IP地址:")
send_port = int(input("请输入要发送的端口号:").strip())
send_message = input("请输入要发送的信息:")
s.sendto(send_message.encode("utf-8"), (send_ip, send_port))
def recv_msg(s):
recv_message = s.recvfrom(1024)
recv_data = recv_message[0].decode("utf-8")
recv_addr = recv_message[1][0] + ": " + str(recv_message[1][1])
print("从%s接收消息:%s" % (recv_addr, recv_data))
def main():
# 创建udp socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("", 8080))
while True:
print("欢迎进入聊天室")
print("1.发送消息")
print("2.接收消息")
print("0.退出聊天室")
op = input("请输入操作:")
if op == "1":
# 发送
send_msg(s)
elif op == "2":
# 接收
recv_msg(s)
elif op == "0":
print("退出聊天室")
break
else:
print("输入有误,请重新输入。")
s.close()
if __name__ == "__main__":
main()
发送给自己的输出结果
欢迎进入聊天室
1.发送消息
2.接收消息
0.退出聊天室
请输入操作:1
请输入要发送的IP地址:127.0.0.1
请输入要发送的端口号:8080
请输入要发送的信息:haha
欢迎进入聊天室
1.发送消息
2.接收消息
0.退出聊天室
请输入操作:2
从127.0.0.1: 8080接收消息:haha
欢迎进入聊天室
1.发送消息
2.接收消息
0.退出聊天室
请输入操作:0
退出聊天室
这样我们的小聊天室就完成了!可是这个小聊天室有一个很严重的问题,如果你知道请在文章后面回复给我,答案我也会在下一篇博客中揭晓
TCP套接字
TCP套接字就是使用TCP协议提供的传输服务来实现网络通信的编程接口。只需要将socket方法的第二个参数设置为SOCK.STREAM就可以得到一个TCP套接字。由于一台主机可能拥有多个IP地址,而且很有可能会配置多个不同的服务,所以作为服务器端的程序,需要在创建套接字对象后将其绑定到指定的IP地址和端口上。
下面来看服务器端和客户端
#!usr/bin/python
# -*- coding: utf-8 -*-
# author: 李爽
# description:tcp 套接字服务器端
import socket
def main():
# 创建tcp套接字
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 绑定端口
s.bind(("127.0.0.1", 8080))
# socket创建的套接字默认属性是为主动的
# 将套接字的属性变为被动,接收发送来的数据
s.listen(1024)
# 当有客户端连接到服务器时,就产生一个新的套接字专门来为这个客户端服务
# client_socket 为这个客户端服务的套接字
# 而s则继续等待其他客户端连接
client_socket, client_addr = s.accept()
# 接收数据
recv_data = client_socket.recv(1024)
print("从%s接收到的数据为:%s" % (client_addr, recv_data.decode("utf-8")))
# 返回数据到客户端
client_socket.send("收到啦!".encode("utf-8"))
# 关闭服务于这个客户端的套接字,关闭后,就意味着只能重新连接才能再次开启服务
client_socket.close()
if __name__ == "__main__":
main()
客户端
#!usr/bin/python
# -*- coding: utf-8 -*-
# author: 李爽
# description:tcp 套接字客户端
import socket
def main():
# 创建tcp套接字
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 连接服务器
s.connect(("127.0.0.1", 8080))
# 发送数据
send_msg = input("请输入要发送的信息:")
s.send(send_msg.encode("utf-8"))
# 接收数据
recv_data = s.recv(1024)
print("从服务器接收到的数据为:%s" % recv_data.decode("utf-8"))
# 关闭套接字
s.close()
if __name__ == "__main__":
main()
可以在服务器端加上死循环,循环接收消息。在此不再赘述。需要注意的是
- tcp服务器端需要绑定,否则客户端无法连接服务器端
- tcp客户端一般不绑定,因为是主动链接服务器,所以只要确定好服务器的ip、port等信息就好,本地客户端可以随机
- tcp服务器中通过listen可以将socket创建出来的主动套接字变为被动的,这是做tcp服务器时必须要做的
- 当客户端需要链接服务器时,就需要使用connect进行链接,udp是不需要链接的而是直接发送,但是tcp必须先链接,只有链接成功才能通信
- 当一个tcp客户端连接服务器时,服务器端会有1个新的套接字,这个套接字用来标记这个客户端,单独为这个客户端服务
- listen后的套接字是被动套接字,用来接收新的客户端的链接请求的,而accept返回的新套接字是标记这个新客户端的
- 关闭listen后的套接字意味着被动套接字关闭了,会导致新的客户端不能够链接服务器,但是之前已经链接成功的客户端正常通信。
- 关闭accept返回的套接字意味着这个客户端已经服务完毕
- 当客户端的套接字调用close后,服务器端会recv解堵塞,并且返回的长度为0,因此服务器可以通过返回数据的长度来区别客户端是否已经下线
结语
socket编程简介到此就结束了。
如果你发现我的文章哪里有错误或者有什么好的想法可以联系我,我们一起学习共同进步,我的邮箱地址是[email protected]
let’s do more of those!