本文共 12071 字,大约阅读时间需要 40 分钟。
socket 封装了TCP/IP、UDP、FTP、SSH、DHCP等多个协议。
由于socket封装了TCP/IP所以在建立连接的时候,不需要手动去写三次握手/四次握手等代码。
socket.AF_INET 表示IPV4
socket.AF_INET6 表示IPV6
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()用于声明协议类型
我们按住ctrl键点第二个socket可以看到下面的内容可以看到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() #关闭连接可以看到在括号中只允许填写一个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()。
执行服务器:先执行服务器端的代码,因为服务器需要进行监听
执行客户端:
上图是执行客户端后,客户端收到的代码,可以看到是服务器已大写的形式,将数据返回给客户端 上图是执行客户端后服务器看到的结果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来转码中文上图是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()
先运行服务器端来监听
上图客户端输入数据上图服务器收到的数据上图客户端输入第二个数据时就卡主了上图可以看到服务器没有收到客户端第二次发送的数据,服务器端也卡主了。此时客户端1的连接并没有断开 上图是再次运行客户端建立一个新的连接上图可以看到新连接的数据上图是之前的连接,可以看到已经关闭了,表示当前服务器端只能接收会话一个连接的数据(当前先不实现多连接,先实现反复的发送和接收数据)修改服务器端代码: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()上图是客户端发送的数据 上图是服务器收到的数据已经实现了客户端与服务器的多次数据交互。 客户端点击停止来断开连接在服务器端也断开了
在linux中和Windows中断开连接时是有区别的,下面在linux执行上面的客户端和服务器代码
在linux中服务器正在接收和返回数据 上图在linux中客户端正在发送和接收返回的数据 当停止客户端的连接后,服务器端一直在不断的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()
然后下面我们在继续看代码的执行情况
客户端发送和接收数据上图统计已大于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()服务器端匹配了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()客户端这里直接回车后,没有卡主,而是让你重新输入数据
客户端: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()上图为test1.txt文件中的内容; 上图为客户端读取服务器发送过来的数据后,新建的test123123.txt文档,可以看到数据内容相同。
转载于:https://blog.51cto.com/daimalaobing/2051205