博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Python29 Socket1
阅读量:7050 次
发布时间:2019-06-28

本文共 12071 字,大约阅读时间需要 40 分钟。

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可以看到下面的内容
image_1c1cevfvr1upo12lotkqvm3c6p9.png-8.1kB
可以看到family表示地址簇,AF_INET表示ipv4; SOCK_STREAM表示TCP; 后面的内容暂时无需了解;
在socket.socket()这个括号中不指定协议,默认就是IPV4和TCP

客户端:import socketclient = 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()  #关闭连接

image_1c1cfd00c6u6hgileeheb15mkm.png-9.9kB

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

服务器端:import socketserver = 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()。

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

image_1c1cib11v1ss4cer148pf3l81g13.png-6.9kB

执行客户端:

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

image_1c1cii5fn936140q49foun1d8r2q.png-15.8kB

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

print (conn)看到的是: 
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来转码中文

image_1c1cjouvesa7fret3eb5mo599.png-3.5kB

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

print ('recv:',data.decode())  #decode(),默认给解码成unicode,所以不用指定编码执行结果:recv: 测试测试!#解码后可以正常看print的中文数据。

实现多次重复发送和接收

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

客户端:import socketclient = 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 socketserver = 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()

先运行服务器端来监听

image_1c1ckl4f119ka1k8u10p294i1hmfm.png-7.4kB
上图客户端输入数据
image_1c1ckltc418as19vm1ok59u9j5g13.png-14.5kB
上图服务器收到的数据
image_1c1ckmsg41tsf1hbi13c4oro1d3u1g.png-3kB
上图客户端输入第二个数据时就卡主了
image_1c1ckltc418as19vm1ok59u9j5g13.png-14.5kB
上图可以看到服务器没有收到客户端第二次发送的数据,服务器端也卡主了。
此时客户端1的连接并没有断开

image_1c1ckumtrkqiu4p18o1obtmc91t.png-12.7kB

上图是再次运行客户端建立一个新的连接
image_1c1cl06p8b0s1d052lm13tq1pqb2a.png-38.2kB
上图可以看到新连接的数据
image_1c1cl146vm8h3c867h18a61fat2n.png-34.7kB
上图是之前的连接,可以看到已经关闭了,表示当前服务器端只能接收会话一个连接的数据(当前先不实现多连接,先实现反复的发送和接收数据)

修改服务器端代码:import socketserver = 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()

image_1c1clfgtg1m6218ij1stp1gkjll534.png-9.9kB

上图是客户端发送的数据

image_1c1clg7na1chn1j061hg81hfu10d3h.png-19.4kB

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

image_1c1cvp7iu7qe1l46h4u1qir1rj89.png-14.9kB

客户端点击停止来断开连接
image_1c1cvq5pvnfi13f4qegvj991qm.png-18.1kB
在服务器端也断开了

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

image_1c1cvs5uctcu1v9nfjbmd4rd413.png-17.6kB

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

image_1c1cvstk29n9104357u173u1vin1g.png-7.5kB

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

image_1c1cvtp31d9e1qsb13691d9a1g9l1t.png-1.7kB

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

修改一下服务器端的代码:import socketserver = 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()

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

image_1c1d075sukie1dqt1587qv2k0s2a.png-21.3kB

客户端发送和接收数据
image_1c1d08nfrnb1guh14881vds1n8r2n.png-27.2kB

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

修改服务器端代码:import socketserver = socket.socket()server.bind(('localhost',6969))server.listen()print ('等待接收数据......')conn, addr = server.accept()print(conn, addr)print('数据来了!')count = 0while 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:breakserver.close()

image_1c1d0r7nrvpvip512n8d2s1jdu34.png-17.3kB

image_1c1d0rnps1of41dsn1cbls68lm13h.png-22.1kB

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

修改服务器端代码:让其当断开连接后,客户端可以随时建立连接发送数据,那么服务器端随时监听import socketserver = 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>>:1recv: 1>>:2recv: 2>>:3recv: 3>>:4recv: 4>>:5recv: 5>>:server端的结果:test@test-virtual-machine:~$ python3 桌面/A2.py等待接收数据......
('127.0.0.1', 32826)数据来了!recv: 1recv: 2recv: 3recv: 4recv: 5此时client1的连接先不断开,然后在新启一个连接模拟client2client2:test@test-virtual-machine:~$ python3 桌面/A1.py >>:1#client2 发送数据后就卡主了(卡主表示当前client2的连接被挂起了),且当前server端也没有接收到来自client2的数据。client1:ctrl+c断开连接server端:test@test-virtual-machine:~$ python3 桌面/A2.py等待接收数据......
('127.0.0.1', 32826)数据来了!recv: 1recv: 2recv: 3recv: 4recv: 5recv: client has lost... #此处表示client1已经断开连接了
('127.0.0.1', 32828)数据来了!recv: 1#这里可以看到当client1断开连接后,挂起的client2立刻建立好连接,然后并接收到了来自client2的数据#现在client2就可以与server不断的交互了。

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

server.listen(5)  #在括号中定义数字,5就表示同一时间可以挂起5个连接#不过当前的代码逻辑还实现不了,需要以后在使用异步的时候可以实现
修改客户端代码:import socketclient = 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()

image_1c1d2knp517l619821p7e1pgmpmt3u.png-2.2kB

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


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

客户端:import socketclient = 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 socketimport osserver = 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等待接收数据......
('127.0.0.1', 58682)数据来了!recv: df客户端:test@test-virtual-machine:~$ python3 桌面/A1.py>>:df文件系统 1K-块 已用 可用 已用% 挂载点udev 991244 0 991244 0% /devtmpfs 203072 6492 196580 4% /run/dev/sda1 19478204 5194164 13271560 29% /tmpfs 1015344 124 1015220 1% /dev/shmtmpfs 5120 0 5120 0% /run/locktmpfs 1015344 0 1015344 0% /sys/fs/cgrouptmpfs 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.05Tasks: 292 total,   1 running, 290 sleeping,   0 stopped,   1 zombie%Cpu(s):  0.4 us,  0.5 sy,  0.0 ni, 98.4 id,  0.7 wa,  0.0 hi,  0.0 si,  0.0 stKiB Mem :  2030688 total,   157464 free,   995464 used,   877760 buff/cacheKiB 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 1top - 22:19:25 up  1:32,  4 users,  load average: 0.00, 0.01, 0.05Tasks: 292 total,   1 running, 290 sleeping,   0 stopped,   1 zombie%Cpu(s):  0.4 us,  0.5 sy,  0.0 ni, 98.5 id,  0.6 wa,  0.0 hi,  0.0 si,  0.0 stKiB Mem :  2030688 total,   156592 free,   996028 used,   878068 buff/cacheKiB 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 socketclient = 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 socketserver = 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()

Python29 Socket1

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

Python29 Socket1

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

转载于:https://blog.51cto.com/daimalaobing/2051205

你可能感兴趣的文章
JavaScript 异步、栈、事件循环、任务队列
查看>>
图解 React Virtual DOM
查看>>
Spring Boot [组件学习-Spring Data JPA]
查看>>
百度云磁盘CDS、对象存储BOS技术深度解析
查看>>
Deno:来自Node之父的V8 TypeScript运行时
查看>>
姜宁谈红帽绩效考核:不关心员工具体做什么
查看>>
Trello中的Scrum
查看>>
Pivotal发布了具有新应用程序托管工具的Spring Cloud Data 1.6
查看>>
Scala类型系统的目的——Martin Odersky访谈(三)
查看>>
无服务器计算的黑暗面:程序移植没那么容易
查看>>
Ockam为物联网设备带来区块链无服务器身份识别
查看>>
Agile Consortium的营销交流章
查看>>
Java二十年历程回顾
查看>>
干研发更喜欢无服务器,搞DevOps偏爱容器?
查看>>
《领导力敏捷》作者访谈
查看>>
Vue2.0 学习笔记
查看>>
研究人员发现:基于文本的AI模型容易受到改述攻击
查看>>
物联网技术周报第 103 期: DIY 智能音箱:基于 Raspberry Pi + Snowboy + AVS
查看>>
Creating Great Teams作者问答
查看>>
Azure编配器简化有状态无服务器工作流的创建
查看>>