先看一个实例,上代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#!/usr/bin/env python3 # _*_ coding:utf-8 _*_ import socket sk = socket.socket() sk.bind(("0.0.0.0",9000)) # 把IP绑定到套接字 sk.listen() # 监听链接 print("等待客户端连接....") conn,addr = sk.accept() # 接收客户端链接 print("IP: %s Port: %s 已建立连接..." %(addr[0],addr[1])) client_data = conn.recv(1024) # 接收客户端信息 print(client_data) #打印客户端信息息 conn.send(b"haha") # 向客户端发送信息 conn.close() # 关闭这次的连接 sk.close() # 关闭服务器socket,相当于停止了服务
运行以上代码: 使用 telnet 连接服务端
# 客户端退出连接, 再次启动服务端会报错,说端口已经被占用。
# 查看tcp状态,TIME_WAIT。。什么鬼 程序已经结束了,socket为啥还存在呢? 这是因为tcp协议的可靠性。
解决办法:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
#加入一条socket配置,重用ip和端口 phone=socket(AF_INET,SOCK_STREAM) phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加 phone.bind(('127.0.0.1',8080))
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
vi /etc/sysctl.conf 编辑文件,加入以下内容: net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_fin_timeout = 30 然后执行 /sbin/sysctl -p 让参数生效。 net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭; net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭; net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。 net.ipv4.tcp_fin_timeout 修改系統默认的 TIMEOUT 时间
tcp协议三次握手与四次挥手
三次握手:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
01.客户端: 客户端发送syn信息给服务端,然后客户端从closed状态变为syn_send(请求建立连接)状态(三次握手的第一次握手) 01.服务端: ①服务端从closed(关闭状态)状态转换为listen(监听状态)状态(在服务端开启相应服务),只有在listen状态才可以接收客户端建立连接请求 closed--listen状态,实际上就是创建了一个socket条目信息 ②服务端在listen状态接收到客户端发送的syn请求,会响应syn和ack信息,并且从listen状态变为syn_rcvd状态(三次握手的第二次握手) 02.客户端: 客户端在syn_send状态接收到syn和ack字段信息,然后回复ack确认信息(三次握手的第三次握手) syn_send状态变为最终建立连接的状态(established) 02.服务端: 服务端在syn_rcvd状态接收到了ack字段信息,从syn_rcvd状态变为established
四次挥手:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1.客户端: 在established状态发送fin字段信息给服务端(四次挥手过程第一次挥手) 然后客户端状态转变为fin_wait1(第一次等待:服务端的确认ack信息) 2.服务端: ① 服务端在established状态接收到客户端发送的fin字段信息,从established状态变为close_wait ② 服务端在close_wait发送ack确认字段(四次挥手的第二挥手) 3.客户端 ①客户端在fin_wait1状态接收服务端的ack信息,进入到fin_wait2(第二次等待:) 4.服务端: 服务端在close_wait发送fin断开连接字段给客户端(四次挥手的第三次挥手) 服务端从close_wait变为last_ack状态 5.客户端 客户端在fin_wait2状态接收服务端的fin信息,然后响应ack信息,并将自己的状态转变为time_wait状态 (四次挥手的第四次挥手) 6.服务端: 服务端在last_ack状态接收到客户端发送的ack字段信息之后,就会最终变为closed状态 7.客户端: 在time_wait会等待120秒钟时间,才能进入到closed状态
tcp十一种状态集转换