## 简介
- WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC
6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。- WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket
API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
三次握手
(1)第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
(2)第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
(3)第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了。⑤SYN攻击:
在三次握手过程中,Server发送SYN-ACK之后,收到Client的ACK之前的TCP连接称为半连接(half-open
connect),此时Server处于SYN_RCVD状态,当收到ACK后,Server转入ESTABLISHED状态。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server回复确认包,并等待Client的确认,由于源地址是不存在的,因此,Server需要不断重发直至超时,这些伪造的SYN包将产时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络堵塞甚至系统瘫痪。SYN攻击时一种典型的DDOS攻击,检测SYN攻击的方式非常简单,即当Server上有大量半连接状态且源IP地址是随机的,则可以断定遭到SYN攻击了。使用如下命令可以让之现行:#netstat -nap | grep SYN_RECV ⑥TCP连接的建立:SYN分节
终止:FIN(finish)分节
四次挥手
由于TCP连接是全双工的,每个方向都必须要单独进行关闭,原则是当一方完成数据发送任务后,发送一个FIN来终止这一方向的连接,收到一个FIN只是意味着这一方向上没有数据流动了,即不会再收到数据了,但是在这个TCP连接上仍然能够发送数据,直到这一方向也发送了FIN。首先进行关闭的一方将执行主动关闭,而另一方则执行被动关闭,上图描述的即是如此。
(1)第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
(2)第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server入CLOSE_WAIT状态。
(3)第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
(4)第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。
TCP(C/S)流程图
①TCP是一种精致的,可靠的字节流协议。
②在TCP编程中,三路握手一般由客户端(Client)调用Connent函数发起。
③TCP3次握手后数据收发通道即打开(即建立了连接)。
TCP群聊(Server端工作原理)
import socket, threading, logging, datetime
FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
logging.basicConfig(format=FORMAT, level=logging.INFO)
class Sserver():
def __init__(self, ip='127.0.0.1', port=8888):
self.addr = (ip, port)
self.sock = socket.socket()
self.cliens = {}
self.event = threading.Event()
def start(self):
self.sock.bind(self.addr)
self.sock.listen()
threading.Thread(target=self.accept, name='accept').start()
def accept(self):
while not self.event.is_set():
s, raddr = self.sock.accept()
logging.info(raddr)
logging.info(s)
self.cliens[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)
except expression as e:
logging.error(e)
data = b'quit'
if data == b'quit':
self.cliens.pop(sock.getpeername())
sock.close()
break
msg = 'bak{}. {} {}'.format(
sock.getpeername(),
datetime.datetime.now().strftime("%Y/%m/%d-%H:%M:%S"),
data.decode()).encode()
for s in self.cliens.values():
s.send(msg)
def stop(self):
for s in self.cliens.values():
s.close()
self.sock.close()
cs = Sserver()
cs.start()
while True:
cmd = input('>>>> ')
if cmd.strip() == 'quit':
cs.stop()
threading.Event.wait(3)
break
logging.info(threading.enumerate())
TCP群聊进阶(socket对象转换为文件)
import socket, threading, logging, datetime
FORMAT = '%(asctime)s %(threadName)s %(thread)d %(message)s'
logging.basicConfig(format=FORMAT, level=logging.INFO)
class Sserver():
def __init__(self, ip='127.0.0.1', port=8888):
self.addr = (ip, port)
self.sock = socket.socket()
self.cliens = {}
self.event = threading.Event()
def start(self):
self.sock.bind(self.addr)
self.sock.listen()
threading.Thread(target=self.accept, name='accept').start()
def accept(self):
while not self.event.is_set():
s, raddr = self.sock.accept()
f = s.makefile(mode='rw')
logging.info(raddr)
logging.info(s)
logging.info(f)
self.cliens[raddr] = f
threading.Thread(target=self.recv, name='recv', args=(f, raddr)).start()
def recv(self, f, addr):
while True:
try:
# data = sock.recv(1024)
data = f.readline()
logging.info(data)
except expression as e:
logging.error(e)
data = b'quit'
if data == b'quit':
self.cliens.pop(addr)
f.close()
break
msg = 'bak{}. {} {}'.format(
addr,
datetime.datetime.now().strftime("%Y/%m/%d-%H:%M:%S"),
data.decode()).encode()
for s in self.cliens.values():
f.write(msg)
f.flush()
def stop(self):
for s in self.cliens.values():
s.close()
self.sock.close()
self.event.set()
def main():
cs = Sserver()
cs.start()
while True:
cmd = input('>>>> ')
if cmd.strip() == 'quit':
cs.stop()
threading.Event.wait(3)
break
logging.info(threading.enumerate())
if __name__ == '__main__':
main()