基于TCP的socket编程实现client和server通信
基于TCP的socket编程实现client和server通信
.
实验内容:
.
client为单线程、server为多线程(群聊功能)
.
原理:
.
套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。
.
环境:
.
Python 3.8.2、Windows10、pycharm
.
实验过程:
.
一、先编写服务器实现群聊、
.
- 成功建立连接、每个线程都有自己文件占位符fd和端口号port:
- 三个client共同发送消息、验证群聊
- 群聊实现,每个client都收到来自时间、端口各异的消息
- TcpServer代码
#服务端
import socket
import threading #基于线程的并行
import logging #Python 的日志记录工具
import datetime #基本的日期和时间类型
FORMAT = "%(asctime)s %(threadName)s %(thread)d %(message)s"
logging.basicConfig(format=FORMAT, level=logging.INFO)
#创建类
class TcpChatServer:
#初始化、ip、port
def __int__(self, ip='127.0.0.1', port=9999):
self.addr = (ip, port)
self.sock = socket.socket()
self.clients = {} #空字典储存已有进程
#开启
def start(self):
self.sock.bind(self.addr)
self.sock.listen() #服务启动、开始监听
threading.Thread(target=self.accept, name='accept').start() #开始线程活动
def accept(self):
while True:
s, raddr = self.sock.accept() #返回新的套接字对象和客户端IP地址
logging.info(s)
logging.info(raddr)
#key = raddr, values = s
self.clients[raddr] = s
threading.Thread(target=self.recv, name='recv', args=(s,)).start()
def recv(self, sock:socket.socket):
while True:
try: #异常处理
data = sock.recv(1024) #阻塞点
logging.info(data) # 打印拿到的数据
except Exception as e:
logging.error(e)
data = b'quit'
if data == b'quit':
self.clients.pop(sock.getpeername())
break
msg = "ack{}. {} {}".format( #数据整理=地址+时间+内容
sock.getpeername(),
datetime.datetime.now().strftime("%Y/%m/%d-%H:%M:%S"),
data.decode()).encode()
#一对多、发送所有接受的数据
for s in self.clients.values():
s.send(msg)
#发送
def send(self, cmd):
sMsg = "send{}. {} {}".format(
self.sock.getsockname(), #Return the socket's own address
datetime.datetime.now().strftime("%Y/%m/%d-%H:%M:%S"),
cmd).encode()
# self.sock.send(sMsg)
# 一对多、发送所有接受的数据
for s in self.clients.values():
s.send(sMsg)
#停止
def stop(self):
for s in self.clients.values():
s.close()
self.sock.close()
#custom
cs = TcpChatServer()
cs.__int__()
cs.start()
#测试、输入显示当前进程
while True:
cmd = input("输入show查看当前进程,输入quit关闭服务器\n")
if cmd.strip() == 'quit': #移除字符串头尾指定的字符(默认为空格或换行符)或字符序列
cs.stop()
threading.Event.wait(3) #阻塞线程直到内部变量为true
break
elif cmd.strip() == 'show':
logging.info(threading.enumerate()) # 以列表形式返回当前所有存活的 Thread 对象
else:
cs.send(cmd)
.
二、再编写客户端、
.
- 连接服务器
- 实现双向数据传输
-
TcpClient代码
#客户端 import socket import datetime import threading class TcpCilent: def __int__(self): host = '127.0.0.1' port = 9999 raddr = (host, port) self.bufsize = 1024 # 指定接收数据大小 self.tcpCilent = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建TCP Socket self.tcpCilent.connect(raddr) # 连接 threading.Thread(target=self.send, name='send').start() # 开始线程活动 threading.Thread(target=self.recv, name='recv').start() def send(self): #发送数据 while True: cmd = input("输入要发送的数据、输入quit断开连接:\n") if cmd.strip(':') == 'quit': self.stop() break cMsg = "send{}. {} {}".format( self.tcpCilent.getsockname(), datetime.datetime.now().strftime("%Y/%m/%d-%H:%M:%S"), cmd ) self.tcpCilent.send(cMsg.encode()) def recv(self): while True: #接收数据 try: data = self.tcpCilent.recv(self.bufsize) except Exception as e: #常规错误的基类、线程释放问题? print(e) data = "" if not len(data): break sMsg = "ack{}. {} {}".format( # 数据整理=地址+时间+内容 self.tcpCilent.getpeername(), datetime.datetime.now().strftime("%Y/%m/%d-%H:%M:%S"), data.decode()) print(sMsg) def stop(self): self.tcpCilent.close() cs = TcpCilent() cs.__int__()
.
三、最后实现两者通信
.
- cmd运行server、pycharm运行client、建立socket连接
- 验证互相通信
.
.
.
Windows 下的 socket 程序和 Linux 思路相同,但细节有所差别:
Windows 下的 socket 程序依赖 Winsock.dll 或 ws2_32.dll,必须提前加载。DLL 有两种加载方式,请查看:动态链接库DLL的加载
Linux 使用“文件描述符”的概念,而 Windows 使用“文件句柄”的概念;Linux 不区分 socket 文件和普通文件,而 Windows 区分;Linux 下 socket() 函数的返回值为 int 类型,而 Windows 下为 SOCKET 类型,也就是句柄。
Linux 下使用 read() / write() 函数读写,而 Windows 下使用 recv() / send() 函数发送和接收。
关闭 socket 时,Linux 使用 close() 函数,而 Windows 使用 closesocket() 函数,python中socket模块也是用的socket.close()。
dows 区分;Linux 下 socket() 函数的返回值为 int 类型,而 Windows 下为 SOCKET 类型,也就是句柄。
Linux 下使用 read() / write() 函数读写,而 Windows 下使用 recv() / send() 函数发送和接收。
关闭 socket 时,Linux 使用 close() 函数,而 Windows 使用 closesocket() 函数,python中socket模块也是用的socket.close()。
上一篇: muduo的日志库分析二之Logger类
推荐阅读
-
详解Android 基于TCP和UDP协议的Socket通信
-
使用C#实现基于TCP和UDP协议的网络通信程序的基本示例
-
基于Java的Socket类Tcp网络编程实现实时聊天互动程序:QQ聊天界面的搭建
-
【Netty】Socket 编程(C/S):基于Netty的Server、Client示例(少注释)
-
Java Socket实现基于TCP和UDP多线程通信
-
网络编程概念、UDP通信程序和TCP通信程序的通信原理及实现程序
-
linux网络编程之用socket实现简单客户端和服务端的通信(基于TCP)
-
C#基于Socket的UDP和TCP处理通信报文开发传输
-
基于Java的Socket类Tcp网络编程实现实时聊天互动程序(三):回车实现数据到发送(详细代码完结)
-
基于Java的Socket类Tcp网络编程实现实时聊天互动程序(二):Tcp通信的过程及代码编写