- socket模块
- socket模块主要对象和方法
- 服务端与多个客户端实现通信
- 服务端
- 客户端
- 通过socket.socket()函数来创建 套接字对象:
# socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None) s = socket.socket(family=AF_INET, type=SOCK_STREAM)
socket.socket()函数参数:
摘自:Python文档 创建套接字
| 参数 | 说明 |
|---|---|
| family | 地址簇:AF_INET (默认)、AF_INET6、AF_UNIX、AF_CAN、AF_PACKET 或 AF_RDS |
| type | 套接字类型: SOCK_STREAM (默认)、SOCK_DGRAM、SOCK_RAW 或其他 SOCK_ 常量之一 |
| proto | 协议号通常为零,可以省略,或者在地址簇为 AF_CAN 的情况下,协议号应为 CAN_RAW、CAN_BCM 或 CAN_ISOTP 之一 |
| fileno | 如果指定了 fileno,那么将从这一指定的文件描述符中自动检测 family、type 和 proto 的值。如果调用本函数时显式指定了 family、type 或 proto 参数,可以覆盖自动检测的值。 |
- 将套接字绑定到 address:
# socket.bind(address)
s.bind(address=("127.0.0.1",9898))
| 参数 | 说明 |
|---|---|
| address | address 的格式取决于地址簇 |
- 启动一个服务器用于接受连接:
# socket.listen([backlog]) s.listen(5)
- 接受一个连接:
# socket.accept() # accept()方法返回值是一个tuple(conn, address) conn, addr = s.accept()
socket.accept()方法返回值说明:
| 返回值 | 说明 |
|---|---|
| conn | 是一个新的套接字对象,请使用该套接字对象在连接上收发数据 |
| address | 连接另一端的套接字所绑定的地址,客户端的 address tuple类型(IP,PORT) |
- 连接到 address 处的远程套接字:
# socket.connect(address)
# 客户端向服务端发起连接
s.connect(("127.0.0.1",9898))
- 如果需要接收多个客户端的连接请求,那么每个连接都需要开启新的进程或者是线程
- 从套接字接收数据的方法:
# recv方法:socket.recv(bufsize[, flags]) # bufsize 指定一次接收的最大数据量 # 返回值是一个字节对象,表示接收到的数据 data_bytes = conn.recv(1024) # recvfrom方法:socket.recvfrom(bufsize[, flags]) # 返回值是一个tuple(bytes, address) # bytes 是字节对象,表示接收到的数据,address 是发送端套接字的地址 data_bytes, addr = conn.recvfrom(1024)
更多接收数据的方法 参考 Python文档
- 发送数据给套接字:
# send方法:socket.send(bytes[, flags])
# 返回已发送的字节数
# 应用程序要负责检查所有数据是否已发送,如果仅传输了部分数据,程序需要自行尝试传输其余数据
conn.send(b'Hi!')
conn.send(str("msg").encode("utf-8")
# sendall方法:socket.sendall(bytes[, flags])
# 与 send() 不同,本方法持续从 bytes 发送数据,直到所有数据都已发送或发生错误为止
# 成功后会返回 None。出错后会抛出一个异常,此时并没有办法确定成功发送了多少数据
conn.sendall(b'Hi!')
conn.sendall(str("msg").encode("utf-8")
# sendto方法:socket.sendto(bytes, address)
# 本套接字不应连接到远程套接字,而应由 address 指定目标套接字。
# 返回已发送的字节数
注意:服务端数据的收发需要使用accept()方法返回的套接字对象操作
更多发送数据的方法 参考Python文档
服务端与多个客户端实现通信 服务端了解以上方法之后,利用套接字对象和方法来构建一个服务端:
# demo_socket_server.py文件
import logging
import socket
import threading
import time
logging.basicConfig(level=logging.DEBUG,
format="%(asctime)s>%(levelname)s>%(name)s>%(funcName)s>%(lineno)s>%(message)s")
logger = logging.getLogger(__name__)
class ServerClass(object):
"""docstring for ServerClass"""
__HOST = "127.0.0.1"
__PORT = 9898
def __init__(self):
# 创建 套接字对象 socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
# 参数:family:地址簇 type:套接字类型
self.__TCP_SOCKET = socket.socket(
family=socket.AF_INET, type=socket.SOCK_STREAM)
# 服务端的地址 address
self.__ADDR = (ServerClass.__HOST, ServerClass.__PORT)
# 用来交互的信息
self.__MSG_DICT = {
"1": ("讲笑话", "这是一个笑话"),
"2": ("讲故事", "这是一个故事"),
"3": ("学唱歌", "这是在唱歌"),
}
def start_server(self):
"""启动服务端方法"""
with self.__TCP_SOCKET as s:
# 将套接字绑定到 address
s.bind(self.__ADDR)
# 启动一个服务器用于接受连接 socket.listen([backlog])
s.listen(5)
logger.info("等待连接...")
# 循环控制 用来等待客户端连接 以及保持连接状态
while True:
# 接受一个连接 返回值是一个 (conn, address)
conn, addr = s.accept()
# 多个客户端连接需要开启新的线程或者是进程
th = threading.Thread(target=self.conn_client,
args=(conn, addr), daemon=True)
th.start()
def conn_client(self, conn, addr):
"""连接客户端,并进行收发数据"""
logger.info("正在与 %s 建立连接...", addr)
with conn:
# 如果有新的连接进来就 发送数据;
conn.sendall(b'hi! %s %s n' %
tuple(map(lambda x: str(x).encode("utf-8"), addr)))
time.sleep(0.8)
conn.sendall(self.get_msg().encode("utf-8"))
# 循环控制 使用accept()方法返回的套接字对象 收发数据
while True:
rec_data = conn.recv(1024).decode("utf-8")
if not rec_data:
logger.info("与%s,断开连接...", addr)
time.sleep(0.5)
break
# 如果客户端发送的数据可以识别 发送设置好的对应数据
if rec_data in self.__MSG_DICT.keys():
conn.sendall(self.__MSG_DICT[rec_data][1].encode("utf-8"))
else:
conn.sendall("不知道你在说什么".encode("utf-8"))
def get_msg(self):
"""构造发送数据的格式"""
msg_str = "可以为您提供一下服务:n"
for k, v in self.__MSG_DICT.items():
msg_str += k + " : " + v[0] + "n"
return msg_str
if __name__ == '__main__':
ServerClass().start_server()
然后启动服务端:
CMD进入到demo_socket_server.py同目录下,输入:
Python demo_socket_server.py客户端
客户端的构建比服务端相对简单一些:
# demo_socket_client.py 文件
import logging
import time
import socket
logging.basicConfig(level=logging.DEBUG,
format="%(asctime)s>%(levelname)s>%(name)s>%(funcName)s>%(lineno)s>%(message)s")
logger = logging.getLogger(__name__)
class ClientClass(object):
"""docstring for ClientClass"""
__HOST = "127.0.0.1"
__PORT = 9898
def __init__(self):
self.__TCP_SOCKET = socket.socket(
family=socket.AF_INET, type=socket.SOCK_STREAM)
# 服务端的地址 address
self.__ADDR = (ClientClass.__HOST, ClientClass.__PORT)
def start_client(self):
with self.__TCP_SOCKET as s:
# 链接服务端地址
s.connect(self.__ADDR)
# 接收服务端发送来的数据
recv_data = s.recv(1024).decode("utf-8")
logger.info(recv_data)
# 循环控制 保持和服务端通讯 收发数据
while True:
recv_data = s.recv(1024).decode("utf-8")
logger.info(recv_data)
time.sleep(0.5)
send_data = input("请输入:")
# 如果输入 quit 或者 exit 断开连接
if send_data in ("quit", "exit"):
logger.info("正在退出...")
time.sleep(1.5)
break
# 发送数据到服务端
s.sendall(str(send_data).encode("utf-8"))
if __name__ == '__main__':
ClientClass().start_client()
然后启动客户端:
CMD进入到demo_socket_client.py同目录下,输入:
Python demo_socket_server.py
开启一个客户端交互的效果:
下面是开启多个客户端交互的效果:
以上就是服务端向多个客户端之间接收发送数据的一个简单示例;可以实现服务端与多个客户端的交互。
但是还没有实现怎么让各个客户端之间可以进行通信,
下面要去学习如何能让各个客户端之间能够进行通信;学完了再抽空发上来。
主要参考资料来源:
廖雪峰的官方网站 - 网络编程 - TCP编程
Python 文档 : socket — 底层网络接口
如果有什么不对的地方,欢迎指正!



