Python 构建TCP通讯/TCP转发 您所在的位置:网站首页 pythontcp客户端 Python 构建TCP通讯/TCP转发

Python 构建TCP通讯/TCP转发

2024-01-23 19:09| 来源: 网络整理| 查看: 265

Python 构建TCP通讯 TCP/IP协议

TCP/IP(Transmission Control Protocol/Internet Protocol,传输控制协议/网际协议)是指能够在多个不同网络间实现信息传输的协议簇。TCP/IP协议 不仅仅 指的是TCP 和IP两个协议,而是指一个由FTP、SMTP、TCP、UDP、IP等协议构成的协议簇, 只是因为在TCP/IP协议中TCP协议和IP协议最具代表性,所以被称为TCP/IP协议。

关于Python 的TCP协议处理

老实说python在这种偏底层的网络协议处理上并不占太大优势,他的处理速度相对于C/C++或者别的什么语言来说相对较慢,但胜就胜在简单 wwwwww

使用 Python 实现tcp通讯,一般要通过 Python 的 scoket 模块来实现。

socket 是 Python 自带的源生库,大家并不需要通过 pip 进行下载,直接在代码里 import 就行了。

Python 提供的 socket 模块, 它提供了标准的 BSD Sockets API

socket 类型

先来看一看 socket 的构造器

socket.socket([family[, type[, proto]]]) class socket(_socket.socket): """A subclass of _socket.socket adding the makefile() method.""" __slots__ = ["__weakref__", "_io_refs", "_closed"] def __init__(self, family=-1, type=-1, proto=-1, fileno=None): pass # 以下省略 family:

意义为地址族,决定 socket 的通讯类型,类型以及描述如下

socket 地址族类型地址族描述socket.AF_UNIX只能够用于单一的Unix系统进程间通讯socket.AF_INET服务器之间网络通讯socket.AF_INET6IPv6 type:

意义为 scoket 类型,根据不同的值构建不同的连接,类型以及描述如下

socket 类型类型描述socket.SOCK_STREAM流式的 scoket, 处理 TCP 连接选择这种socket.SOCK_DGAM数据报式的 socket,处理 UDP 连接选择这种socket.SOCK_RAW原始类型的 socket,普通的套接字无法处理 ICMP、IGMP等网络报文,而 SOCK_RAW 可以;同时,SCOK_RAW 也可以处理特殊的 IPv4 报文;利用原始 socket,可以通过 IP_HDRINCL socket选项由用户构造 IP 头。socket.SOCK_SEQPACKET可靠的连续数据包服务 protocol:

一般不填默认为 0。

socket 函数

1)与 HTTP 或 UDP 协议不一样, TCP 发送数据时,已建立好 TCP 连接,所以不需要指定地址。 2)服务端与客户端不能直接发送***字符串***外的数据类型。 3)可用函数如下

服务端函数 socket 函数函数描述bind(address)将 socket 绑定到地址,在 AF_INET 地址族方式下,以元组 (host,port) 的形式表示地址listen(backlog)开始监听 TCP 传入连接。bcaklog 指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为 1,在Win/MAC 系统下有效。Linux系统下默认最大值。accept()接受 TCP 连接并返沪 (conn, address), conn 是新的 socket对象,可以用来接受和发送数据。 address 是连接客户端的地址。 客户端函数 socket 函数函数描述connect(address)连接到address处的套接字。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。connect_ex(address)功能与connect(address)相同,但是成功返回0,失败返回errno的值。 通用函数 socket 函数函数描述recv(bufsize[,flag])接受TCP套接字的数据。数据以 byte 式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。send(byte[,flag])发送TCP数据。将 byte 中的数据发送到连接的 socket。返回值是要发送的字节数量,该数量可能小于string的字节大小。sendall(byte[,flag])完整发送TCP数据。将 byte 中的数据发送到连接的 socket,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。recvfrom(bufsize[.flag])接受UDP socket 的数据。与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的 socket 地址。sendto(string[,flag],address)发送UDP数据。将数据发送到 socket,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。close()关闭 socketgetpeername()返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。getsockname()返回套接字自己的地址。通常是一个元组(ipaddr,port)setsockopt(level,optname,value)设置给定套接字选项的值。getsockopt(level,optname[.buflen])返回套接字选项的值。settimeout(timeout)设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())gettimeout()返回当前超时期的值,单位是秒,如果没有设置超时期,则返回None。fileno()返回套接字的文件描述符。setblocking(flag)如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。makefile()创建一个与该套接字相关连的文件 socket 编程 思路

应当存在一个服务端的 socket ,监听一个端口,接受从客户端建立的连接,以及接收从客户端发送而来的消息,向客户端发送消息。 应当存在一个服务端的 socket,使用一个端口,向服务端请求构建 TCP连接,并且发送消息,并且接收从服务端发送来的消息。

伪代码 服务端:

1.创建 socket, 绑定到本地的IP与端口

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.bind(('127.0.0.1', 8892))

2.开始监听端口

s.listen(5)

3.进入循环,不断接受客户端的连接请求

s.accept()

4.接收数据,并且发送返回信息给客户端

s.recv(1024) s.send()

5.传输完毕或者需求完成后,关闭 socket

s.close() 客户端:

1.创建 socket,连接服务端

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect()

2.发送数据并且接收数据

data = 'data' s.send(data) s.recv()

3.传输完毕或者需求完成后,关闭 socket

s.close() 示意图

在这里插入图片描述

代码 服务端: # server.py import socket class Server: def __init__(self, family, t, port): self.sock = socket.socket(family, t) self.sock.bind(('0.0.0.0', port)) self.sock.listen(10) def accept(self): self.sock.accept() if __name__ == '__main__': server = Server(socket.AF_INET, socket.SOCK_STREAM, 7792) client, client_addr = server.sock.accept() print('来自:' + str(client_addr) + '的连接') data = client.recv(1024) print('收到从:' + str(client_addr) + '的消息:') # 接收到的数据是 byte, 需要转换成 string print(str(data, 'utf-8')) # 发送数据则需要转换成 byte client.send(str('你好,我已收到消息!').encode()) 客户端: # client.py import socket class Client: def __init__(self, family, t, ip, port): self.sock = socket.socket(family, t) self.ip = ip self.port = port def connect(self): self.sock.connect((self.ip, self.port)) def recv(self, size): return self.sock.recv(size) def send(self, byteData): self.sock.send(byteData) def close(self): self.sock.close() if __name__ == '__main__': client = Client(socket.AF_INET, socket.SOCK_STREAM, '127.0.0.1', 7792) client.connect() client.send(str('你好!').encode()) data = client.recv(1024) client.close() print('来自服务端的消息:\n' + str(data, 'utf-8')) 运行结果

先启动服务端,再启动客户端 服务端结果:在这里插入图片描述

客户端结果: 在这里插入图片描述

用线程来操作 socket # 实现 tcp 请求转发 import sys, socket, time, threading, json # 日志锁,使得日志输出线程安全 loglock = threading.Lock() def log(msg, ip): loglock.acquire() try: print('[%s]: \n[IP:%s]\n%s\n' % (time.ctime(), ip, msg.strip())) sys.stdout.flush() finally: loglock.release() # 传输类,source为来源,target为目标 # 转发原理,服务端 与 客户端建立连接, 服务端与转发目标端建立连接 # 服务端得到两个 sock,使用PipeThread 进行消息互转 class PipeThread(threading.Thread): def __init__(self, source, target, addr): threading.Thread.__init__(self) self.source = source self.target = target self.addr = addr def run(self): while True: try: data = self.source.recv(1024) log('datas:', self.addr) print(data) if not data: break self.target.send(data) except Exception as e: print(e) break log('PipeThread done', self.addr) class Forwarding(threading.Thread): def __init__(self, port, targethost, targetport): threading.Thread.__init__(self) self.targethost = targethost self.targetport = targetport self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.bind(('0.0.0.0', port)) self.sock.listen(10) def run(self): while True: client_fd, client_addr = self.sock.accept() target_fd = socket.socket(socket.AF_INET, socket.SOCK_STREAM) target_fd.connect((self.targethost, self.targetport)) log('new connect', client_addr) PipeThread(target_fd, client_fd, '(' + self.targethost + ':' + str(self.targetport) + ")").start() PipeThread(client_fd, target_fd, client_addr).start() # two direct pipe if __name__ == '__main__': Forwarding(8928, '10.110.87.243', 8879).start() sys.exit(1)


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有