python socket.socket,python的socket用法_1

  python socket.socket,python的socket用法

  本文介绍了Python网络编程的socket和socketserver,并通过示例代码进行了详细介绍。对大家的学习或者工作都有一定的参考价值,有需要的朋友可以参考一下。

  00-1010 I基于TCP协议的套接字编程1、套接字工作流程1、服务器套接字函数2、客户端套接字函数3、公共套接字函数4、面向锁的套接字方法5、面向文件的套接字函数2、基于TCP协议的套接字编程1、服务器2、客户端3、地址占用问题1、方法1:添加一个套接字配置,复用ip和端口2。方法二:通过调整linux内核参数4,模拟ssh远程执行命令5,粘包1,发送方需要等到缓冲区满了再发送出去,导致粘包2,接收方没有及时收到缓冲区中的包。导致多个包接收6、解决包粘连问题1、先发字节流总大小(低版本)2、自定义定长头(struct模块)2、基于UDP协议的socket编程UDP socket简单示例1、服务器2、客户端3、基于socketserver的socket编程1、基于TCP协议的socket编程1、服务器类2、类3、服务器4、客户端2、基于UDP的协议1、服务器2、客户端4、Python互联网模块

  

目录

  

一、基于TCP协议的socket套接字编程

  让我们从服务器开始。服务器首先初始化套接字,然后绑定到端口,监听端口,调用accept来阻塞,等待客户端连接。此时,如果客户端初始化一个套接字,然后连接到服务器,如果连接成功,则客户端和服务器之间的连接建立。客户端发送数据请求,服务器接收并处理请求,然后向客户端发送响应数据。客户端读取数据,最后关闭连接,一次交互结束,使用以下Python代码实现:

  导入插座

  # socket_family可以是AF_UNIX或AF_INET。Socket_type可以是SOCK_STREAM或者SOCK_DGRAM。一般不填写协议,默认值为0。

  socket.socket(套接字系列,套接字类型,协议=0)

  #获取tcp/ip套接字

  tcpSock=socket.socket(socket。AF_INET,socket。袜子_流)

  #获取udp/ip套接字

  udpSock=socket.socket(socket。AF_INET,socket。SOCK_DGRAM)

  

1、套接字工作流程

  S.bind():将(主机,端口号)绑定到socket s.listen():启动TCP侦听s.accept():被动接受TCP客户端的连接,(阻塞)等待连接的到来。

  

1、 服务端套接字函数

  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():创建一个与该套接字相关的文件

      

      

    2、基于TCP协议的套接字编程

      可以通过netstat -an findstr 8080查看套接字状态

      

      

      

    1、 服务端

      

    import socket

      # 1、买手机

      phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # tcp称为流式协议,udp称为数据报协议SOCK_DGRAM

      # print(phone)

      # 2、插入/绑定手机卡

      # phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)

      phone.bind((127.0.0.1, 8080))

      # 3、开机

      phone.listen(5) # 半连接池,限制的是请求数

      # 4、等待电话连接

      print(start....)

      while True: # 连接循环

      conn, client_addr = phone.accept() # (三次握手建立的双向连接,(客户端的ip,端口))

      # print(conn)

      print(已经有一个连接建立成功, client_addr)

      # 5、通信:收发消息

      while True: # 通信循环

      try:

      print(服务端正在收数据...)

      data = conn.recv(1024) # 最大接收的字节数,没有数据会在原地一直等待收,即发送者发送的数据量必须>0bytes

      # print(===>)

      if len(data) == 0: break # 在客户端单方面断开连接,服务端才会出现收空数据的情况

      print(来自客户端的数据, data)

      conn.send(data.upper())

      except ConnectionResetError:

      break

      # 6、挂掉电话连接

       conn.close()

      # 7、关机

      phone.close()

      # start....

      # 已经有一个连接建立成功 (127.0.0.1, 4065)

      # 服务端正在收数据...

      # 来自客户端的数据 bxad

      # 服务端正在收数据...

      

      

      

    2、 客户端

      

    import socket

      # 1、买手机

      phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

      # print(phone)

      # 2、拨电话

      phone.connect((127.0.0.1, 8080)) # 指定服务端ip和端口

      # 3、通信:发收消息

      while True: # 通信循环

      msg = input(>>: ).strip() # msg=

      if len(msg) == 0: continue

      phone.send(msg.encode(utf-8))

      # print(has send----->)

      data = phone.recv(1024)

      # print(has recv----->)

      print(data)

      # 4、关闭

      phone.close()

      # >>: 啊

      # ba

      # >>: 啊啊

      # bxb0xa1xb0xa1

      # >>:

      

      

    3、地址占用问题

      这个是由于你的服务端仍然存在四次挥手的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、 方法二:通过调整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 时间

      

      

    4、模拟ssh远程执行命令

      服务端通过subprocess执行该命令,然后返回命令的结果。

      服务端:

      

    from socket import *

      import subprocess

      server = socket(AF_INET, SOCK_STREAM)

      server.bind((127.0.0.1, 8000))

      server.listen(5)

      print(start...)

      while True:

      conn, client_addr = server.accept()

      while True:

      print(from client:, client_addr)

      cmd = conn.recv(1024)

      if len(cmd) == 0: break

      print(cmd:, cmd)

      obj = subprocess.Popen(cmd.decode(utf8), # 输入的cmd命令

      shell=True, # 通过shell运行

      stderr=subprocess.PIPE, # 把错误输出放入管道,以便打印

      stdout=subprocess.PIPE) # 把正确输出放入管道,以便打印

      stdout = obj.stdout.read() # 打印正确输出

      stderr = obj.stderr.read() # 打印错误输出

      conn.send(stdout)

      conn.send(stderr)

      conn.close()

      server.close()

      客户端

      

    import socket

      client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

      client.connect((127.0.0.1, 8000))

      while True:

      data = input(please enter your data)

      client.send(data.encode(utf8))

      data = client.recv(1024)

      print(from server:, data)

      client.close()

      输入dir命令,由于服务端发送字节少于1024字节,客户端可以接受。

      输入tasklist命令,由于服务端发送字节多于1024字节,客户端只接受部分数据,并且当你再次输入dir命令的时候,客户端会接收dir命令的结果,但是会打印上一次的剩余未发送完的数据,这就是粘包问题。

      

      

      

    5、粘包

      

      

    1、发送端需要等缓冲区满才发送出去,造成粘包

      发送数据时间间隔很短,数据量很小,会合到一起,产生粘包。

      服务端

      

    # _*_coding:utf-8_*_

      from socket import *

      ip_port = (127.0.0.1, 8080)

      TCP_socket_server = socket(AF_INET, SOCK_STREAM)

      TCP_socket_server.bind(ip_port)

      TCP_socket_server.listen(5)

      conn, addr = TCP_socket_server.accept()

      data1 = conn.recv(10)

      data2 = conn.recv(10)

      print(----->, data1.decode(utf-8))

      print(----->, data2.decode(utf-8))

      conn.close()

      客户端

      

    # _*_coding:utf-8_*_

      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)

      s.send(hello.encode(utf-8))

      s.send(world.encode(utf-8))

      # 服务端一起收到bhelloworld

      

      

    2、接收方不及时接收缓冲区的包,造成多个包接收

      客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包。

      服务端

      

    # _*_coding:utf-8_*_

      from socket import *

      ip_port = (127.0.0.1, 8080)

      TCP_socket_server = socket(AF_INET, SOCK_STREAM)

      TCP_socket_server.bind(ip_port)

      TCP_socket_server.listen(5)

      conn, addr = TCP_socket_server.accept()

      data1 = conn.recv(2) # 一次没有收完整

      data2 = conn.recv(10) # 下次收的时候,会先取旧的数据,然后取新的

      print(----->, data1.decode(utf-8))

      print(----->, data2.decode(utf-8))

      conn.close()

      客户端

      

    # _*_coding:utf-8_*_

      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)

      s.send(hello feng.encode(utf-8))

      

      

    6、解决粘包问题

      

      

    1、先发送的字节流总大小(low版)

      问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据。

      为何low:程序的运行速度远快于网络传输速度,所以在发送一段字节前,先用send去发送该字节流长度,这种方式会放大网络延迟带来的性能损耗。

      服务端:

      

    import socket, subprocess

      server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

      server.bind((127.0.0.1, 8000))

      server.listen(5)

      while True:

      conn, addr = server.accept()

      print(start...)

      while True:

      cmd = conn.recv(1024)

      print(cmd:, cmd)

      obj = subprocess.Popen(cmd.decode(utf8),

      shell=True,

      stderr=subprocess.PIPE,

      stdout=subprocess.PIPE)

      stdout = obj.stdout.read()

      if stdout:

      ret = stdout

      else:

      stderr = obj.stderr.read()

      ret = stderr

      ret_len = len(ret)

       conn.send(str(ret_len).encode(utf8))

      data = conn.recv(1024).decode(utf8)

      if data == recv_ready:

      conn.sendall(ret)

      conn.close()

      server.close()

      客户端:

      

    import socket

      client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

      client.connect((127.0.0.1, 8000))

      while True:

      msg = input(please enter your cmd you want>>>).strip()

      if len(msg) == 0: continue

      client.send(msg.encode(utf8))

      length = int(client.recv(1024))

      client.send(recv_ready.encode(utf8))

      send_size = 0

      recv_size = 0

      data = b

      while recv_size < length:

      data = client.recv(1024)

      recv_size += len(data)

      print(data.decode(utf8))

      

      

    2、自定义固定长度报头(struct模块)

      struct模块解析

      

    import struct

      import json

      # i是格式

      try:

      obj = struct.pack(i, 1222222222223)

      except Exception as e:

      print(e)

      obj = struct.pack(i, 1222)

      print(obj, len(obj))

      # i format requires -2147483648 <= number <= 2147483647

      # bxc6x04x00x00 4

      res = struct.unpack(i, obj)

      print(res[0])

      # 1222

      解决粘包问题的核心就是:为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,对端在接收时,先从缓存中取出定长的报头,然后再取真实数据。

      1、 使用struct模块创建报头:

      

    import json

      import struct

      header_dic = {

      filename: a.txt,

      total_size:111111111111111111111111111111111222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222222223131232,

      hash: asdf123123x123213x

      }

      header_json = json.dumps(header_dic)

      header_bytes = header_json.encode(utf-8)

      print(len(header_bytes))# 223

      # i是格式

      obj = struct.pack(i, len(header_bytes))

      print(obj, len(obj))

      # bxdfx00x00x00 4

      res = struct.unpack(i, obj)

      print(res[0])

      # 223

      2、服务端:

      

    from socket import *

      import subprocess

      import struct

      import json

      server = socket(AF_INET, SOCK_STREAM)

      server.bind((127.0.0.1, 8000))

      server.listen(5)

      print(start...)

      while True:

      conn, client_addr = server.accept()

      print(conn, client_addr)

      while True:

      cmd = conn.recv(1024)

      obj = subprocess.Popen(cmd.decode(utf8),

      shell=True,

      stderr=subprocess.PIPE,

      stdout=subprocess.PIPE)

      stderr = obj.stderr.read()

      stdout = obj.stdout.read()

      # 制作报头

      header_dict = {

      filename: a.txt,

      total_size: len(stdout) + len(stderr),

      hash: xasf123213123

      }

      header_json = json.dumps(header_dict)

      header_bytes = header_json.encode(utf8)

      # 1. 先把报头的长度len(header_bytes)打包成4个bytes,然后发送

      conn.send(struct.pack(i, len(header_bytes)))

      # 2. 发送报头

       conn.send(header_bytes)

      # 3. 发送真实的数据

       conn.send(stdout)

      conn.send(stderr)

      conn.close()

      server.close()

      3、 客户端:

      

    from socket import *

      import json

      import struct

      client = socket(AF_INET, SOCK_STREAM)

      client.connect((127.0.0.1, 8000))

      while True:

      cmd = input(please enter your cmd you want>>>)

      if len(cmd) == 0: continue

      client.send(cmd.encode(utf8))

      # 1. 先收4个字节,这4个字节中包含报头的长度

      header_len = struct.unpack(i, client.recv(4))[0]

      # 2. 再接收报头

      header_bytes = client.recv(header_len)

      # 3. 从包头中解析出想要的东西

      header_json = header_bytes.decode(utf8)

      header_dict = json.loads(header_json)

      total_size = header_dict[total_size]

      # 4. 再收真实的数据

      recv_size = 0

      res = b

      while recv_size < total_size:

      data = client.recv(1024)

      res += data

      recv_size += len(data)

      print(res.decode(utf8))

      client.close()

      

      

    二、基于UDP协议的socket套接字编程

      

    • UDP是无链接的,先启动哪一端都不会报错,并且可以同时多个客户端去跟服务端通信

        

    • UDP协议是数据报协议,发空的时候也会自带报头,因此客户端输入空,服务端也能收到。

        

    • UPD协议一般不用于传输大数据。

        

    • UPD套接字无粘包问题,但是不能替代TCP套接字,因为UPD协议有一个缺陷:如果数据发送的途中,数据丢失,则数据就丢失了,而TCP协议则不会有这种缺陷,因此一般UPD套接字用户无关紧要的数据发送,例如qq聊天。

        

      

      

    UDP套接字简单示例

      

      

    1、服务端

      

    import socket

      server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 数据报协议-》UDP

      server.bind((127.0.0.1, 8080))

      while True:

      data, client_addr = server.recvfrom(1024)

      print(===>, data, client_addr)

      server.sendto(data.upper(), client_addr)

      server.close()

      

      

    2、客户端

      

    import socket

      client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 数据报协议-》UDP

      while True:

      msg = input(>>: ).strip() # msg=

      client.sendto(msg.encode(utf-8), (127.0.0.1, 8080))

      data, server_addr = client.recvfrom(1024)

      print(data)

      client.close()

      

      

    三、基于socketserver实现并发的socket编程

      

      

    1、基于TCP协议

      基于tcp的套接字,关键就是两个循环,一个链接循环,一个通信循环

      socketserver模块中分两大类:server类(解决链接问题)和request类(解决通信问题)。

      

      

    1、 server类

      

      

      

    2、 request类

      

      基于tcp的socketserver我们自己定义的类中的。

      

    • self.server即套接字对象

        

    • self.request即一个链接
    • self.client_address即客户端地址

        

      

      

    3、 服务端

      

    import socketserver

      class MyHandler(socketserver.BaseRequestHandler):

      def handle(self):

      # 通信循环

      while True:

      # print(self.client_address)

      # print(self.request) #self.request=conn

      try:

      data = self.request.recv(1024)

      if len(data) == 0: break

      self.request.send(data.upper())

      except ConnectionResetError:

      break

      if __name__ == __main__:

      s = socketserver.ThreadingTCPServer((127.0.0.1, 8080), MyHandler, bind_and_activate=True)

      s.serve_forever() # 代表连接循环

      # 循环建立连接,每建立一个连接就会启动一个线程(服务员)+调用Myhanlder类产生一个对象,调用该对象下的handle方法,专门与刚刚建立好的连接做通信循环

      

      

    4、 客户端

      

    import socket

      phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

      phone.connect((127.0.0.1, 8080)) # 指定服务端ip和端口

      while True:

      # msg=input(>>: ).strip() #msg=

      msg = client33333 # msg=

      if len(msg) == 0: continue

      phone.send(msg.encode(utf-8))

      data = phone.recv(1024)

      print(data)

      phone.close()

      

      

    2、基于UDP协议

      基于udp的socketserver我们自己定义的类中的

      

    • self.request是一个元组(第一个元素是客户端发来的数据,第二部分是服务端的udp套接字对象),如(b'adsf',)
    • self.client_address即客户端地址

      

    1、 服务端

      

    import socketserver

      class MyHandler(socketserver.BaseRequestHandler):

      def handle(self):

      # 通信循环

      print(self.client_address)

      print(self.request)

      data = self.request[0]

      print(客户消息, data)

      self.request[1].sendto(data.upper(), self.client_address)

      if __name__ == __main__:

      s = socketserver.ThreadingUDPServer((127.0.0.1, 8080), MyHandler)

      s.serve_forever()

      

    2、 客户端

      

    import socket

      client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 数据报协议-》udp

      while True:

      # msg=input(>>: ).strip() #msg=

      msg = client1111

      client.sendto(msg.encode(utf-8), (127.0.0.1, 8080))

      data, server_addr = client.recvfrom(1024)

      print(data)

      client.close()

      

      

    四、Python Internet 模块

      以下列出了 Python 网络编程的一些重要模块:

      协议功能用处端口号Python 模块HTTP网页访问80httplib, urllib, xmlrpclibNNTP阅读和张贴新闻文章,俗称为"帖子"119nntplibFTP文件传输20ftplib, urllibSMTP发送邮件25smtplibPOP3接收邮件110poplibIMAP4获取邮件143imaplibTelnet命令行23telnetlibGopher信息查找70gopherlib, urllib

      到此这篇关于Python网络编程之socket与socketserver的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持盛行IT软件开发工作室。

    郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

    留言与评论(共有 条评论)
       
    验证码: