Server端:python
# Echo server program import socket HOST = '' # Symbolic name meaning all available interfaces PORT = 50007 # Arbitrary non-privileged port sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock_server.bind((HOST, PORT)) sock_server.listen(1) #开始监听,1表明在容许有一个链接排队,更多的新链接连进来时就会被拒绝 conn, addr = sock_server.accept() #阻塞直到有链接为止,有了一个新链接进来后,就会为这个请求生成一个链接对象 with conn: print('Connected by', addr) while True: data = conn.recv(1024) #接收1024个字节 if not data: break #收不到数据,就break conn.sendall(data) #把收到的数据再所有返回给客户端
Client端:linux
# Echo client program import socket HOST = 'localhost' # The remote host PORT = 50007 # The same port as used by the server client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect((HOST, PORT)) client.sendall(b'Hello, world') data = client.recv(1024) print('Received',data)
先启动Server端,再启动Client端,结果以下:编程
第一次接触就这么交待了,之说了一句话,感受不够过瘾,如何实现更多的交互呢?简单,只须要让客户端不断的发,服务端不断的收就能够了,写个循环搞定。windows
Server端:服务器
# Echo server program import socket HOST = '' # Symbolic name meaning all available interfaces PORT = 50007 # Arbitrary non-privileged port sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock_server.bind((HOST, PORT)) sock_server.listen(1) #开始监听,1表明在容许有一个链接排队,更多的新链接连进来时就会被拒绝 conn, addr = sock_server.accept() #阻塞直到有链接为止,有了一个新链接进来后,就会为这个请求生成一个链接对象 with conn: print('Connected by', addr) while True: data = conn.recv(1024) #接收1024个字节 print("server recv:",conn.getpeername(), data.decode()) if not data: break #收不到数据,就break conn.sendall(data) #把收到的数据再所有返回给客户端
Client端:cookie
# Echo client program import socket HOST = 'localhost' # The remote host PORT = 50007 # The same port as used by the server client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect((HOST, PORT)) while True: msg = input(">>>:").strip() if len(msg) == 0:continue client.sendall(msg.encode()) #发送用户输入的数据,必须是bytes模式 data = client.recv(1024) print('Received',data.decode()) #收到服务器的响应后,decode一下
上面的例子,服务端只是将客户端发来的再发送给客户端,这哪叫聊天啊,这种事须要双方配合,得让服务端也能说话。网络
Server端:并发
import socket HOST = '' # Symbolic name meaning all available interfaces PORT = 50007 # Arbitrary non-privileged port sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock_server.bind((HOST, PORT)) sock_server.listen(1) #开始监听,1表明在容许有一个链接排队,更多的新链接连进来时就会被拒绝 conn, addr = sock_server.accept() #阻塞直到有链接为止,有了一个新链接进来后,就会为这个请求生成一个链接对象 with conn: print('Connected by', addr) while True: data = conn.recv(1024) #接收1024个字节 print("recv from Alex:",conn.getpeername(), data.decode()) if not data: break #收不到数据,就break response = input(">>>").strip() conn.send(response.encode()) print("send to alex:",response)
Client不须要作更改,直接看结果:socket
以上的例子仍是有bug,双方只能一来一往的说话,若是你想来纳许发2句话是不行的,会卡住。这是由于你发了一条消息后,就去调用recv方法接收服务器的响应了,再服务器端返回消息以前,这个recv(1024)方法是阻塞的,若是想容许此时还能再发消息给服务器端,就须要再单独启动一个线程,只负责发消息。tcp
刚才在聊天的时候,服务端在服务客户端的时候,其它人若是也想跟服务端链接是处于排队状态,而后等正在被服务的客户端完事并断开后,下一我的就跟上,但实际状况是客户端一断开,服务端也跟着断了。
为何会断呢?引文服务端如下代码的意思是,若是收不到数据,就跳出循环,就断开了。
conn, addr = sock_server.accept() #阻塞直到有链接为止,有了一个新链接进来后,就会为这个请求生成一个链接对象 with conn: print('Connected by', addr) while True: data = conn.recv(1024) #接收1024个字节 print("recv from Alex:",conn.getpeername(), data.decode()) if not data: break #收不到数据,就break , 就是它干的 response = input(">>>").strip() conn.send(response.encode()) print("send to alex:",response)
想实现一个客户端断开后,能够马上接入另一个客户端的话,怎么办呢?只须要再在外层加个循环。
while True: #最外层loop conn, addr = sock_server.accept() #阻塞直到有链接为止,有了一个新链接进来后,就会为这个请求生成一个链接对象 #为什么把上面这句话也包含在循环里? print("来了个新客人",conn.getpeername() ) with conn: print('Connected by', addr) while True: data = conn.recv(1024) #接收1024个字节 print("recv from :",conn.getpeername(), data.decode()) if not data: break #收不到数据,就break conn.send(data.upper()) print("send to alex:",data)
break 跳出后就回到大while那层:
可是,有的人在重启服务端时可能会遇到:
这是因为你的服务端仍然存在4次挥手的time_wait状态,在占用地址(若是不懂,请深刻研究:一、tcp三次握手,四次挥手。二、sun洪水攻击。三、服务器高并发状况下会有大量的time_wait状态的优化方法)
解决方法1:
sock_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #一行代码搞定,写在bind以前 sock_server.bind((HOST, PORT))
解决方法2(用于Linux系统):
发现系统存在大量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 时间
UDP不须要通过3次握手和4次挥手,不须要提早创建链接,直接发数据就行。
Server端:
import socket ip_port=('127.0.0.1',9000) BUFSIZE=1024 udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #udp类型 udp_server_client.bind(ip_port) while True: msg,addr=udp_server_client.recvfrom(BUFSIZE) print("recv ",msg,addr) udp_server_client.sendto(msg.upper(),addr)
Client端:
import socket ip_port = ('127.0.0.1',9000) BUFSIZE = 1024 udp_server_client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) while True: msg=input('>>: ').strip() if not msg:continue udp_server_client.sendto(msg.encode('utf-8'),ip_port) back_msg,addr = udp_server_client.recvfrom(BUFSIZE) print(back_msg.decode('utf-8'),addr)
结果:
一、TCP基于连接通讯
二、UDP无连接