设为首页 收藏本站
查看: 862|回复: 0

[经验分享] 第九篇 Python网络编程

[复制链接]

尚未签到

发表于 2018-8-13 07:20:48 | 显示全部楼层 |阅读模式
先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束  2、socket()模块函数用法
  import socket
  socket.socket(socket_family,socket_type,protocal=0)
  socket_family 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。protocol 一般不填,默认值为 0。
  tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 获取tcp/ip套接字
  udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 获取udp/ip套接字
  注意:由于socket模块中有太多的属性。我们在这里破例使用了'from module import *'语句。使用 'from socket import *',我们就把 socket 模块里的所有属性都带到我们的命名空间里了,这样能大幅减短我们的代码。
  例如tcpSock = socket(AF_INET, SOCK_STREAM)
  1)服务端套接字函数
  s.bind()    绑定(主机,端口号)到套接字
  s.listen()  开始TCP监听
  s.accept()  被动接受TCP客户的连接,(阻塞式)等待连接的到来
  2)客户端套接字函数
  s.connect()     主动初始化TCP服务器连接
  s.connect_ex()  connect()函数的扩展版本,出错时返回出错码,而不是抛出异常
  3)公共用途的套接字函数
  s.recv()            接收TCP数据
  s.send()            发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)
  s.sendall()         发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)
  s.recvfrom()        接收UDP数据
  s.sendto()          发送UDP数据
  s.getpeername()     连接到当前套接字的远端的地址
  s.getsockname()     当前套接字的地址
  s.getsockopt()      返回指定套接字的参数
  s.setsockopt()      设置指定套接字的参数
  s.close()           关闭套接字
  4)面向锁的套接字方法
  s.setblocking()     设置套接字的阻塞与非阻塞模式
  s.settimeout()      设置阻塞套接字操作的超时时间
  s.gettimeout()      得到阻塞套接字操作的超时时间
  5)面向文件的套接字的函数
  s.fileno()          套接字的文件描述符
  s.makefile()        创建一个与该套接字相关的文件
  七、基于TCP的套接字
  1、tcp是基于链接的,必须先启动服务端,然后再启动客户端去链接服务端
  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()            # 关闭客户套接字
  2、socket通信流程与打电话流程类似,我们就以打电话为例来实现一个low版的套接字通信
  服务端:
  import socket
  ip_port=('127.0.0.1',9000)  #电话卡
  BUFSIZE=1024                #收发消息的尺寸
  s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #初始化socket买手机
  s.bind(ip_port) #绑定IP端口 手机插卡
  s.listen(5)     #监听端口 手机待机
  conn,addr=s.accept()    #调用accept阻塞,等待客户端连接手机接电话
  print('接到来自%s的电话' %addr[0])
  msg=conn.recv(BUFSIZE)             #听消息,听话
  print(msg,type(msg))
  conn.send(msg.upper())          #发消息,说话
  conn.close()                    #挂电话
  s.close()                       #手机关机
  客户端:
  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('hello nihao'.encode('utf-8'))         #发消息,说话(只能发送字节类型)
  feedback=s.recv(BUFSIZE)                           #收消息,听话
  print(feedback.decode('utf-8'))
  s.close()                                       #挂电话
  3、加上链接循环与通信循环
  服务端:
  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('接到来自%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()                       #手机关机
  客户端:
  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()                                       #挂电话
  4、常见问题
  问题:有的同学在重启服务端时可能会遇到‘Address already in use’的报错?
  原因:这个是由于你的服务端仍然存在四次挥手的time_wait状态在占用地址(如果不懂,请深入研究1.tcp三次握手,四次挥手 2.syn洪水*** 3.服务器高并发情况下会有大量的time_wait状态的优化方法)
  解决方法:
  1)加入一条socket配置,重用ip和端口
  phone=socket(AF_INET,SOCK_STREAM)
  phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
  phone.bind(('127.0.0.1',8080))
  2)发现系统存在大量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的套接字
  1、udp是无链接的,先启动哪一端都不会报错
  udp服务端:
  ss = socket()   #创建一个服务器的套接字
  ss.bind()       #绑定服务器套接字
  inf_loop:       #服务器无限循环
  cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送)
  ss.close()                         # 关闭服务器套接字
  udp客户端:
  cs = socket()   # 创建客户套接字
  comm_loop:      # 通讯循环
  cs.sendto()/cs.recvfrom()   # 对话(发送/接收)
  cs.close()                      # 关闭客户套接字
  2、udp套接字简单示例
  服务端:
  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)
  客户端:
  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先制作一个远程执行命令的程序(1:执行错误命令 2:执行ls 3:执行ifconfig)
  res=subprocess.Popen(cmd.decode('utf-8'),
  shell=True,
  stderr=subprocess.PIPE,
  stdout=subprocess.PIPE)
  结果的编码是以当前所在的系统为准的,如果是windows,那么res.stdout.read()读出的就是GBK编码的,在接收端需要用GBK解码,且只能从管道里读一次结果
  注意:命令ls -l ; lllllll ; pwd 的结果是既有正确stdout结果,又有错误stderr结果
  1、基于tcp的socket,在运行时会发生粘包
  服务端:
  from socket import *
  import subprocess
  ip_port=('127.0.0.1',8080)
  BUFSIZE=1024
  tcp_socket_server=socket(AF_INET,SOCK_STREAM)
  tcp_socket_server.bind(ip_port)
  tcp_socket_server.listen(5)
  while True:
  conn,addr=tcp_socket_server.accept()
  print('客户端',addr)
  while True:
  cmd=conn.recv(BUFSIZE)
  if len(cmd) == 0:break
  res=subprocess.Popen(cmd.decode('utf-8'),shell=True,
  stdout=subprocess.PIPE,
  stdin=subprocess.PIPE,
  stderr=subprocess.PIPE)
  stderr=act_res.stderr.read()
  stdout=act_res.stdout.read()
  conn.send(stderr)
  conn.send(stdout)
  客户端:
  import socket
  BUFSIZE=1024
  ip_port=('127.0.0.1',8080)
  s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  res=s.connect_ex(ip_port)
  while True:
  msg=input('>>: ').strip()
  if len(msg) == 0:continue
  if msg == 'quit':break
  s.send(msg.encode('utf-8'))
  act_res=s.recv(BUFSIZE)
  print(act_res.decode('utf-8'),end='')
  2、基于udp的远程执行命令的程序
  服务端:
  from socket import *
  import subprocess
  ip_port=('127.0.0.1',9003)
  bufsize=1024
  udp_server=socket(AF_INET,SOCK_DGRAM)
  udp_server.bind(ip_port)
  while True:
  cmd,addr=udp_server.recvfrom(bufsize) #收消息
  print('用户命令----->',cmd)
  res=subprocess.Popen(cmd.decode('utf-8'),shell=True,stderr=subprocess.PIPE,stdin=subprocess. PIPE,stdout=subprocess.PIPE) #逻辑处理
  stderr=res.stderr.read()
  stdout=res.stdout.read()
  udp_server.sendto(stderr,addr) #发消息
  udp_server.sendto(stdout,addr)
  udp_server.close()
  客户端:
  from socket import *
  ip_port=('127.0.0.1',9003)
  bufsize=1024
  udp_client=socket(AF_INET,SOCK_DGRAM)
  while True:
  msg=input('>>: ').strip()
  udp_client.sendto(msg.encode('utf-8'),ip_port)
  data,addr=udp_client.recvfrom(bufsize)
  print(data.decode('utf-8'),end='')
  十、什么是粘包
  须知:只有TCP有粘包现象,UDP永远不会粘包,为何,且听我娓娓道来
  1、首先需要掌握一个socket收发消息的原理

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-550870-1-1.html 上篇帖子: python并发编程之多线程理论部分 下篇帖子: 第三篇 Python函数
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表