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 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() #关闭连接
可以看到在括号中只允许填写一个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()。
执行客户端:
上图是执行客户端后,客户端收到的代码,可以看到是服务器已大写的形式,将数据返回给客户端
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 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()
先运行服务器端来监听
上图客户端输入数据
上图服务器收到的数据
上图客户端输入第二个数据时就卡主了
上图可以看到服务器没有收到客户端第二次发送的数据,服务器端也卡主了。
此时客户端1的连接并没有断开
上图是再次运行客户端建立一个新的连接
上图可以看到新连接的数据
上图是之前的连接,可以看到已经关闭了,表示当前服务器端只能接收会话一个连接的数据(当前先不实现多连接,先实现反复的发送和接收数据)
修改服务器端代码:
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()
上图是服务器收到的数据
已经实现了客户端与服务器的多次数据交互。
在linux中和Windows中断开连接时是有区别的,下面在linux执行上面的客户端和服务器代码
当停止客户端的连接后,服务器端一直在不断的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()
然后下面我们在继续看代码的执行情况
上图统计已大于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()
服务器端匹配了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
等待接收数据......
('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
等待接收数据......
('127.0.0.1', 32826)
数据来了!
recv: 1
recv: 2
recv: 3
recv: 4
recv: 5
recv:
client has lost... #此处表示client1已经断开连接了
('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()
客户端:
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
等待接收数据......
('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 id, 0.7 wa, 0.0 hi, 0.0 si, 0.0 st
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 id, 0.6 wa, 0.0 hi, 0.0 si, 0.0 st
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()
转载于:https://blog.51cto.com/daimalaobing/2051205