DDOS:从socket编程开始

文章目录

    • 资源
    • 什么是socket
    • TCP 三次握手
    • TCP 四次挥手
    • Python socket编程--------------------
    • TCP本地通信示例
    • Server端函数解析
    • Client端函数解析
    • TCP远程通信实例
    • UDP本地通信示例
    • 总结

资源

官方文档socket
菜鸟教程socket
廖雪峰网络编程

什么是socket

每一条TCP连接有两个端点,UDP也是端点到端点的传输
这个端点不是主机、不是主机IP、不是应用进程、也不是运输层的协议端口
这个端点就是套接字(socket)

套接字socket=IP地址:端口号

每一条TCP连接唯一地被通信两端的两个端点(即套接字socket)确定

TCP连接::={socket1,socket2}={ (IP1,port1) , (IP2,port2) }

同一个IP地址可以有多个TCP连接
同一个端口号也可以出现在多个不同的TCP连接中(Server同一个端口为多个Client服务)

TCP 三次握手

SYN:Synchronize 同步
ACK:ACKnowledge Character 确认字符

第一次:Client发送一个SYN数据包给Server

我(客户端)想连接到你(服务器)

第二次:Server收到后回应一个SYN+ACK数据包给Client

你(客户端)确定想连接到我(服务器)嘛?

第三次:Client回应一个ACK数据包给Server

我(客户端)确定想连接到你(服务器)

为什么是三次握手而非两次
已失效的连接请求报文段” 的产生在这样一种情况下:client 发出的第一个连接请求报文段并没有丢失,而是在某个网络结点长时间的滞留了,以致延误到连接释放以后的某个时间才到达 server。本来这是一个早已失效的报文段。但 server 收到此失效的连接请求报文段后,就误认为是 client 再次发出的一个新的连接请求。于是就向 client 发出确认报文段,同意建立连接。假设不采用 “三次握手”,那么只要 server 发出确认,新的连接就建立了。由于现在 client 并没有发出建立连接的请求,因此不会理睬 server 的确认,也不会向 server 发送数据。但 server 却以为新的运输连接已经建立,并一直等待 client 发来数据。这样,server 的很多资源就白白浪费掉了。采用 “三次握手” 的办法可以防止上述现象发生。例如刚才那种情况,client 不会向 server 的确认发出确认。server 由于收不到确认,就知道 client 并没有要求建立连接。”

TCP 四次挥手

FIN:Finish 结束

第一次:Client发送FIN数据包给Server

我没有数据要发了,已经准备好关闭连接了,你如果没发完可以继续发

第二次:Server发送ACK数据包给Client,收到后Client进入FIN_WAIT状态等待服务器发FIN数据包

好的,我知道你想关闭了,但请你再等我一下,我先把数据发完

第三次:数据发完后,Server发送FIN数据包给Client

数据发完了,我这边也准备好关闭了

第四次:Client发送ACK数据包给Server,进入TIME_WAIT状态

好的,那就关闭吧,不过我怕你没收到确认信号,所以再等等

如果由于网络原因最后的ACK数据包丢失,Server会重发第三次的FIN数据包
如果未丢失则Server进入CLOSED状态,不会再发数据包给Client,Client等待 2MSL未收到FIN数据包后则进入CLOSED状态

Python socket编程--------------------

TCP本地通信示例

Server

import socket
import sys


MySocket=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#socket对象

host = '127.0.0.1'
port = 9999

MySocket.bind((host, port))
# 绑定端口号

MySocket.listen(5)
# 设置最大连接数,超过后排队

while True:
    ClientSocket,addr = MySocket.accept()   
    # 建立客户端连接   
    print(ClientSocket.recv(1024).decode('utf-8'))
    print("连接地址: %s" % str(addr))
    
    msg='Hello World!'+ "\r\n"
    ClientSocket.send(msg.encode('utf-8'))
    ClientSocket.close()

Client

import socket
import sys

ServerSocket=socket.socket(socket.AF_INET, socket.SOCK_STREAM) 

host='127.0.0.1'
port = 9999

ServerSocket.connect((host, port))
# 尝试连接服务端套接字

ServerSocket.send('I am client'.encode('utf-8'))
msg = ServerSocket.recv(1024)
# 接收小于 1024 字节的数据

ServerSocket.close()

print (msg.decode('utf-8'))

Server端函数解析

 socket.socket(family, type, [proto], [fileno])

创建并返回一个socket对象

family:地址族。通常为AF_INET或AF_INET6,分别代表IPV4和IPV6地址族
type:套接字类型。通常为SOCK_STREAM或SOCK_DGRAM,分别代表面向流的TCP和面向数据报的UDP

MySocket.bind((host,port))

(绑定套接字)指定服务端监听哪块网卡的哪个端口
host:网卡IP。注意如果是127.0.0.1的话,客户端程序也必须在本机运行才能访问

MySocket.listen(NUM)

服务端启动监听
NUM:连接池中挂起队列的最大长度,即提前准备好NUM个挂起的连接,要用时直接使用,节省了有客户端连接时创建socket的时间和资源

MySocket.accept()

服务端等待并返回一个客户端的连接
第一个返回值是代表客户端的socket对象,第二个是tuple形式的客户端套接字地址(host,port)

ClientSocket.recv(buffersize)

接收从客户端套接字传来的最多buffersize大小的bytes类型数据,使用decode(‘utf-8’)可解码

ClientSocket.send(MESSAGE)

服务端向客户端的socket对象ClientSocket发送bytes类型的MESSAGE

ClientSocket.close()

服务端关闭客户端的套接字
建议显式close或打开时使用with

Client端函数解析

ServerSocket.connect((host,port))

客户端连接到指定的服务端套接字

ServerSocket.recv(buffersize)

从服务端套接字接收数据并返回

TCP远程通信实例

客户端:Win10 (192.168.0.115)
服务端:CentOS 7 (192.168.0.111)

服务端一定先配置好防火墙把所监听的端口打开!!!

  1. 服务端调用
MySocket.bind('192.168.0.111',9999)

绑定套接字 192.168.0.111:9999

  1. 服务端调用
MySocket.listen()

开始本服务端的套接字192.168.0.111:9999

  1. 客户端调用
ServerSocket.connect(('192.168.0.111',9999))

尝试连接服务端的套接字

  1. 服务端调用
ClientSocket,addr=ServerSocket.accept()

获得客户端的socket对象和套接字并分别用ClientSocket和addr接收
接收到的addr是 (‘192.168.0.115’, 55062)

  1. 服务端调用
ClientSocket.send(MESSAGE)

向客户端发送bytes型MESSAGE

  1. 客户端调用
ServerSocket.recv()

接收MESSAGE
8. 通信结束,服务端使用ClientSocket.close()关闭客户端的套接字连接,继续监听绑定的本服务端套接字MySocket,客户端使用ServerSocket.close()关闭到服务端的套接字

注意:服务端需绑定自己监听的端口,而客户端连接时不需指定自己所使用的端口

UDP本地通信示例

使用UDP时不需要建立连接,只需要知道对方的IP:PORT就可以发送数据报,但是能不能到达就不一定了
Server代码

import socket
MySocket=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
#创建UDP类型的socket对象

MySocket.bind(('127.0.0.1', 9999))
#绑定套接字
#不需要调用listen()方法,而是直接接收客户端数据

while True:
    data,ClientAddr= MySocket.recvfrom(1024)
    #返回值是数据和客户端套接字地址,没有客户端socket对象
    print('Received from %s:%s.' %ClientAddr)
    print('client message is',data.decode('utf-8'))
    
    MySocket.sendto(b'Hello, %s!' % data,ClientAddr)
    #从本机socket向客户端socket发送数据

Client代码

import socket
ServerSocket=socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for data in ['Michael', 'Tracy', 'Sarah']:
    ServerSocket.sendto(data.encode('utf-8'), ('127.0.0.1', 9999))
    #只能发送bytes型的数据
    print(ServerSocket.recv(1024).decode('utf-8'))
ServerSocket.close()

总结

  1. 服务器绑定UDP端口和TCP端口互不冲突
  2. 无论是TCP还是UDP,数据通信使用的都是bytes类型的数据,收到数据后可用decode(‘utf-8’)解码
  3. TCP连接时,注意区分不同的Socket对象,服务端创建MySocket对象用于对外提供连接,并用ClientSocket代表客户端的套接字对象;客户端只有ServerSocket代表服务端的套接字对象,这个套接字对象的地址就是服务端的MySocket地址
  4. TCP连接时,服务端收发数据时使用ClientSocket.send()和ClientSocket.recv()分别表示向客户端套接字对象发送和从客户端套接字对象接收。客户端收发时使用ServerSocket.send()和ServerSocket.recv()分别表示向服务端套接字对象发送和从服务端套接字对象接收
  5. UDP时,服务端调用MySocket.recvrfom(buffersize)返回本套接字对象收到的data和客户端的套接字地址ClientAddr,调用MySocket.sendto(data,ClientAddr)表示本套接字对象向客户端套接字地址发送数据。客户端调用ServerSocket.sendto(data,(IP,port)),表示向服务端套接字对象发送数据,调用ServerSocket.recv(bffersize)表示从服务端套接字对象接收数据

你可能感兴趣的:(DDOS)