欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

基于TCP的socket编程实现client和server通信

程序员文章站 2022-06-14 10:18:37
...

基于TCP的socket编程实现client和server通信

.

实验内容:

.

client为单线程、server为多线程(群聊功能)

.

原理:

.

套接字(socket)是一个抽象层,应用程序可以通过它发送或接收数据,可对其进行像对文件一样的打开、读写和关闭等操作。套接字允许应用程序将I/O插入到网络中,并与网络中的其他应用程序进行通信。

.
环境:

.

Python 3.8.2、Windows10、pycharm

.
实验过程:

.

一、先编写服务器实现群聊、

.

基于TCP的socket编程实现client和server通信

  • 成功建立连接、每个线程都有自己文件占位符fd和端口号port:

基于TCP的socket编程实现client和server通信

  • 三个client共同发送消息、验证群聊

基于TCP的socket编程实现client和server通信

  • 群聊实现,每个client都收到来自时间、端口各异的消息

基于TCP的socket编程实现client和server通信

基于TCP的socket编程实现client和server通信

  • 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)
        

.

二、再编写客户端、

.

  • 连接服务器

基于TCP的socket编程实现client和server通信

  • 实现双向数据传输

基于TCP的socket编程实现client和server通信

  • 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连接

基于TCP的socket编程实现client和server通信

基于TCP的socket编程实现client和server通信

  • 验证互相通信

基于TCP的socket编程实现client和server通信

基于TCP的socket编程实现client和server通信

.

.

.

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()。

相关标签: Python学习