Python(socket编程——2)
- import socket
- '''
- socket.socket(socket_family,socket_type,protocal=0)
- socket_family 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。protocol 一般不填,默认值为 0。
- 获取tcp/ip套接字
- tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- 获取udp/ip套接字
- udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- 由于 socket 模块中有太多的属性。我们在这里破例使用了'from module import *'语句。使用 'from socket import *',我们就把 socket 模块里的所有属性都带到我们的命名空间里了,这样能 大幅减短我们的代码。
- 例如tcpSock = socket(AF_INET, SOCK_STREAM)
- '''
基于TCP的套接字
tcp服务端
- 1 ss = socket() #创建服务器套接字
- 2 ss.bind() #把地址绑定到套接字
- 3 ss.listen() #监听链接
- 4 inf_loop: #服务器无限循环
- 5 cs = ss.accept() #接受客户端链接
- 6 comm_loop: #通讯循环
- 7 cs.recv()/cs.send() #对话(接收与发送)
- 8 cs.close() #关闭客户端套接字
- 9 ss.close() #关闭服务器套接字(可选)
tcp客户端
- 1 cs = socket() # 创建客户套接字
- 2 cs.connect() # 尝试连接服务器
- 3 comm_loop: # 通讯循环
- 4 cs.send()/cs.recv() # 对话(发送/接收)
- 5 cs.close() # 关闭客户套接字
socket通信流程与打电话流程类似,我们就以打电话为例来实现一个low版的套接字通信
- #_*_coding:utf-8_*_服务端
- __author__ = 'Linhaifeng'
- import socket
- ip_port=('127.0.0.1',9000) #电话卡
- BUFSIZE=1024 #收发消息的尺寸
- s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
- s.bind(ip_port) #手机插卡
- s.listen(5) #手机待机
- conn,addr=s.accept() #手机接电话
- # print(conn)
- # print(addr)
- print('接到来自%s的电话' %addr[0])
- msg=conn.recv(BUFSIZE) #听消息,听话
- print(msg,type(msg))
- conn.send(msg.upper()) #发消息,说话
- conn.close() #挂电话
- s.close() #手机关机
- #_*_coding:utf-8_*_客户端
- __author__ = 'Linhaifeng'
- import socket
- ip_port=('127.0.0.1',9000)
- BUFSIZE=1024
- s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
- s.connect_ex(ip_port) #拨电话
- s.send('linhaifeng nb'.encode('utf-8')) #发消息,说话(只能发送字节类型)
- feedback=s.recv(BUFSIZE) #收消息,听话
- print(feedback.decode('utf-8'))
- s.close() #挂电话
- '''
- 上述流程的问题是,服务端只能接受一次链接,然后就彻底关闭掉了,实际情况应该是,服务端不断接受链接,然后循环通信,通信完毕后只关闭链接,服务器能够继续接收下一次链接,下面是修改版
- '''
- #_*_coding:utf-8_*_
- __author__ = 'Linhaifeng'
- import socket
- ip_port=('127.0.0.1',8081)#电话卡
- BUFSIZE=1024
- s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
- s.bind(ip_port) #手机插卡
- s.listen(5) #手机待机
- while True: #新增接收链接循环,可以不停的接电话
- conn,addr=s.accept() #手机接电话
- # print(conn)
- # print(addr)
- print('接到来自%s的电话' %addr[0])
- while True: #新增通信循环,可以不断的通信,收发消息
- msg=conn.recv(BUFSIZE) #听消息,听话
- # if len(msg) == 0:break #如果不加,那么正在链接的客户端突然断开,recv便不再阻塞,死循环发生
- print(msg,type(msg))
- conn.send(msg.upper()) #发消息,说话
- conn.close() #挂电话
- s.close() #手机关机
- 服务端改进版
- #_*_coding:utf-8_*_
- __author__ = 'Linhaifeng'
- import socket
- ip_port=('127.0.0.1',8081)
- BUFSIZE=1024
- s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
- s.connect_ex(ip_port) #拨电话
- while True: #新增通信循环,客户端可以不断发收消息
- msg=input('>>: ').strip()
- if len(msg) == 0:continue
- s.send(msg.encode('utf-8')) #发消息,说话(只能发送字节类型)
- feedback=s.recv(BUFSIZE) #收消息,听话
- print(feedback.decode('utf-8'))
- s.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 时间
基于UDP的套接字
- udp服务端
- 1 ss = socket() #创建一个服务器的套接字
- 2 ss.bind() #绑定服务器套接字
- 3 inf_loop: #服务器无限循环
- 4 cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送)
- 5 ss.close() # 关闭服务器套接字
- udp客户端
- cs = socket() # 创建客户套接字
- comm_loop: # 通讯循环
- cs.sendto()/cs.recvfrom() # 对话(发送/接收)
- cs.close() # 关闭客户套接字
udp服务端、客户端
- udp套接字简单示例
- #_*_coding:utf-8_*_
- __author__ = 'Linhaifeng'
- import socket
- ip_port=('127.0.0.1',9000)
- BUFSIZE=1024
- udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
- udp_server_client.bind(ip_port)
- while True:
- msg,addr=udp_server_client.recvfrom(BUFSIZE)
- print(msg,addr)
- udp_server_client.sendto(msg.upper(),addr)
- #_*_coding:utf-8_*_
- __author__ = 'Linhaifeng'
- 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)
- qq聊天(由于udp无连接,所以可以同时多个客户端去跟服务端通信)
- #_*_coding:utf-8_*_
- __author__ = 'Linhaifeng'
- import socket
- ip_port=('127.0.0.1',8081)
- udp_server_sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) #买手机
- udp_server_sock.bind(ip_port)
- while True:
- qq_msg,addr=udp_server_sock.recvfrom(1024)
- print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],qq_msg.decode('utf-8')))
- back_msg=input('回复消息: ').strip()
- udp_server_sock.sendto(back_msg.encode('utf-8'),addr)
- #_*_coding:utf-8_*_
- __author__ = 'Linhaifeng'
- import socket
- BUFSIZE=1024
- udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
- qq_name_dic={
- '狗哥alex':('127.0.0.1',8081),
- '瞎驴':('127.0.0.1',8081),
- '一棵树':('127.0.0.1',8081),
- '武大郎':('127.0.0.1',8081),
- }
- while True:
- qq_name=input('请选择聊天对象: ').strip()
- while True:
- msg=input('请输入消息,回车发送: ').strip()
- if msg == 'quit':break
- if not msg or not qq_name or qq_name not in qq_name_dic:continue
- udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])
- back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)
- print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))
- udp_client_socket.close()
- #_*_coding:utf-8_*_
- __author__ = 'Linhaifeng'
- import socket
- BUFSIZE=1024
- udp_client_socket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
- qq_name_dic={
- '狗哥alex':('127.0.0.1',8081),
- '瞎驴':('127.0.0.1',8081),
- '一棵树':('127.0.0.1',8081),
- '武大郎':('127.0.0.1',8081),
- }
- while True:
- qq_name=input('请选择聊天对象: ').strip()
- while True:
- msg=input('请输入消息,回车发送: ').strip()
- if msg == 'quit':break
- if not msg or not qq_name or qq_name not in qq_name_dic:continue
- udp_client_socket.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])
- back_msg,addr=udp_client_socket.recvfrom(BUFSIZE)
- print('来自[%s:%s]的一条消息:\033[1;44m%s\033[0m' %(addr[0],addr[1],back_msg.decode('utf-8')))
- udp_client_socket.close()
recv与recvfrom的区别
- '''
- ============part1:须知============
- 收发消息的原理须知晓--->请见十一的图:发消息,都是将数据发送到己端的发送缓冲中,收消息都是从己端的缓冲区中收
- 1. tcp:send发消息,recv收消息
- 2. udp:sendto发消息,recvfrom收消息
- ============part2:send与sendinto============
- tcp是基于数据流的,而udp是基于数据报的:
- send(bytes_data):发送数据流,数据流bytes_data若为空,自己这段的缓冲区也为空,操作系统不会控制tcp协议发空包
- sendinto(bytes_data,ip_port):发送数据报,bytes_data为空,还有ip_port,所有即便是发送空的bytes_data,数据报其实也不是空的,自己这端的缓冲区收到内容,操作系统就会控制udp协议发包。
- ============part3:recv与recvfrom============
- 1.tcp协议:
- (1)如果收消息缓冲区里的数据为空,那么recv就会阻塞(阻塞很简单,就是一直在等着收)
- (2)只不过tcp协议的客户端send一个空数据就是真的空数据,客户端即使有无穷个send空,也跟没有一个样。
- (3)tcp基于链接通信
- 基于链接,则需要listen(backlog),指定半连接池的大小
- 基于链接,必须先运行的服务端,然后客户端发起链接请求
- 对于mac系统:如果一端断开了链接,那另外一端的链接也跟着完蛋recv将不会阻塞,收到的是空(解决方法是:服务端在收消息后加上if判断,空消息就break掉通信循环)
- 对于windows/linux系统:如果一端断开了链接,那另外一端的链接也跟着完蛋recv将不会阻塞,收到的是空(解决方法是:服务端通信循环内加异常处理,捕捉到异常后就break掉通讯循环)
- 2.udp协议
- (1)如果如果收消息缓冲区里的数据为“空”,recvfrom也会阻塞
- (2)只不过udp协议的客户端sendinto一个空数据并不是真的空数据(包含:空数据+地址信息,得到的报仍然不会为空),所以客户端只要有一个sendinto(不管是否发送空数据,都不是真的空数据),服务端就可以recvfrom到数据。
- (3)udp无链接
- 无链接,因而无需listen(backlog),更加没有什么连接池之说了
- 无链接,udp的sendinto不用管是否有一个正在运行的服务端,可以己端一个劲的发消息,只不过数据丢失
- recvfrom收的数据小于sendinto发送的数据时,在mac和linux系统上数据直接丢失,在windows系统上发送的比接收的大直接报错
- 只有sendinto发送数据没有recvfrom收数据,数据丢失
- ==============注意:===============
- 1.你单独运行上面的udp的客户端,你发现并不会报错,相反tcp却会报错,因为udp协议只负责把包发出去,对方收不收,我根本不管,而tcp是基于链接的,必须有一个服务端先运行着,客户端去跟服务端建立链接然后依托于链接才能传递消息,任何一方试图把链接摧毁都会导致对方程序的崩溃。
- 2.上面的udp程序,你注释任何一条客户端的sendinto,服务端都会卡住,为什么?因为服务端有几个recvfrom就要对应几个sendinto,哪怕是sendinto(b'')那也要有。
- '''
什么是粘包(解法:定长报头)
须知:只有TCP有粘包现象,UDP永远不会粘包,为何,且听我娓娓道来
首先需要掌握一个socket收发消息的原理
所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。
Python(socket编程——2)的更多相关文章
- Python Socket 编程——聊天室示例程序
上一篇 我们学习了简单的 Python TCP Socket 编程,通过分别写服务端和客户端的代码了解基本的 Python Socket 编程模型.本文再通过一个例子来加强一下对 Socket 编程的 ...
- python/socket编程之粘包
python/socket编程之粘包 粘包 只有TCP有粘包现象,UDP永远不会粘包. 首先需要掌握一个socket收发消息的原理 发送端可以是1k,1k的发送数据而接受端的应用程序可以2k,2k的提 ...
- PYTHON SOCKET编程简介
原文地址: PYTHON SOCKET编程详细介绍 Python 提供了两个基本的 socket 模块. 第一个是 Socket,它提供了标准的 BSD Sockets API. 第二个是 Soc ...
- python socket编程笔记
用python实现一个简单的socket网络聊天通讯 (Linux --py2.7平台与windows--py3.6平台) 人生苦短之我用Python篇(socket编程) python之路 sock ...
- [Python_7] Python Socket 编程
0. 说明 Python Socket 编程 1. TCP 协议 [TCP Server] 通过 netstat -ano 查看端口是否开启 # -*-coding:utf-8-*- "&q ...
- Python Socket 编程示例 Echo Server
简评:我们已经从「Python Socket 编程概览」了解了 socket API 的概述以及客户端和服务器的通信方式,接下来让我们创建第一个客户端和服务器,我们将从一个简单的实现开始,服务器将简单 ...
- Python Socket 编程——聊天室演示样例程序
上一篇 我们学习了简单的 Python TCP Socket 编程,通过分别写服务端和client的代码了解主要的 Python Socket 编程模型.本文再通过一个样例来加强一下对 Socket ...
- python socket编程入门(编写server实例)+send 与sendall的区别与使用方法
python 编写server的步骤: 1. 第一步是创建socket对象.调用socket构造函数.如: socket = socket.socket( family, type ) family参 ...
- 第九章:Python高级编程-Python socket编程
第九章:Python高级编程-Python socket编程 Python3高级核心技术97讲 笔记 9.1 弄懂HTTP.Socket.TCP这几个概念 Socket为我们封装好了协议 9.2 cl ...
- python socket编程详细介绍
Python 提供了两个基本的 socket 模块. 第一个是 Socket,它提供了标准的 BSD Sockets API. 第二个是 SocketServer, 它提供了服务器中心类,可以简化网络 ...
随机推荐
- C++ map修改指定key的value
对于修改C++指定key的value,网上查了很多,都说直接insert就会覆盖原来的值,是否是这样的呢? C++ Code 12345678910111213141516171819202122 ...
- Gradle -- buildScript块与allprojects块及根级别的repositories区别
http://blog.sina.com.cn/s/blog_72ef7bea0102vvg3.html
- easyui上传文件
效果图: 代码: <form id="importFileForm" method="post" enctype="multipart/form ...
- 剑指 offer set 16 数字在排序数组中出现的次数
总结 1. Leetcode 上有一道题, 求某一个数字在有序数组中出现的最左位置和最右位置, 而这道题就是那题的变形
- 状态栏,ActionBar,工具栏高度调整
1.在属性中可以这样设置更改ActionBar的高度android:layout_marginTop="?android:attr/actionBarSize" Rect fram ...
- jquery与javescript的区别(一)
一.找元素: <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /&g ...
- poj_2486 动态规划
题目大意 N个节点构成一棵树,每个节点上有一个权重val[i], 从根节点root出发在树上行走,行走的时候只能沿着树枝行进.最多在树上走k步,每第一次到达某个节点j,可以获得val[j]的收益,求从 ...
- maven项目引入jar包
今天看一下maven项目的创建和具体操作.
- you *might* want to use the less safe log_bin_trust_function_creators variable
报错:you *might* want to use the less safe log_bin_trust_function_creators variable 解决方法如下: 1. mysql&g ...
- Maven的使用入门
0.什么是maven? 它是一个软件开发管理工具,主要管理工作是:依赖管理,项目一键构建 1.我们为什么要使用maven? 使用maven构建的项目不包含jar包文件,所以整个项目的体积非常小 mav ...