前言
网络程序开发的过程之中一般都会考虑两种不同的开发模式:
c/s模式即客户端与服务端:该设计架构一般需要编写两套不同的程序,一套是服务端程序,一套是客户端程序,在进行项目维护的时候需要进行两套项目的维护,所以维护成本高,但是这种程序一般使用特定的协议,特定的数据结构,隐藏的端口,所以安全性比较高。
b/s模式即浏览器与服务端:主要是基于web设计的一种架构,基于http协议进行数据交互。
对于网络程序的开发不仅仅是一个简单的数据交互的过程,还包含数据处理逻辑,同时所有的网络设备厂商也会不同,所以为了可以保证数据传输的可靠与标准,就有了osi七层模型。
应用层 表示层 会话层 传输层 网络层 数据链路层 物理层
socket网络编程
socket(套接字)是一种对tcp网络协议进行的一种保证,或者称为协议的一种抽象应用,它本身最大的特点是为了提供不同进程之间的数据通讯操作。
socket主要是针对于tcp和udp的包装。
tcp采用有状态的通讯机制进行传输,在通讯时会通过三次握手机制保证与一个指定节点的数据传输的可靠性,在通讯完毕后会通过四次挥手的机制关闭连接。由于每次数据通讯前都需要消耗大量的时间进行连接控制,所以执行性能较低,且系统资源占用较大。
udp采用无状态的通讯机制进行传输,没有握手与挥手处理机制,节约了大量的系统资源,同时传输性能较高,但是由于不保存单个节点的连接状态,所以发送的数据不一定可以被全部接收。 udp不需要链接就可以直接发送数据,并且多个接收端都可以接收到同样的信息,所以使用udp适合于广播操作。
python中socket类中的方法:
- bind((hostname,port))在指定主机的端口绑定监听
- listen()在绑定端口上开启监听
- accept()等待客户端连接,连接后返回客户端地址
- send(data)发送数据
- recv(buffer)接受数据
- close()关闭套接字连接
- connect((hostname,port))设置要连接的主机名称与端口号
创建套接字对象
socket.socket(AddressFamily, Type)
AddressFamily:可以选择AF_INET(用于因特网通信)/AF_UNIX(同一台机器进程间通信)
Type:SOCK_STREAM(流套接字,用于TCP协议),SOCK_DGRAM(数据报套接字,用于UDP)
1.TCP
1.1服务端程序:
import socket
SERVER_HOST = "localhost"
SERVER_PORT = 8080
def main():
with socket.socket() as server_socket:
# 绑定本机端口
server_socket.bind((SERVER_HOST, SERVER_PORT))
# 开启监听
server_socket.listen()
print("服务器启动完毕,在8080端口上连接,等待客户端连接")
# 进入阻塞状态
# 当客户有连接之后,就会解除阻塞状态,同时可以获得客户端的Socket和地址
client_conn, address = server_socket.accept()
# 进行处理
with client_conn:
print("客户端连接到服务器端,客户端的地址为:%s,连接端口:%s" % address)
# 向客户端进行响应
client_conn.send('OK'.encode("utf-8"))
print("连接断开")
cmd中使用telnet localhost 8080 测试(需要电脑打开Telnet功能)
这样就是客户端连接上服务器,服务器对客户端进行了一次响应,然后关闭
1.2客户端程序:
import socket
# 网络服务器的主机名称或IP地址
SERVER_HOST = "127.0.0.1"
SERVER_PORT = 8080
def main():
# 建立客户端
with socket.socket() as client_socket:
# 连接服务器
client_socket.connect((SERVER_HOST, SERVER_PORT))
# 显示 收到的信息 限定字节长度
print("服务器端响应数据: %s" % client_socket.recv(12).decode("utf-8"))
2.echo模型
在网络编程之中有一个经典的程序模型:echo程序模型,这个echo程序实际源于echo命令,在操作系统内部提供有一个echo命令进行内容的回显。
扩大到网络环境之中,就可以理解为,客户端输入一组数据发送服务器,服务端接受之后要对该数据进行响应,这种模型就是网络编程echo模型。
在整个网络编程中所有的网络程序一定要有一个绑定的端口存在,所以一个端口只允许绑定一套服务,如果出现端口被占用,那么程序将无法启动。
2.1 echo服务端
import socket
SERVER_HOST = "localhost"
SERVER_PORT = 8080
def main():
with socket.socket() as server_socket:
# 绑定本机端口
server_socket.bind((SERVER_HOST, SERVER_PORT))
# 开启监听
server_socket.listen()
print("服务器启动完毕,在8080端口上连接,等待客户端连接")
# 进入阻塞状态
# 当客户有连接之后,就会解除阻塞状态,同时可以获得客户端的Socket和地址
client_conn, address = server_socket.accept()
# 进行处理
with client_conn:
print("客户端连接到服务器端,客户端的地址为:%s,连接端口:%s" % address)
while True:
# 接收客户端发送来的数据
data = client_conn.recv(100).decode('utf-8')
if data.upper() == 'EXIT':
bye = 'bye bye'.encode('utf-8')
client_conn.send(bye)
print('客户端已断开连接')
break
else:
print("收到消息:%s" % data)
client_conn.send(("ECHO %s" % data).encode('utf-8'))
2.2 echo客户端
import socket
# 网络服务器的主机名称或IP地址
SERVER_HOST = "127.0.0.1"
SERVER_PORT = 8080
def main():
# 建立客户端
with socket.socket() as client_socket:
# 连接服务器
client_socket.connect((SERVER_HOST, SERVER_PORT))
while True:
input_data = input('请输入要发送的数据:').encode('utf-8')
# 发送数据
client_socket.send(input_data)
# 接收echo回应
echo_data = client_socket.recv(100).decode('utf-8')
if echo_data == 'bye bye':
break
print("服务器端响应数据: %s" % echo_data)
不足的地方是当前的服务器使用的是单进程
当前程序执行的过程之中,如果服务端已经有客户端进行连接了,那么整个主进程就都为当前客户端进行服务了。
要让服务器可以处理多个客户端请求,需要使用多进程的形式来进行处理,每一个客户端连接都启动一个专属的操作进程进行响应处理。
2.3在服务端追加上多进程的处理方式
# 多进程服务端
import socket
import multiprocessing
SERVER_HOST = "localhost"
SERVER_PORT = 8080
def echo_handle(client_conn, address):
# 进程处理函数
print("客户端连接到服务器端,客户端的地址为:%s,连接端口:%s" % address)
# 进行处理
with client_conn:
while True:
# 接收客户端发送来的数据
data = client_conn.recv(100).decode('utf-8')
if data.upper() == 'EXIT':
bye = 'bye bye'.encode('utf-8')
client_conn.send(bye)
print('%s已断开连接' % address[1])
break
else:
print("%s发来信息:%s" % (address[1], data))
client_conn.send(("ECHO %s" % data).encode('utf-8'))
def main():
print("服务器启动完毕,在8080端口上连接,等待客户端连接")
while True:
with socket.socket() as server_socket:
server_socket.bind((SERVER_HOST, SERVER_PORT))
server_socket.listen()
client_conn, address = server_socket.accept()
# 定义进程
process = multiprocessing.Process(target=echo_handle, args=(client_conn, address), name='%s' % address[1])
# 启动进程
process.start()
3.UDP
3.1服务端
import socket
SERVER_HOST = '127.0.0.1'
SERVER_PORT = 8080
def main():
# AF_INET:使用IPV4网络协议进行服务端创建
# SOCK_DGRAM:创建UDP
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as server_socket:
print('服务端已启动\n')
server_socket.bind((SERVER_HOST, SERVER_PORT))
while True:
# 不间断接受客户端发送数据
data, address = server_socket.recvfrom(30)
print("客户端连接到服务器端,客户端的地址为:%s,连接端口:%s" % address)
echo_data = ('echo %s' % data.decode('utf-8')).encode('utf-8')
# 将内容响应到客户端
server_socket.sendto(echo_data, address)
3.2客户端
import socket
# 网络服务器的主机名称或IP地址
SERVER_HOST = "127.0.0.1"
SERVER_PORT = 8080
def main():
# 建立客户端
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as client_socket:
# 无监听 不需要连接
while True:
input_data = input('请输入要发送的数据:').encode('utf-8')
if input_data:
# 发送数据
client_socket.sendto(input_data, (SERVER_HOST, SERVER_PORT))
echo_data = client_socket.recv(100).decode('utf-8')
print('服务端返回数据:%s' % echo_data)
else:
break
4.UDP广播
udp除了可以建立快速的网络通讯之外,还可以实现局域网内所有的主机信息的广播处理。如果要实现UDP广播操作,则一定要在程序之中使用如下的方法进行定义:
setsockopt(self, level:int, optname:int, value:Union[int, bytes])
方法参数:
1.level:设置选项所在的协议层编号,有如下四个可用配置
- socket.SOL_SOCKET:基本套接字接口
- socket.IPPROTO_IP:IPv4套接字接口
- socket.IPPROTO_IPV6:IPv6
- socket.IPPROTO_TCP:TCP套接口
2.optname:设置选项名称,例如,如果要进行广播则可以使用socket.BROADCAST
3.value:设置选项的具体内容
如果要进行广播那么肯定要有接收端,而接收端不一定可以接收到广播,但是只要打开了接收端就可以接收到广播
4.1服务端
import socket
# 设置广播地址 接收广播的端口号
BROADCAST_SERVER_ADDR = ('<broadcast>', 21567)
def main():
# AF_INET:使用IPV4网络协议进行服务端创建
# SOCK_DGRAM:创建UDP
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as server_socket:
print('服务端已启动\n')
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
server_socket.sendto('这是一条广播消息'.encode('utf-8'), BROADCAST_SERVER_ADDR)
4.2客户端
import socket
# 客户端绑定地址
BROADCAST_CLIENT_ADDR = ("0.0.0.0", 21567)
def main():
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as client_socket:
# 开启广播模式
client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
# 绑定广播客户端地址
client_socket.bind(BROADCAST_CLIENT_ADDR)
while True:
message, address = client_socket.recvfrom(100)
print("消息内容:%s\nIP:%s\n端口:%s" % (message.decode('utf-8'), address[0], address[1]))
以上就是关于python 中socket的大致使用,更多的是要运用到实际场景当中去,以后再继续补充