Python29 Socket1

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 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()  #关闭连接

image_1c1cfd00c6u6hgileeheb15mkm.png-9.9kB
可以看到在括号中只允许填写一个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()。

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

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

image_1c1cii5fn936140q49foun1d8r2q.png-15.8kB
上图是执行客户端后服务器看到的结果

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来转码中文

image_1c1cjouvesa7fret3eb5mo599.png-3.5kB
上图是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()

先运行服务器端来监听
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 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()

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 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()

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

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

上图统计已大于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()

image_1c1d0r7nrvpvip512n8d2s1jdu34.png-17.3kB

image_1c1d0rnps1of41dsn1cbls68lm13h.png-22.1kB
服务器端匹配了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()

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


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

客户端:

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 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()

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值