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

[经验分享] Python29 Socket1

[复制链接]

尚未签到

发表于 2018-8-5 13:05:26 | 显示全部楼层 |阅读模式
socket 基本信息
  socket 封装了TCP/IP、UDP、FTP、SSH、DHCP等多个协议。
  由于socket封装了TCP/IP所以在建立连接的时候,不需要手动去写三次握手/四次握手等代码。


  • socket IP地址
  socket.AF_INET 表示IPV4 
  socket.AF_INET6  表示IPV6


  • socket 类型
  socket.SOCK_STREAM  # tcp协议
  socket.SOCK_DGRAM   # udp协议
  socket.SOCK_RAW     #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
  可以使用socket.SOCK_RAW伪造IP,来发起泛洪***
  socket.SOCK_RDM  #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。

socket实例
  socket.socket()用于声明协议类型
  我们按住ctrl键点第二个socket可以看到下面的内容
DSC0000.png

  可以看到family表示地址簇,AF_INET表示ipv4; SOCK_STREAM表示TCP; 后面的内容暂时无需了解;
  在socket.socket()这个括号中不指定协议,默认就是IPV4和TCP
  

客户端:  

  
import socket
  
client = socket.socket()  #声明socket类型,同时生成socket连接对象
  

  
client.connect(('localhost',6969))  #设置连接的目标,因为括号中只能写一个元素,所以这里将组ip(localhost)和端口6969两个元素写在一个元组中。
  

  
client.send(b'Hello World!')     #发送数据;在python2中允许发送字符串和字节,但是在python3中只能发送字节(bytes)类型,所以前面的b就是将其转换数据类型为字节
  

  
data = client.recv(1024)    #让客户端可以接收数据,这里值允许接收1024字节;官方建议最大写8192。
  

  
print ('recv:',data)    #打印接收到的数据
  

  
client.close()  #关闭连接
  

  

DSC0001.png

  可以看到在括号中只允许填写一个address元素,所以只能通过元组的方式将多个元素写入。
  

服务器端:  

  
import socket
  

  
server = socket.socket()
  
server.bind(('localhost',6969)) #绑定要监听的端口
  
server.listen()     #开始监听,括号中可以设置监听挂起的client数量,默认不限制。
  

  
print ('等待接收数据......')
  

  
conn,addr = server.accept()     #接受并建立与客户端的连接,程序在此处开始阻塞,只到有客户端连接进来...
  

  
#accept会返回两个值,第一个是连接的标记位(这里赋值给conn),然后#conn就是客户端链接过来而在服务器端为其生成的一个连接实例,多个客户端连接服务器就是对个实例;第二个是对方的地址(这里赋值给addr)
  

  
print (conn,addr)
  

  
print ('数据来了!')
  

  
data = conn.recv(1024)   #让这个实例,接收数据
  

  
print ('recv:',data)
  

  
conn.send(data.upper())     #让这个实例返回数据(大写的形式)
  

  
server.close()  #服务器关闭的话就是这整个服务器关闭,而不是针对实例关闭,所以这里还是要使用server.close(),而不是conn.close()。
  

  

  执行服务器:先执行服务器端的代码,因为服务器需要进行监听
DSC0002.png

  执行客户端:
DSC0003.png

  上图是执行客户端后,客户端收到的代码,可以看到是服务器已大写的形式,将数据返回给客户端
DSC0004.png

  上图是执行客户端后服务器看到的结果
  

print (conn)看到的是: <socket.socket fd=664, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 6969), raddr=('127.0.0.1', 56413)>  

  
print (addr)看到的结果是:('127.0.0.1', 56413)  #这个56413是随机的源端口
  

  
print ('recv:',data)的结果是:recv: b'Hello World!'  #从客户端接收到的数据
  

  

client.send(b'Hello World!') #通过b可以将数据转成字节,但是这里只能转ASCII码,如果转中文就会报错。  

  
client.send('测试测试!'.encode('utf-8'))    #通过encode来转码中文
  

  

DSC0005.png

  上图是print中文的结果,因为是utf-8所以不能正常显示,需要进行decode才可以
  

print ('recv:',data.decode())  #decode(),默认给解码成unicode,所以不用指定编码  

  
执行结果:
  
recv: 测试测试!
  
#解码后可以正常看print的中文数据。
  

  

实现多次重复发送和接收
  之前的代码只能实现一次的发送和接收,那么接下来实现多次发送和接收。
  

客户端:  
import socket
  

  
client = socket.socket()
  

  
client.connect(('localhost',6969))
  

  
while True: #循环,可以使客户端多次发送数据
  info = input('>>:').strip()
  

  client.send(info.encode('utf-8'))
  

  data = client.recv(1024)
  

  print ('recv:',data.decode())
  

  
client.close()
  

  

服务器端:  

  
import socket
  

  
server = socket.socket()
  
server.bind(('localhost',6969))
  
server.listen()
  

  
print ('等待接收数据......')
  
while True:
  

  conn,addr = server.accept()
  

  print (conn,addr)
  

  print ('数据来了!')
  

  data = conn.recv(1024)
  

  print ('recv:',data)
  

  conn.send(data.upper())
  

  
server.close()
  

  

  先运行服务器端来监听
DSC0006.png

  上图客户端输入数据
DSC0007.png

  上图服务器收到的数据
DSC0008.png

  上图客户端输入第二个数据时就卡主了

  上图可以看到服务器没有收到客户端第二次发送的数据,服务器端也卡主了。
  此时客户端1的连接并没有断开
DSC0009.png

  上图是再次运行客户端建立一个新的连接
DSC00010.png

  上图可以看到新连接的数据
DSC00011.png

  上图是之前的连接,可以看到已经关闭了,表示当前服务器端只能接收会话一个连接的数据(当前先不实现多连接,先实现反复的发送和接收数据)
  

修改服务器端代码:  
import socket
  

  
server = socket.socket()
  
server.bind(('localhost',6969))
  
server.listen()
  

  
print ('等待接收数据......')
  

  
conn, addr = server.accept()    #将实例移出while循环,这样就不会重复的去建立实例,而是通过一个实例(连接)来反复的发送数据。
  
#如果该代码还在while中,那么每一次循环都会建立一个新连接,等发送一次数据后,又新建连接,那么之前的连接就不能再发送数据了,只能通过新的连接发送数据。
  
print(conn, addr)
  
print('数据来了!')
  

  
while True:
  data = conn.recv(1024)
  print ('recv:',data.decode())
  conn.send(data.upper())
  

  
server.close()
  

  

DSC00012.png

  上图是客户端发送的数据
DSC00013.png

  上图是服务器收到的数据
  已经实现了客户端与服务器的多次数据交互。
DSC00014.png

  客户端点击停止来断开连接
DSC00015.png

  在服务器端也断开了
  在linux中和Windows中断开连接时是有区别的,下面在linux执行上面的客户端和服务器代码
DSC00016.png

  在linux中服务器正在接收和返回数据
DSC00017.png

  上图在linux中客户端正在发送和接收返回的数据
DSC00018.png

  当停止客户端的连接后,服务器端一直在不断的recv空的数据,进入死循环一直在刷屏;
  

修改一下服务器端的代码:  

  
import socket
  

  
server = socket.socket()
  
server.bind(('localhost',6969))
  
server.listen()
  
print ('等待接收数据......')
  
conn, addr = server.accept()
  
print(conn, addr)
  
print('数据来了!')
  

  
count = 0   #设置一个计数器
  

  
while True:
  data = conn.recv(1024)
  print ('recv:',data.decode())
  conn.send(data.upper())
  count += 1  #每循环一次+1
  if count > 10:break     #当大于10时,断开
  

  
server.close()
  

  

  然后下面我们在继续看代码的执行情况
DSC00019.png

  客户端发送和接收数据
DSC00020.png

  上图统计已大于10,所以会自动停止。
  

修改服务器端代码:  

  
import socket
  

  
server = socket.socket()
  
server.bind(('localhost',6969))
  
server.listen()
  
print ('等待接收数据......')
  
conn, addr = server.accept()
  
print(conn, addr)
  
print('数据来了!')
  

  
count = 0
  

  
while True:
  data = conn.recv(1024)
  print ('recv:',data.decode())
  if not data:     #设立data不为真(也就是空),执行条件下的语句
  print ('client has lost...')
  break
  

  conn.send(data.upper())
  count += 1
  if count > 10:break
  

  
server.close()
  

  

DSC00021.png

DSC00022.png

  服务器端匹配了if not data的条件,然后就break循环了。
  连接断开后客户端和服务器的程序就都结束了。
  

修改服务器端代码:让其当断开连接后,客户端可以随时建立连接发送数据,那么服务器端随时监听  
import socket
  

  
server = socket.socket()
  
server.bind(('localhost', 6969))
  
server.listen()
  

  
print('等待接收数据......')
  

  
while True:
  conn, addr = server.accept()
  print(conn, addr)
  print('数据来了!')
  

  while True:
  data = conn.recv(1024)
  print('recv:', data.decode())
  if not data:
  print("client has lost...")
  break   #这里断开连接就跳出当前循环,到外面的while循环,通过conn, addr = server.accept()可以建立新的连接然后再次发送数据
  

  conn.send(data.upper())
  

  
server.close()
  

  

client1的执行结果:  
test@test-virtual-machine:~$ python3 桌面/A1.py hey
  
>>:1
  
recv: 1
  
>>:2
  
recv: 2
  
>>:3
  
recv: 3
  
>>:4
  
recv: 4
  
>>:5
  
recv: 5
  
>>:
  

  
server端的结果:
  
test@test-virtual-machine:~$ python3 桌面/A2.py
  
等待接收数据......
  
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 6969), raddr=('127.0.0.1', 32826)> ('127.0.0.1', 32826)
  
数据来了!
  
recv: 1
  
recv: 2
  
recv: 3
  
recv: 4
  
recv: 5
  

  
此时client1的连接先不断开,然后在新启一个连接模拟client2
  
client2:
  
test@test-virtual-machine:~$ python3 桌面/A1.py
  
>>:1
  

  
#client2 发送数据后就卡主了(卡主表示当前client2的连接被挂起了),且当前server端也没有接收到来自client2的数据。
  

  
client1:
  
ctrl+c断开连接
  

  
server端:
  
test@test-virtual-machine:~$ python3 桌面/A2.py
  
等待接收数据......
  
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 6969), raddr=('127.0.0.1', 32826)> ('127.0.0.1', 32826)
  
数据来了!
  
recv: 1
  
recv: 2
  
recv: 3
  
recv: 4
  
recv: 5
  
recv:
  
client has lost...      #此处表示client1已经断开连接了
  
<socket.socket fd=5, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 6969), raddr=('127.0.0.1', 32828)> ('127.0.0.1', 32828)
  
数据来了!
  
recv: 1
  
#这里可以看到当client1断开连接后,挂起的client2立刻建立好连接,然后并接收到了来自client2的数据
  
#现在client2就可以与server不断的交互了。
  

  

  当一个连接正在与server交互时,其他的连接就要被挂起等待,那么同时可以挂起几个连接呢?
  

server.listen(5)  #在括号中定义数字,5就表示同一时间可以挂起5个连接  
#不过当前的代码逻辑还实现不了,需要以后在使用异步的时候可以实现
  

修改客户端代码:  
import socket
  
client = socket.socket()
  
client.connect(('localhost',6969))
  

  
while True: #循环,可以使客户端多次发送数据
  info = input('>>:').strip()
  if len(info) == 0:continue  #目前有个问题当客户端什么数据都不填写,直接回车,就会卡主,所以这里要定义一个条件当数据为0时,继续下次循环
  

  client.send(info.encode('utf-8'))
  

  data = client.recv(1024)
  

  print ('recv:',data.decode())
  

  
client.close()
  

  

DSC00023.png

  客户端这里直接回车后,没有卡主,而是让你重新输入数据

通过命令获取远程服务器执行结果
  

客户端:  

  
import socket
  

  
client = socket.socket()
  

  
client.connect(('localhost',6969))
  

  
while True: #循环,可以使客户端多次发送数据
  info = input('>>:').strip()
  if len(info) == 0:continue
  

  client.send(info.encode('utf-8'))
  

  data = client.recv(1024)
  

  print (data.decode())
  

  
client.close()
  

  
服务器端:
  

  
import socket
  
import os
  

  
server = socket.socket()
  
server.bind(('localhost', 6969))
  
server.listen()
  

  
print('等待接收数据......')
  

  
while True:
  conn, addr = server.accept()
  print("新连接:", addr)
  

  while True:
  data = conn.recv(1024)
  

  if not data:
  print("client has lost...")
  break
  print('recv:', data.decode())
  

  res = os.popen(data.decode()).read()    #因为发送过来的是字节,但系统不识别这个字节命令,所以要解码成字符串命令,系统才能识别
  conn.send(res.encode('utf-8'))  #将结果编码后在发送过去,客户端相应的查看结果也要解码
  

  
server.close()
  

  
执行结果:
  
服务器端:
  
test@test-virtual-machine:~$ python3 桌面/A2.py
  
等待接收数据......
  
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 6969), raddr=('127.0.0.1', 58682)> ('127.0.0.1', 58682)
  
数据来了!
  
recv: df
  

  
客户端:
  
test@test-virtual-machine:~$ python3 桌面/A1.py
  
>>:df
  
文件系统          1K-块    已用     可用 已用% 挂载点
  
udev             991244       0   991244    0% /dev
  
tmpfs            203072    6492   196580    4% /run
  
/dev/sda1      19478204 5194164 13271560   29% /
  
tmpfs           1015344     124  1015220    1% /dev/shm
  
tmpfs              5120       0     5120    0% /run/lock
  
tmpfs           1015344       0  1015344    0% /sys/fs/cgroup
  
tmpfs            203072      64   203008    1% /run/user/1000
  
/dev/sr0        1511808 1511808        0  100% /media/test/Ubuntu-Kylin 16.04 LTS amd64
  

  
#可以看到成功的通过执行df命令,并将结果返回
  

  

>>:top  
#客户端执行不了这种动态的命令,因为这个内容一直在变动。不想df执行完成后将结果返回。
  

  
>>:top -bn 1    #只显示1秒钟的数据,接下来不在更新了,所以可以给返回
  
top - 22:11:43 up  1:24,  4 users,  load average: 0.00, 0.01, 0.05
  
Tasks: 292 total,   1 running, 290 sleeping,   0 stopped,   1 zombie

  
%Cpu(s):  0.4 us,  0.5 sy,  0.0 ni, 98.4>  
KiB Mem :  2030688 total,   157464 free,   995464 used,   877760 buff/cache
  
KiB Swap:  1046524 total,  1046524 free,        0 used.   955516 avail Mem
  

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
  3689 test      20   0   43672   3664   3056 R   6.7  0.2   0:00.01 top
  1 root      20   0  185100   5656   3916 S   0.0  0.3   0:02.19 systemd
  2 root      20   0       0      0      0 S   0.0  0.0   0:00.02 kthreadd
  3 root      20   0       0      0      0 S   0.0  0.0   0:00.04 ksoftirqd/0
  5 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/0:0H
  7 root      20   0       0      0      0 S   0.0  0.0   0:06.82 rcu_sched
  8 root      20   0       0      0      0 S   0.0  0.0   0:00.00 rcu_bh
  9 root      rt   0       0
  
>>:
  

  
#通过结果可以看到内容并没有显示全,这是因为设置了字节为1024的原因; 剩下没有显示的内容,会等你再次执行命令(任何命令)后,会发送过来,因为没显示的数据已经被换成了,只能等待下次发送。
  

  
将客户端的代码修改:data = client.recv(102400)   #改成102400然后在测试
  

  
客户端:
  

  
>>:top -bn 1
  
top - 22:19:25 up  1:32,  4 users,  load average: 0.00, 0.01, 0.05
  
Tasks: 292 total,   1 running, 290 sleeping,   0 stopped,   1 zombie

  
%Cpu(s):  0.4 us,  0.5 sy,  0.0 ni, 98.5>  
KiB Mem :  2030688 total,   156592 free,   996028 used,   878068 buff/cache
  
KiB Swap:  1046524 total,  1046524 free,        0 used.   954876 avail Mem
  

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
  1535 test      20   0   44152   4516   2820 S   6.2  0.2   0:05.52 dbus-daemon
  3743 test      20   0   43672   3616   3008 R   6.2  0.2   0:00.01 top
  1 root      20   0  185100   5656   3916 S   0.0  0.3   0:02.22 systemd
  2 root      20   0       0      0      0 S   0.0  0.0   0:00.02 kthreadd
  3 root      20   0       0      0      0 S   0.0  0.0   0:00.04 ksoftirqd/0
  5 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/0:0H
  7 root      20   0       0      0      0 S   0.0  0.0   0:07.14 rcu_sched
  8 root      20   0       0      0      0 S   0.0  0.0   0:00.00 rcu_bh
  9 root      rt   0       0      0      0 S   0.0  0.0   0:00.01 migration/0
  10 root      rt   0       0      0      0 S   0.0  0.0   0:00.04 watchdog/0
  11 root      rt   0       0      0      0 S   0.0  0.0   0:00.04 watchdog/1
  12 root      rt   0       0      0      0 S   0.0  0.0   0:00.01 migration/1
  13 root      20   0       0      0      0 S   0.0  0.0   0:00.10 ksoftirqd/1
  15 root       0 -20       0      0      0 S   0.0  0.0   0:00.00 kworker/1:0H
  16 root      rt   0       0      0      0 S   0.0  0.0   0:00.04 watchdog/2
  17 root      rt   0       0      0      0 S   0.0  0.0   0:00.01 migration/2
  

  
#可以看到一下就将结果都显示出来了,只不过因为太长,这里没有全部粘贴过来。
  

  

  下面代码示例传输文件:
  

客户端:  
import socket
  

  
client = socket.socket()
  

  
client.connect(('localhost',6969))
  

  
f = open('test123123.txt','w')    #打开一个新文件(原来不存在),将服务器端发送过来的数据,写在这个新建的test123123.txt文件中;在while外面打开文件,以免每次循环时都重新打开文件。
  
while True: #循环,可以使客户端多次发送数据
  info = input('>>:').strip()
  if len(info) == 0:continue
  

  client.send(info.encode('utf-8'))
  

  data = client.recv(102400000)  # 设置可以一次收最大100M
  

  f.write(data.decode())  # 发送过来的数据是utf-8,需要decode成unicode(这里要注意:linux系统数据格式默认就是utf-8,所以直接data.decode()是没问题的,但如果是Windows,那么就需要data.decode(gbk))
  f.flush()  # 写入后需要更新内容
  

  
client.close()
  

  
服务器端:
  
import socket
  

  
server = socket.socket()
  

  #不加下面代码,重复执行代码时会报错,显示address used,表示地址正在使用;因为代码执行结束时系统的进程可能还在挂着,所以再次执行代码时,没法复用地址,所以下面的代码就是允许复用地址。
  
server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
  

  
server.bind(('localhost', 6969))
  
server.listen()
  

  
print('等待接收数据......')
  

  
while True:
  conn, addr = server.accept()
  print("新连接:", addr)
  

  while True:
  data = conn.recv(1024)
  

  if not data:
  print("client has lost...")
  break
  print('recv:', data.decode())
  

  f = open('test1.txt','r')
  data = f.read()
  print (len(data))
  conn.sendall(data.encode('utf-8'))
  

  
server.close()
  

  

DSC00024.jpg

  上图为test1.txt文件中的内容;
DSC00025.jpg

  上图为客户端读取服务器发送过来的数据后,新建的test123123.txt文档,可以看到数据内容相同。

运维网声明 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-547035-1-1.html 上篇帖子: python 库安装方法及常用库 下篇帖子: CentOS 6编译安装python 3.6.1
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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