Python:基于 Socket 网络编程 + threading 多线程 搭建多用户通信模型

1. Socket 协议

1.1. 什么是 Socket 协议

  • Socket 通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求;
  • 网络通信,归根到底还是进程间的通信(不同计算机上的进程间通信, 又称进程间通信, IP协议进行的主要是端到端通信);
  • 在网络中,每一个节点(计算机或路由)都有一个网络地址,也就是IP地址,两个进程通信时,首先要确定各自所在的网络节点的网络地址,但是,网络地址只能确定进程所在的计算机,而一台计算机上很可能同时运行着多个进程,所以仅凭网络地址还不能确定到底是和网络中的哪一个进程进行通信,因此套接口中还需要包括其他的信息,也就是端口号(PORT)。在一台计算机中,一个端口号一次只能分配给一个进程,也就是说,在一台计算机中,端口号和进程之间是一一对应关系,使用端口号和网络地址的组合可以唯一的确定整个网络中的一个网络进程;
  • Socket ,是操作系统内核中的一个数据结构,它是网络中的节点进行相互通信的门户;
  • Socket 是网络进程的ID
  • Socket 要求服务端一直处于监听状态;
    • 计算机网络是由协议组成的,但 Socket 是对 TCP/IP 协议的封装,Socket 本身并不是协议,而是一个调用接口(API)
  • Socket 起源于 Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,对于文件用【打开】【读写】【关闭】模式来操作。socket 就是该模式的一个实现,socket 即是一种特殊的文件,一些socket函数就是对其进行的操作(读 / 写 IO、打开、关闭)
  • Socket 和 file 的区别:
    -- file 模块是针对某个指定文件进行【打开】【读写】【关闭】;
    -- Socket 模块是针对 服务器端 和 客户端 Socket 进行【打开】【读写】【关闭】;

1.2. Socket 分类

  • Socket 类型在Liunx和Python是一样的,只是Python中的类型都定义在 Socket 模块中,调用方式 socket.SOCK_XXXX;

  • 流式 Socket (SOCK_STREAM) 用于TCP通信
    -- 流式套接字提供可靠的、面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性

  • 数据报 Socket (SOCK_DGRAM) 用于UDP通信
    -- 数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议UDP;

  • 原始 Socket (SOCK_RAW) 用于新的网络协议实现的测试等
    -- 原始套接字,普通的套接字无法处理 ICMP、IGMP 等网络报文,而 SOCK_RAW 可以, 其次,SOCK_RAW 也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过 IP_HDRINCL 套接字选项由用户构造IP头。

2. client & server 通信模型

  • server
import socket
#创建服务端的socket对象socketserver
socketserver = socket.socket(
    socket.AF_INET,             # AF_INET 表示服务器到服务器通信
    socket.SOCK_STREAM          # SOCK_STREAM 表示 socket 连接
)
host = '127.0.0.1'
port = 8888
#绑定地址(包括ip地址会端口号)
socketserver.bind((host, port))
#设置监听
socketserver.listen(5)
#等待客户端的连接
#注意:accept()函数会返回一个元组
#元素1为客户端的socket对象,元素2为客户端的地址(ip地址,端口号)
clientsocket,addr = socketserver.accept()

#while循环是为了能让对话一直进行,直到客户端输入q
while 1:

    #接收客户端的请求
    recvmsg = clientsocket.recv(1024)
    #把接收到的数据进行解码
    strData = recvmsg.decode("utf-8")
    #判断客户端是否发送q,是就退出此次对话
    if strData=='q':
        break
    print("收到:"+strData)
    msg = input("回复:")
    #对要发送的数据进行编码
    clientsocket.send(msg.encode("utf-8"))

socketserver.close()
  • client
import socket
#创建一个客户端的socket对象
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

#设置服务端的ip地址
host = "127.0.0.1"
#设置端口
port = 8888
# 连接服务端
client.connect((host, port))

#while循环是为了保证能持续进行对话
while 1:
    #输入发送的消息
    sendmsg = input("请输入:")
    #如果客户端输入的是q,则停止对话并且退出程序
    if sendmsg=='q':
        break

    sendmsg = sendmsg
    #发送数据,以二进制的形式发送数据,所以需要进行编码
    client.send(sendmsg.encode("utf-8"))
    msg = client.recv(1024)
    #接收服务端返回的数据,需要解码
    print(msg.decode("utf-8"))
#关闭客户端
client.close()
  • 执行结果

-- client:

请输入:你好,server

-- server:

收到:你好,server
回复: 你好,client,server 收到

-- client:

你好,client,server 收到

3. 构建多 client 用户通信模型

  • server
    -- server 是唯一的,此处使用多线程处理多 client 连接问题
import socket
import threading


#创建服务端的socket对象socketserver
socketserver = socket.socket(
    socket.AF_INET,             # AF_INET 表示服务器到服务器通信
    socket.SOCK_STREAM          # SOCK_STREAM 表示 socket 连接
)
host = '127.0.0.1'
port = 8888
#绑定地址(包括ip地址会端口号)
socketserver.bind((host, port))
#设置监听
socketserver.listen(5)

def handle_sock(sock, addr):
    while 1:
        # 接收客户端的请求
        recvmsg = clientsocket.recv(1024)
        # 把接收到的数据进行解码
        strData = recvmsg.decode("utf-8")
        # 判断客户端是否发送q,是就退出此次对话
        if strData == 'q':
            break
        print("收到:" + strData)
        msg = input("回复:")
        # 对要发送的数据进行编码
        clientsocket.send(msg.encode("utf-8"))


#while循环是为了能让对话一直进行,直到客户端输入q
while 1:
    # 等待客户端的连接
    # 注意:accept()函数会返回一个元组
    # 元素1为客户端的socket对象,元素2为客户端的地址(ip地址,端口号)
    clientsocket, addr = socketserver.accept()
    client_thread = threading.Thread(target=handle_sock, args=(clientsocket, addr))
    client_thread.start()

socketserver.close()
  • client 1、client 2、...:
    -- client 端的代码,与前面 client 的代码并无区别;
import socket
#创建一个客户端的socket对象
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

#设置服务端的ip地址
host = "127.0.0.1"
#设置端口
port = 8888
# 连接服务端
client.connect((host, port))

#while循环是为了保证能持续进行对话
while True:
    #输入发送的消息
    sendmsg = input("请输入:")
    #如果客户端输入的是q,则停止对话并且退出程序
    if sendmsg=='q':
        break

    sendmsg = sendmsg
    #发送数据,以二进制的形式发送数据,所以需要进行编码
    client.send(sendmsg.encode("utf-8"))
    msg = client.recv(1024)
    #接收服务端返回的数据,需要解码
    print(msg.decode("utf-8"))
#关闭客户端
client.close()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容