python_day9_socket

简介:

1、socket 套接字工作流程图

2、收发功能

3、不间断一发一收

4、多客户端连接

5、UDP:收发功能

6、UDP:实现时间功能

7、执行命令

8、黏包


socket 套接字工作流程图

  image.png

先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束


AF_UNIX: 基于文件编程 

    基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信


AF_INET: 基于网络编程 有AF_INET6 ipv6

CS架构


SOCK_STREAM: TCP协议   数据流式通信

SOCK_DGRAM:  UDP协议   数据报式的套接字



## 收发功能 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
socket服务端
 
  import  socket
  cat = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  cat.bind(( '192.168.0.12' , 9090 ))
 
  cat.listen( 4 )
  conn,addr = cat.accept()
 
  msg = conn.recv( 1024 )
  conn.close()
  print ( '接收到的信息: %s' % msg)
  conn.send(msg.upper())
  cat.close()
 
socket客户端
 
  import  socket
  cat = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  cat.connect(( '192.168.0.12' , 9090 ))
  cat.send( 'xiong' .encode( 'utf-8' ))
  data = cat.recv( 1024 )
  print ( '接收到的信息: %s' % data)


#服务端会主动断开 已经传输完客户端数据的连接,将状态改变为TIME_WAIT, 四次挥手之后确定数据已经完全传输完,直接断开并清理状态连接信息


# 收发都是在操作自己的缓存区

# recv 接收的字节, 由recv用户态的应用程序发起

# 回车: 当前 socket 内核态缓存没数据 对端内核态也就没法收到数据,自然也就卡死了


##不间断一发一收

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
socket服务端
 
  import  socket
  cat = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  cat.bind(( '127.0.0.1' , 9009 ))
  cat.listen( 5 )
  conn,addr = cat.accept()
  while  True :
      msg = conn.recv( 1024 )      #回车: 当前 socket 内核态缓存没数据 对端内核态也就没法收到数据,自然也就卡死了
      print ( '接收到的信息: %s' % msg.decode( 'utf-8' ))
      if  msg  = =  b 'q'  break
      inp  =  input ( '输入一个值: ' )
      conn.send(inp.encode( 'utf-8' ))
      continue
  conn.close()
  cat.close()
 
socket客户端
 
  import  socket
  cat = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  cat.connect(( '127.0.0.1' , 9009 ))
  while  True :
      inp  =  input ( '输入一个值: ' )
      if  inp  = =  'q' :
          cat.send(inp.encode( 'utf-8' ))
          break
      cat.send(inp.encode( 'utf-8' ))
      msg  =  cat.recv( 1024 )
      print ( '接收到的信息: %s'  %  msg.decode( 'utf-8' ))
      continue
  cat.close()


## 多客户端连接

# 1、当客户端与服务端建立连接,每次最大客户端连接数由listen控制,我这里最大是5个连接

# 2、多个客户端与服务端建立连接,每次只能有一个客户端与服务端通信,其它队列都会保持在队列中

# 3、unix有些系统使用try except无法解决客户端conn连接突然中断, 可以使用 if not conn: break 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
socket服务端
 
  import  socket
  cat = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  cat.bind(( '192.168.2.192' , 9009 ))
  cat.listen( 5 )
  while  True :      # 客户端退出,如果队列中还有连接那么再重新建立连接
      conn,addr = cat.accept()       # 连接一次
      while  True :      # 与单个用户建立连接
          try :
              msg = conn.recv( 1024 )    # 当客户端关掉连接,而服务端连接却没有中断,它就直接报错 ConnectionResetError: [WinError 10054]
          except  Exception:
              break
          print ( '接收到的信息: %s' % msg.decode( 'utf-8' ))
          if  msg  = =  b 'q'  break
          conn.send(msg.upper())
      conn.close()         # 关闭连接
  cat.close()      # 关闭程序
 
socket客户端
 
  import  socket
  cat = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  cat.connect(( '192.168.2.192' , 9009 ))
  while  True :
      inp  =  input ( '输入一个值: ' )
      if  inp  = =  'q' :
          cat.send(inp.encode( 'utf-8' ))
          break
      cat.send(inp.encode( 'utf-8' ))
      msg  =  cat.recv( 1024 )
      print ( '接收到的信息: %s'  %  msg.decode( 'utf-8' ))
      continue
  cat.close()


UDP:收发功能

1、udp不需要建立accept连接,因为无需三次握手建立一条固定的通道

2、多个客户端同时连接服务端,可同时收发信息

3、udp可以接受空 (直接回车)???


recv在自己这端的缓冲区为空时,阻塞

recvfrom在自己这端的缓冲区为空时,就收一个空


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
UDP_socket服务端
 
  import  socket
 
  ip_port = ( '127.0.0.1' , 9999 )
  udp_sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
  udp_sock.bind(ip_port)
 
  while  True :
      data,addr = udp_sock.recvfrom( 1024 )
      print (data)
      udp_sock.sendto(data,addr)
 
UDP_socket客户端
 
  import  socket
  ip_port = ( '127.0.0.1' , 9999 )
  udp_sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
 
  while  True :
      inp  =  input ( '>>: ' )
      udp_sock.sendto(inp.encode( 'utf-8' ),ip_port)
      data,addr = udp_sock.recvfrom( 1024 )
      print (data)


UDP:实现时间功能

  # 1、实现时间功能

  # 2、data传递进来是二进制的格式,在strftime之前需要先将格式转换回来

  # 3、注意格式转换,发送都是encode,接受基本都是recvfrom

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# udp_socket_server端
 
  import  socket
  import  time
  ip_port = ( '127.0.0.1' , 9001 )
  udp_sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
  udp_sock.bind(ip_port)
 
  while  True :
      data,addr = udp_sock.recvfrom( 1024 )
      print (data)
      if  not  data:
          default_time = '%Y-%m-%d %X'
      else :
          default_time = data.decode( 'utf-8' )
      udp_back_time = time.strftime(default_time)
      udp_sock.sendto(udp_back_time.encode( 'utf-8' ),addr)
 
# udp_socket_client端
  import  socket
  udp_client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
  ip_port = ( '127.0.0.1' , 9001 )    #TypeError: sendto() takes 2 or 3 arguments (1 given)  需要带地址
  while  True :
      inp = input ( '>>: ' )
      udp_client.sendto(inp.encode( 'utf-8' ),ip_port)
      data,addr = udp_client.recvfrom( 1024 )
      print ( '现在时间是: %s' % data.decode( 'utf-8' ))
1
2
3
4
5
6
7
8
# 1、先运行服务端
# 2、再运行客户端打印结果如下:
         现在时间是:  18
         >>:  % Y
         现在时间是:  2018
         >>: 
         现在时间是:  2018 - 01 - 04  20 : 49 : 53
         >>:


### 执行命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# socket TCP服务端
 
  import  socket
  import  subprocess
 
  ip_port = ( '127.0.0.1' , 9001 )
  ip_connect = 5
  buff_size = 1024
 
  command = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  command.bind(ip_port)
  command.listen(ip_connect)
 
  while  True :
    # 客户端断开之后保持重连
      data,addr = command.accept()
      print ( '客户端连接信息: %s'  % data)
      while  True :
          try :
            # 接收客户端传递过来的值
              cmd_value = data.recv(buff_size)
          except  Exception:
              break
          # 执行传递过来的命令,将结果保存到管道对象中赋值给res  
          res = subprocess.Popen(cmd_value.decode( 'utf-8' ),shell = True ,
                               stdout = subprocess.PIPE,
                               stdin = subprocess.PIPE,
                               stderr = subprocess.PIPE)
 
          # 取出stderr的值,如果是空那么执行stdout,不为空说明报错了
          err = res.stderr.read()
          if  err:
              cmd_res = err
          else :
              cmd_res = res.stdout.read()
          if  not  cmd_res:      # 判断如果是类似 cd .. 的命令,它到subprocess值为空
              cmd_res = '命令为空' .encode( 'gbk' )
          data.send(cmd_res)
          continue
      data.close()
  command.close()
 
 
# socket_客户端 
 
  import  socket
 
  ip_port = ( '127.0.0.1' , 9001 )
  ip_connect = 5
  buff_size = 1024
 
  command = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
  command.connect(ip_port)
 
  while  True :
      cmd_inp = input ( '请输入命令: ' ).strip()
      if  not  cmd_inp: command
      if  cmd_inp  = =  'quit' break
      command.send(cmd_inp.encode( 'utf-8' ))
      data = command.recv(buff_size)
      print (data.decode( 'gbk' ))
  command.close()
 
#  最后在客户端这边输入 dir就能看到结果了#  最后在客户端这边输入 dir就能看到结果了


黏包

udp不会黏包,一个recvfrom对应一个sendto,每发送一个包就接收一个包

tcp黏包:优化算法合并了每次send发送的数据, 更优的减少网络负载量, 如果第一次没有收完,那么第二次发送过来的,还是第一次缓存冲区的数据


###### 黏包解决办法 

解决粘包的思路:

1、判断数据的长度,

2、封闭消息头


UDP 数据报: TCP数据流的方式 封装了一个消息头(消息来源地址,发送端的IP+端口) 


应用程序永远不能操作硬件,能操作硬件的只能是操作系统的内核

用户态内存: socket程序

内核态内存: 内核操作硬件


Forking 进程

threading 线程

多进程比多线程的开销更大

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
### 服务端配置
#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
from  socket  import  *
import  subprocess
import  struct
 
ip_port = ( '127.0.0.1' , 9002 )
ip_listen = 5
buff_size = 1024
 
# TCP 流式协议
tcp_server = socket(AF_INET,SOCK_STREAM)
# 绑定并监听端口
tcp_server.bind(ip_port)
# 允许back_log 允许有多少个队列
tcp_server.listen(ip_listen)
 
while  True :
     # 客户端连接信息,当客户端断开之后,服务端应允许其它客户端连接
     conn,addr = tcp_server.accept()
     print ( '客户端连接信息: %s'  % conn)
     while  True :
         try :
             # 如果是空的,或者客户端非法退出,那么就直接退出程序
             data = conn.recv(buff_size)
             if  not  data: break
         except  Exception:
             break
         # subprocess接收到的命令应该是str格式,客户端发送过来的值是bytes格式,
         res = subprocess.Popen(data.decode( 'utf-8' ),shell = True ,
                              stdout = subprocess.PIPE,
                              stderr = subprocess.PIPE,
                              stdin = subprocess.PIPE)
         err = res.stderr.read()
         # 如果这个值里有东西,说明它报错了直接返回它
         if  err:
             cmd_res = err
         else :
             cmd_res = res.stdout.read()
         # 如果为空比如cd .. 那么应该给它返回一个空值
         if  not  cmd_res:
             cmd_res = 'value is null ' .encode( 'utf-8' )
 
         # 取出这个值最大的长度,并传递给客户端
         length = len (cmd_res)
         # ---------------  方法一 ---------------
         # # length=str(length).encode('utf-8')
         # conn.send(length)
 
         # 将最大值给客户端,返回一个值过来,避免黏包
         # retu_data=conn.recv(buff_size)
         # if retu_data == b'ok':
         #     conn.send(cmd_res)
         # --------------- 方法二 ---------------
         # 黏包发送,第一次发送的值,固定为4字节,客户端接收的时候先接收4字节,再取数据
         length_data = struct.pack( 'i' ,length)
         conn.send(length_data)
         conn.send(cmd_res)
 
     conn.close()
tcp_server.close()


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
### 客户端配置
#!/usr/bin/env python
# -*- coding:utf-8 -*-
 
from  socket  import  *
import  struct
ip_port = ( '127.0.0.1' , 9002 )
ip_listen = 5
buff_size = 1024
 
tcp_client = socket(AF_INET,SOCK_STREAM)
tcp_client.connect(ip_port)
 
while  True :
     inp = input ( '请输入命令: ' )
     tcp_client.send(inp.encode( 'utf-8' ))
     # 如果直接回车,为空,那么应该直接让它退出循环重新再来一次
     if  not  inp: continue
     # 如果输入quit,那么直接退出程序
     if  tcp_client  = =  'quit' break
 
     # ---------------  方法一 ---------------
     # 解决黏包
     # length=tcp_client.recv(buff_size)
     # tcp_client.send('ok'.encode('utf-8'))
     #
     # length=int(length.decode('utf-8'))
 
     # ---------------  方法二 ---------------
     # 服务端发送的第一个值是4字节大小的,手动调置一下
     length = tcp_client.recv( 4 )
     # 取出大小
     length = struct.unpack( 'i' ,length)[ 0 ]
 
     # 设置初始值,用于保存
     recv_size = 0
     # 配置二进制的大小
     recv_msg = b''
 
     while  recv_size < length:
         recv_msg + = tcp_client.recv(buff_size)
         recv_size  =  len (recv_msg)
     # data=tcp_client.recv(buff_size)
     print (recv_msg.decode( 'gbk' ))


1
2
3
4
5
6
7
# 结果
请输入命令: cd ..
     value  is  null 
     
请输入命令: ipconfig
     Windows IP 配置
     以太网适配器 本地连接:  xxxxxxxxxxxxxxxxxxxx



     本文转自812374156 51CTO博客,原文链接:http://blog.51cto.com/xiong51/2057579,如需转载请自行联系原作者






相关文章
|
8天前
|
安全 Java 数据处理
Python网络编程基础(Socket编程)多线程/多进程服务器编程
【4月更文挑战第11天】在网络编程中,随着客户端数量的增加,服务器的处理能力成为了一个重要的考量因素。为了处理多个客户端的并发请求,我们通常需要采用多线程或多进程的方式。在本章中,我们将探讨多线程/多进程服务器编程的概念,并通过一个多线程服务器的示例来演示其实现。
|
8天前
|
程序员 开发者 Python
Python网络编程基础(Socket编程) 错误处理和异常处理的最佳实践
【4月更文挑战第11天】在网络编程中,错误处理和异常管理不仅是为了程序的健壮性,也是为了提供清晰的用户反馈以及优雅的故障恢复。在前面的章节中,我们讨论了如何使用`try-except`语句来处理网络错误。现在,我们将深入探讨错误处理和异常处理的最佳实践。
|
2月前
|
网络协议 安全 开发者
Python 中的 Socket 编程
Python 中的 Socket 编程
44 4
|
3月前
|
网络协议 程序员 Python
揭秘Python网络编程:深入理解Socket通信
在当今信息时代,网络通信已经成为人们生活中不可或缺的一部分。而Python作为一种高效、易用的编程语言,自然也能够很好地支持网络编程和Socket通信。本文将介绍Python网络编程与Socket通信的相关知识,包括Socket通信模型、Socket编程接口、网络套接字等内容。
|
3月前
|
网络协议 开发者 Python
Python网络编程与Socket通信:连接世界的无限可能
在当今数字化时代,Python作为一种强大的编程语言,通过网络编程与Socket通信为我们打开了连接世界的无限可能。本文将深入探讨Python网络编程的基础知识、Socket通信的原理以及实际应用,帮助读者更好地理解并运用这一技术。
|
4月前
|
Python
python socket 编程实现猜数字
python socket 编程实现猜数字
30 0
|
3月前
|
数据处理 Python
Python 的网络编程:什么是 Socket 编程?在 Python 中如何实现 Socket 编程?
Python 的网络编程:什么是 Socket 编程?在 Python 中如何实现 Socket 编程?
|
7天前
|
网络协议 Java API
Python网络编程基础(Socket编程)Twisted框架简介
【4月更文挑战第12天】在网络编程的实践中,除了使用基本的Socket API之外,还有许多高级的网络编程库可以帮助我们更高效地构建复杂和健壮的网络应用。这些库通常提供了异步IO、事件驱动、协议实现等高级功能,使得开发者能够专注于业务逻辑的实现,而不用过多关注底层的网络细节。
|
11天前
|
Python
Python网络编程基础(Socket编程)UDP服务器编程
【4月更文挑战第8天】Python UDP服务器编程使用socket库创建UDP套接字,绑定到特定地址(如localhost:8000),通过`recvfrom`接收客户端数据报,显示数据长度、地址和内容。无连接的UDP协议使得服务器无法主动发送数据,通常需应用层实现请求-响应机制。当完成时,用`close`关闭套接字。
|
1月前
|
安全 网络协议 网络安全
Python中的Socket编程
Python中的Socket编程
31 0