标签:启用 处理 存在 close 基于 对象 win 溢出 阻塞
c/s架构
server端要:
1、力求一直提供服务
2、要绑定一个唯一的地址,让客户端能明确找到
数据链路层:以太网协议,进行数据分组,head部分包含mac地址,有了mac地址在子网内就可以以广播的方式通信
网络层:基于IP协议,arp协议
传输层:tcp、udp协议
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。
也有人将socket说成ip+port,ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序,ip地址是配置到网卡上的,而port是应用程序开启的,ip与port的绑定就标识了互联网中独一无二的一个应用程序
而程序的pid是同一台机器上不同进程或者线程的标识
基于文件类型的套接字家族:套接字家族的名字:AF_UNIX
基于网络类型的套接字家族:套接字家族的名字:AF_INET
先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束
tcp服务端
ss = socket() #创建服务器套接字 ss.bind() #把地址绑定到套接字 ss.listen() #监听链接 inf_loop: #服务器无限循环 cs = ss.accept() #接受客户端链接 comm_loop: #通讯循环 cs.recv()/cs.send() #对话(接收与发送) cs.close() #关闭客户端套接字 ss.close() #关闭服务器套接字(可选)
tcp客户端
cs = socket() # 创建客户套接字 cs.connect() # 尝试连接服务器 comm_loop: # 通讯循环 cs.send()/cs.recv() # 对话(发送/接收) cs.close() # 关闭客户套接字
socket通信流程与打电话流程类似,我们就以打电话为例来实现一个low版的套接字通信:
#服务端
import socket phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#socket.AF_INET 基于网络的套接字,socket.SOCK_STREAM基于TCP协议的套接字 买手机 phone.bind((‘127.0.0.1‘,8080)) #绑定手机卡 phone.listen(5) #开机 ?5
conn,addr=phone.accept() #等待电话链接,连接的对象和客户端地址
print(‘电话线路是‘,conn)
print(‘客户端的手机号是‘,addr)
data=conn.recv(1024) #收消息 ?1024
conn.send(data.upper())
conn.close() #挂电话
phone.close() #手机关机
#客户端
import socket phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.connect((‘127.0.0.1‘,8080))
data=phone.recv(1024)
print(data)
phone.close()
上述流程的问题是,服务端只能接受一次链接,然后就彻底关闭掉了,实际情况应该是,服务端不断接受链接,然后循环通信,通信完毕后只关闭链接,服务器能够继续接收下一次链接,下面是修改版:
#服务端
import socket phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机 phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #在bind前加,重启释放地址 phone.bind((‘127.0.0.1‘,8080)) #绑定手机卡 phone.listen(5) #开机,在缓存中可以缓存5个连接请求 print(‘starting....‘) while True: #链接循环 conn,addr=phone.accept() #等待电话链接 print(‘电话线路是‘,conn) print(‘客户端的手机号是‘,addr) while True: #通信循环 try: #应对windows系统 data=conn.recv(1024) #收消息,最大一次接受1024个字节 #if not data:break #linux系统中,客户端断了链接会date接收空,服务端陷入循环 print(‘客户端发来的消息是‘,data) conn.send(data.upper()) except Exception: break conn.close() phone.close()
import socket phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM) phone.connect((‘127.0.0.1‘,8080)) while True: #通信循环 msg=input(‘>>: ‘).strip() if not msg:continue phone.send(msg.encode(‘utf-8‘)) data=phone.recv(1024) print(data) phone.close()
问题:
有的同学在重启服务端时可能会遇到
这个是由于你的服务端仍然存在四次挥手的time_wait状态在占用地址(如果不懂,请深入研究1.tcp三次握手,四次挥手 2.syn洪水攻击 3.服务器高并发情况下会有大量的time_wait状态的优化方法)
解决方法:
#加入一条socket配置,重用ip和端口 phone=socket(AF_INET,SOCK_STREAM) phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加 phone.bind((‘127.0.0.1‘,8080))
发现系统存在大量TIME_WAIT状态的连接,通过调整linux内核参数解决, 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 时间
标签:启用 处理 存在 close 基于 对象 win 溢出 阻塞
原文地址:http://www.cnblogs.com/domestique/p/6803302.html