Python中Socket编程

目录

Python中Socket编程 1

一、Socket概述 1

二、python中socket模块使用 1

三、socket之聊天室 3

四、socket之端口探测 7

五、scapy之tcp端口探测 11

Python中Socket编程

一、Socket概述

socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求。

socket即是一种特殊的文件,socket函数就是对其进行的操作(读/写IO、打开、关闭)

socket模块是针对 服务器端 和 客户端Socket 进行【打开】【读写】【关闭】

二、python中socket模块使用

通给指定地址簇和socket类型来进行创建

Socket地址簇描述
socket.AF_UNIX只能够用于单一的Unix系统进程间通信
socket.AF_INET服务器之间网络通信IPv4
socket.AF_INET6IPv6
socket类型描述
socket.SOCK_STREAM流式socket , for TCP
socket.SOCK_DGRAM数据报式socket , for UDP
socket.SOCK_RAW原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
socket.SOCK_SEQPACKET可靠的连续数据包服务
创建TCP Socket:s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
创建UDP Socket:s=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
Socket常用函数描述
sk.bind(address)将socket绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。
sk.listen(backlog)开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。
sk.connect(address)连接到address处的socket。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
sk.connect_ex(address)同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061
sk.close()关闭socket
sk.recv(bufsize)接受socket的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。
sk.send(string)将string中的数据发送到连接的socket。
sk.sendall(string)将string中的数据发送到连接的socket,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
sk.settimeout(timeout)设置socket操作的超时期,timeout是一个浮点数,单位是秒。超时期应该在刚创建socket时设置.
sk.accept()接受连接并返回(conn,address),其中conn是新的socket对象,可以用来接收和发送数据。address是连接客户端的地址。

三、socket之聊天室

简单收发

编程思路

服务端:

1 创建socket,因为socket是特殊的文件(需要close),所以通过with
as来自动释放文件资源

with socket.socket(socket.AF_INET,socket.SOCK_STREAM) as sk:

2 绑定套接字到本地IP与端口,开始监听连接

s.bind((HOST,PORT))
s.listen()

3接受客户端的连接请求,

conn, addr = s.accept()

4 通过accept创建的新socket对象接收数据并发送给对方数据

为了保证能收到所有发送来的信息,通过while循环来一直接受数据,直到数据为空

服务器端代码

import socket

HOST = '127.0.0.1'
PORT = 33333

with socket.socket(socket.AF_INET,socket.SOCK_STREAM) as s:
    s.bind((HOST,PORT))
    s.listen()
    conn, addr = s.accept()
    with conn:
        print('conn by',addr)
        while True:
            data = conn.recv(1024)
            if not data:
                break
            print(data)
            conn.sendall(b'hello,client')

客户端代码

import socket

HOST = '127.0.0.1'
PORT = 33333

with socket.socket(socket.AF_INET,socket.SOCK_STREAM) as s:
    s.connect((HOST,PORT))
    s.sendall(b'hello,friend')
    data = s.recv(1024)

print('rece',repr(data))
socket聊天室

服务器

问题1:首先服务器能同时连接多个客户端

Socket.listen()后通过while循环

sk.accept()不断接收新的连接并创建socket

问题2:服务器要连接客户端后能一直接受数据,直到接受到空数据

多个连接之间肯定是独立的,并行的

所以通过线程的方式来实现

问题3:服务器要指定客户端端口和地址后进行回复信息

通过while 循环来一直接收input数据来指定发送

但是上面为了处理多个客户端时,也用了while

所以将连接多个客户端写入函数通过线程来实现,使之并行

服务器代码

from socket import *
import threading


def text_recv(CliSock,addr):
    while True:
            data = CliSock.recv(1024)
            print(addr,"send to you:",data)
            if not data:
                break


def socket_up():
    SerSock = socket(AF_INET, SOCK_STREAM)
    SerSock.bind(('127.0.0.1',58787))
    SerSock.listen()
    while True:
        CliSock, addr = SerSock.accept()
        print('connnecting from:', addr)
        Host[str(addr[1])] = CliSock
        t = threading.Thread(target=text_recv,args=(CliSock,addr))
        t.start()


if __name__ == '__main__':
    Host = {}
    t = threading.Thread(target=socket_up)
    t.start()
    while True:
        try:
            target_info = input('please input your target : msg,port >\n ').split(',')
            msg = target_info[0]
            port = target_info[1]
            conn = Host[port]
            conn.sendall(msg.encode())
        except Exception as e:
            print(e)

客户端

同服务端问题3一致

客户端代码

import threading
from socket import *


def text_recv(SerSock):
    while True:
            data = SerSock.recv(1024)
            print("send to you:",data)
            if not data:
                break


if __name__ == '__main__':
    CliSock = socket(AF_INET, SOCK_STREAM)
    CliSock.connect(('127.0.0.1',58787))
    t = threading.Thread(target=text_recv, args=(CliSock,))
    t.start()
    while True:
        data1 = input('please input want send > \n')
        CliSock.send(data1.encode())
        if not data1:
            break

四、socket之端口探测

探测原理:当通过socket连接目标主机未开放端口时会传出异常

例:ConnectionRefusedError: [WinError 10061]
由于目标计算机积极拒绝,无法连接。

编程思路

1 通过线程创建socket,对目标主机的范围端口进行尝试连接

2.对于没有抛出异常的端口尝试接受数据了解端口信息

问题:有些端口开放但不能接收到信息,就会将进程卡在接受数据那

解决:通过给socket对象设置settimeout()属性来限制超时时间,当超过时间时抛出新的异常:即timeout

所以 except timeout 也是存活端口,但是端口信息没有获取到

3.将存活端口和端口信息加入到字典中,最后遍历输出

实现代码

import threading
from socket import *


def port_find(p):
    try:
        with socket(AF_INET, SOCK_STREAM) as sk:
            sk.connect((HOST, p))
            sk.settimeout(3)
            alive[p] = sk.recv(1024).strip().decode()
            print("port[{}] is alive\n".format(p), end='')

    except timeout:
        print("port[{}] is alive\n".format(p), end='')
        alive[p] = 'this not have info'
    except:
        pass


if __name__ == '__main__':
    HOST = '10.30.25.199'
    alive = {}
    threads = []
    for p in range(1, 65535):
        t = threading.Thread(target=port_find, args=(p,))
        threads.append(t)
        t.start()
    for t1 in threads:
        t1.join()
    for k,v in alive.items():
        print(k,'--->',v)

connect_ex探究使用

connect_ex相对于connect来说多了返回值,连接成功时返回 0
,连接失败时候返回编码,例如:10061

那么就可以通过返回值来对是否存活进行判断

但是还是需要接收那些没有数据的端口,所以设置settimeout,然后在进一步尝试执行,成功就记录收到的数据,失败就表示端口开放但是没有数据

实现代码

import threading
from socket import *


def port_find(p):
        with socket(AF_INET, SOCK_STREAM) as sk:
            re = sk.connect_ex((HOST, p))
            sk.settimeout(3)
            if re == 0:
                print("port[{}] is alive\n".format(p), end='')
                try:
                    alive[p] = sk.recv(1024).strip().decode()
                except:
                    alive[p] = 'this not have info'
            else:
                print("port[{}] is dead\n".format(p), end='')


if __name__ == '__main__':
    HOST = '10.30.25.199'
    alive = {}
    threads = []
    for p in range(1, 65535):
        t = threading.Thread(target=port_find, args=(p,))
        threads.append(t)
        t.start()
    for t1 in threads:
        t1.join()
    for k,v in alive.items():
        print(k,'--->',v)

探究:因为线程开启太多会损害性能

那么如何用两个线程去完成socket的端口探测?

这里用到了python的队列(queue)

编程思想

将扫描的端口一次性put加入到队列中

调用二个子线程开始并行

回调函数中while不断利用get从队列取出端口号进行扫描

就实现了两个线程的并行扫描,互不干涉

实现代码

from socket import *
from queue import Queue
import threading


def port_find():
    while not port_que.empty():
        p = port_que.get()
        with socket(AF_INET, SOCK_STREAM) as sk:
            re = sk.connect_ex((HOST, p))
            sk.settimeout(3)
            if re == 0:
                print("port[{}] is alive\n".format(p), end='')
                try:
                    alive[p] = sk.recv(1024).strip().decode()
                except:
                    alive[p] = 'this not have info'
            else:
                print("port[{}] is dead\n".format(p), end='')


if __name__ == '__main__':
    HOST = '192.168.84.130'
    alive = {}
    threads = []
    port_que = Queue()
    for i in range(30):
        port_que.put(i)
    t1 = threading.Thread(target=port_find)
    t2 = threading.Thread(target=port_find)
    threads.append(t1)
    threads.append(t2)
    t1.start()
    t2.start()
    for t in threads:
        t.join()

    for k, v in alive.items():
        print(k, '--->', v)

五、scapy之tcp端口探测

抓包分析

失败流程:1.SYN-> 2.RST,ACK->

image-20210108161148659

成功流程:1.SYN-> 2.SYN,ACK-> 3.ACK->

image-20210108161158565

可知:Socket是通过tcp三次握手进行端口探测

scapy中TCP(flags=‘S’)可以实现第一次握手,发送SYN包

flags=‘SA’–SYN,ACK包,flags=’RA’—RST,ACK包

编程思路

先用scapy构造一个 flags 的值为 S 的报文,S 代表SYN,就是请求建立一个 TCP
连接,在接收到服务器返回的报文中,如果 flagw
上的值为SA(SYN,ACK),那么就代表服务器确定接收到发送的连接请求并同意建立连接,这时候客户端再回应一个AR(ACK,RST)报文,确定接收到服务器的响应,建立连接后又立刻断开。

实现代码

from scapy.all import *
from scapy.layers.inet import IP, TCP


def port_scan(port):
    packet = IP(dst=ip)/TCP(dport=port,flags='S')   # 构造一个 flags 的值为 S 的报文
    result = sr1(packet,timeout=2,verbose=0)
    if result.haslayer('TCP'):
        if result['TCP'].flags == 'SA':   # 判断目标主机是否返回 SYN+ACK
            send = sr1(IP(dst=ip)/TCP(dport=port,flags='AR'),timeout=2,verbose=0) # 向目标主机发送 ACK+RST
            print('[+] {} is open'.format(port))
        elif result['TCP'].flags == 'RA':
            pass


if __name__ == '__main__':
    ip = '192.168.84.130'
    threads = []
    for p in range(1, 30):
        t = threading.Thread(target=port_scan, args=(p,))
        threads.append(t)
        t.start()
    for t in threads:
        t.join()
  • 15
    点赞
  • 95
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
可以使用Pythonsocket模块来实现一个TCP多人聊天室。下面是一个简单的示例代码: ```python import socket import threading def handle_client(client_socket, address): while True: # 接收客户端发送的消息 data = client_socket.recv(1024).decode('utf-8') if not data: # 如果客户端断开连接,则退出循环 break print(f"收到来自{address}的消息:{data}") # 向所有客户端发送消息(广播) for client in clients: client.sendall(data.encode('utf-8')) # 关闭客户端连接 client_socket.close() def start_server(): # 创建套接字 server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind(('localhost', 8888)) server_socket.listen(5) print("服务器已启动,等待客户端连接...") while True: # 接受客户端连接请求 client_socket, address = server_socket.accept() print(f"客户端{address}已连接") # 创建新的线程处理客户端连接 client_thread = threading.Thread(target=handle_client, args=(client_socket, address)) client_thread.start() # 存储所有客户端套接字的列表 clients = [] start_server() ``` 这个示例代码使用了`socket`模块来创建服务器和客户端之间的TCP连接。服务器接受客户端连接请求后,为每个客户端创建一个新的线程,用于处理该客户端的消息收发。当有客户端发送消息时,服务器会将该消息广播给所有连接的客户端。 注意:这只是一个简单的示例,没有处理异常情况和安全性等问题。在实际应用,可能需要进一步完善代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值