socket
socket
时间: 分类:技术博客

1.什么是socket

socket是应用层与TCP/IP协议族通信的中间软件层,是一组接口。
用户在使用时,不需要操作具体的TCP协议的内容,用户只需要按照socket的要求操作,就能进行数据的发送和接收。

socket

2.socket通信

socket

3.socket方法

3.1socket实例化相关内容

1.socket实例化:
socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
2.family():
    socket.AF_UNIX:用于本地进程间通讯
    socket.AF_INET:普遍使用这个,主要用于网络通信
3.type:
    socket.SOCK_STREAM:用于TCP协议
    socket.SOCK_DGRAM:用于UDP协议
4.proto=0忽略:
5.fileno=None忽略:

3.2服务端套接字函数

1.s.bind(("127.0.0.1",9999)):
                绑定主机和端口到套接字
2.s.listen(5):
    开始TCP监听。这里是开启5个监听连接,可允许5个客户端连接
3.s.accep():
    等待客户端连接

3.3客户端套接字函数

1.s.connect():
    连接服务端
2.s.connect_ex():
    s.connect()函数的扩展版本,出错时返回出错编码,不抛出异常
"s.connect()"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
import socket
socket_obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
data = socket_obj.connect(("127.0.0.1",9999))
print(data)

D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/test.py
Traceback (most recent call last):
  File "E:/PythonProject/new-python/python-test/BasicGrammer/test.py", line 3, in <module>
    data = socket_obj.connect(("127.0.0.1",9999))
ConnectionRefusedError: [WinError 10061] 由于目标计算机积极拒绝,无法连接。

Process finished with exit code 1
"s.connect_ex()"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
import socket
socket_obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
data = socket_obj.connect_ex(("127.0.0.1",9999))
print(data)

D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/test.py
10061

Process finished with exit code 0

3.4公共用途的套接字函数

1.s.receive(1024):
    结束多少字节的数据
2.s.send():
    两次连着发送,数据会粘包,例如客户端send(3),send(4),连续两次send(),服务端接受recv(1024),就一次性把数据全都接收到了
    send在待发送的数据字节数大于本端缓存区剩余空间时,数据丢失,不会发送完全

3.s.sendall():
    发送完整的TCP数据,本质是循环调用send()
    sendall在待发送数据字节数大于本端缓存区剩余空间时,数据不会丢失,循环调用send()直到发送完成
4.s.recvfrom():
    back_msg,addr = udp_server_client.recvfrom(BUFSIZE)
    会接收数据和对方的地址
5.s.close():
    关闭连接

4.socket实例

4.1基本socket例子

"server.py"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
import socket
sock_obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock_obj.bind(("127.0.0.1",9999))
sock_obj.listen(5)

conn,client_addr = sock_obj.accept()
print(type(conn),client_addr)
data = conn.recv(1024)
print(type(data))
conn.sendall(data)
"client.py"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
import socket
socket_obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket_obj.connect_ex(("127.0.0.1",9999))
# 这里要发送bytes类型数据
socket_obj.send("hello".encode("utf-8"))
data = socket_obj.recv(1024)
print(data.decode("utf-8"))

socket

4.2循环接收

"server"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
import socket
sock_obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock_obj.bind(("127.0.0.1",9999))
sock_obj.listen(5)

conn,client_addr = sock_obj.accept()
print(type(conn),client_addr)
while True:
    data = conn.recv(1024)
    if not data:
        break
    print(type(data))
    conn.sendall(data)
"client"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
import socket
socket_obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket_obj.connect_ex(("127.0.0.1",9999))
while True:
    data = input("please input data:").strip()
    if not data:
        continue #客户端发空消息时,服务端是接受不到的,就卡在那里,客户端也等不到服务端的回复,所以客户端也卡主了
    # 这里要发送bytes类型数据
    socket_obj.send(data.encode("utf-8"))
    data = socket_obj.recv(1024)
    print(data.decode("utf-8"))

socket

4.5简单的单人聊天程序

"server"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
import socket
sock_obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock_obj.bind(("127.0.0.1",9999))
sock_obj.listen(5)

conn,client_addr = sock_obj.accept()
print(type(conn),client_addr)
while True:
    data = conn.recv(1024)
    if not data:
        break
    print("receive:",data.decode("utf-8"))
    send_data = input("plese send your data:").strip()

    conn.sendall(send_data.encode("utf-8"))
"client"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
import socket
socket_obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket_obj.connect_ex(("127.0.0.1",9999))
while True:
    data = input("please input data:").strip()
    if not data:
        continue 
    # 这里要发送bytes类型数据
    socket_obj.send(data.encode("utf-8"))
    data = socket_obj.recv(1024)
    print(data.decode("utf-8"))

socket

4.6多人聊天

上面只允许一个连接,如果客户端断开连接,服务端也同时退出了
只因为服务端if not data: break,接收不到数据,就跳出循环了。

socket

"一个人断开连接后,还允许其他人连接"

"server"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
import socket
sock_obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock_obj.bind(("127.0.0.1",9999))
sock_obj.listen(5)
while True:
    conn,client_addr = sock_obj.accept()# 阻塞直到有链接,有了连接后,就会为请求生成一个连接对象
    print(type(conn),client_addr)
    while True:
        try:
            data = conn.recv(1024)
            if not data:
                print("not data")
                break #适用于linux操作系统,如果客户端断开,Linux系统是陷入死循环
            print("receive:",data.decode("utf-8"))

            conn.sendall(data)
        except Exception as e: #适用于windows操作系统,如果客户端断开,windows系统是报异常
            break
"client"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
import socket
socket_obj = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
socket_obj.connect_ex(("127.0.0.1",9999))
while True:
    data = input("please input data:").strip()
    if not data:
        continue
    # 这里要发送bytes类型数据
    socket_obj.send(data.encode("utf-8"))
    data = socket_obj.recv(1024)
    print(data.decode("utf-8"))

socket

4.7udp案例

"server"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
import socket
sock_obj = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock_obj.bind(("127.0.0.1",9999))
data,client_addr =sock_obj.recvfrom(1024)
print(data,client_addr)
sock_obj.sendto(data.upper(),client_addr)
"client"
#!/usr/bin/env python
# -*- coding:utf-8 -*-
# Author: vita
import socket
socket_obj = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
socket_obj.sendto("hello".encode("utf-8"),("127.0.0.1",9999))
data,server_addr =socket_obj.recvfrom(1024)

print(data,server_addr)

socket

4.8TCP and UDP

"tcp基于链接通信"

1.基于链接,则需要listen(backlog),指定连接池的大小
2.基于链接,必须先运行的服务端,然后客户端发起链接请求
3.对于mac系统:如果一端断开了链接,那另外一端的链接也跟着完蛋recv将不会阻塞,收到的是空(解决方法是:服务端在收消息后加上if判断,空消息就break掉通信循环)
4.对于windows/linux系统:如果一端断开了链接,那另外一端的链接也跟着完蛋recv将不会阻塞,收到的是空(解决方法是:服务端通信循环内加异常处理,捕捉到异常后就break掉通讯循环)
udp无链接

1.无链接,因而无需listen(backlog),更加没有什么连接池之说了
2.无链接,udp的sendinto不用管是否有一个正在运行的服务端,可以己端一个劲的发消息,只不过数据丢失
3.recvfrom收的数据小于sendinto发送的数据时,在mac和linux系统上数据直接丢失,在windows系统上发送的比接收的大直接报错
只有sendinto发送数据没有recvfrom收数据,数据丢失

5.粘包

5.1粘包问题-ssh命令

"server"
import socket
import subprocess

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',9901)) #0-65535:0-1024给操作系统使用
phone.listen(5)

print('starting...')
while True: # 链接循环
    conn,client_addr=phone.accept()
    print(client_addr)

    while True: #通信循环
        try:
            #1、收命令
            cmd=conn.recv(1024)
            if not cmd:break #适用于linux操作系统

            #2、执行命令,拿到结果
            obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)

            stdout=obj.stdout.read()
            stderr=obj.stderr.read()

            #3、把命令的结果返回给客户端
            print(len(stdout)+len(stderr))
            conn.send(stdout+stderr) #+是一个可以优化的点

        except ConnectionResetError: #适用于windows操作系统
            break
    conn.close()

phone.close()

D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/server.py
starting...
('127.0.0.1', 12105)
13652
617
617
617
617
617
617
617
617
617
617
617
617
617
617
617
"client"
import socket

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

phone.connect(('127.0.0.1',9901))

while True:
    #1、发命令
    cmd=input('>>: ').strip() #ls /etc
    if not cmd:continue
    phone.send(cmd.encode('utf-8'))

    #2、拿命令的结果,并打印
    data=phone.recv(1024) #1024是一个坑
    print(data.decode('gbk')) #Windows系统默认是gbk编码,linux上是"utf-8"

phone.close()

D:\software2\Python3\install\python.exe E:/PythonProject/new-python/python-test/BasicGrammer/client.py
>>: tasklist

映像名称                       PID 会话名              会话#       内存使用 
========================= ======== ================ =========== ============
System Idle Process              0 Services                   0          8 K
System                           4 Services                   0        152 K
Registry                        96 Services                   0     73,504 K
smss.exe                       352 Services                   0      1,060 K
csrss.exe                      552 Services                   0      4,676 K
wininit.exe                    652 Services                   0      5,892 K
csrss.exe                      660 Console                    1      6,924 K
services.exe                   724 Services                   0      8,712 K
lsass.exe                      732 Services                   0     13,724 K
svchost.exe                    844 Services                   0      3,696 K
svchost.exe                    868 Services                   0     23,876 K
fontdrvh
>>: dir
ost.exe                892 Services                   0      3,140 K
WUDFHost.exe                   900 Services                   0      7,440 K
svchost.exe                    992 Services                   0     14,372 K
svchost.exe                    388 Services                   0      7,624 K
WUDFHost.exe                   416 Services                   0     12,160 K
winlogon.exe                  1056 Console                    1     11,484 K
fontdrvhost.exe               1144 Console                    1     26,136 K
dwm.exe                       1216 Console                    1     80,596 K
svchost.exe                   1280 Services                   0     10,392 K
svchost.exe                   1288 Services                   0      6,676 K
svchost.exe                   1372 Services                   0      9,684 K
svchost.exe                   1384 Services                   0     11,404 K
svchost.exe                   1480 Services                   0      8,932 K
svchost.exe       
>>: dir
            1564 Services                   0     12,840 K
svchost.exe                   1716 Services                   0      6,812 K
NVDisplay.Container.exe       1752 Services                   0     10,664 K
svchost.exe                   1844 Services                   0      8,616 K
svchost.exe                   1884 Services                   0      5,404 K
svchost.exe                   1952 Services                   0     11,364 K
svchost.exe                   2000 Services                   0      7,696 K
svchost.exe                   2024 Services                   0     10,348 K
svchost.exe                   2036 Services                   0      7,472 K
svchost.exe                   1156 Services                   0     12,260 K
svchost.exe                   1808 Services                   0      5,468 K
Memory Compression            2176 Services                   0    678,716 K
svchost.exe                   2256 Services                   0      9,008 K
svchost.exe                 
>>: dir
  2264 Services                   0      9,488 K
svchost.exe                   2324 Services                   0     13,624 K
igfxCUIService.exe            2368 Services                   0      6,844 K
svchost.exe                   2448 Services                   0     11,796 K
svchost.exe                   2524 Services                   0      7,168 K
svchost.exe                   2544 Services                   0     11,160 K
svchost.exe                   2636 Services                   0      8,528 K
svchost.exe                   2784 Services                   0     11,636 K
svchost.exe                   2796 Services                   0      8,360 K
svchost.exe                   2912 Services                   0      5,720 K
svchost.exe                   2956 Services                   0      5,896 K
svchost.exe                   2964 Services                   0     11,452 K
360rps.exe                    2976 Services                   0      6,112 K
ZhuDongFangYu.exe             3000 Ser
>>: dir
vices                   0     20,540 K
svchost.exe                   3240 Services                   0     13,816 K
svchost.exe                   3304 Services                   0     11,572 K
spoolsv.exe                   3412 Services                   0     11,976 K
svchost.exe                   3480 Services                   0     13,736 K
svchost.exe                   3516 Services                   0      7,908 K
wlanext.exe                   3544 Services                   0     14,340 K
conhost.exe                   3572 Services                   0      4,588 K
svchost.exe                   3788 Services                   0      7,476 K
svchost.exe                   3796 Services                   0      6,588 K
IntelCpHDCPSvc.exe            3940 Services                   0      5,936 K
svchost.exe                   3948 Services                   0     12,208 K
esif_uf.exe                   3960 Services                   0      5,908 K
EvtEng.exe                    3972 Services     
>>: dir
              0     12,148 K
svchost.exe                   3188 Services                   0     21,512 K
QQProtect.exe                 3368 Services                   0     18,352 K
svchost.exe                   3596 Services                   0      6,156 K
svchost.exe                   3576 Services                   0     16,480 K
ibtsiva.exe                   3920 Services                   0      4,104 K
svchost.exe                   4108 Services                   0     16,284 K
tvnserver.exe                 4116 Services                   0      6,828 K
ICEsoundService64.exe         4124 Services                   0      5,352 K
RegSrvc.exe                   4132 Services                   0      7,640 K
ZeroConfigService.exe         4148 Services                   0     15,504 K
vmnat.exe                     4140 Services                   0      6,388 K
FNPLicensingService.exe       4160 Services                   0      9,100 K
vmnetdhcp.exe                 4168 Services               
>>: dir
    0      4,504 K
IntelCpHeciSvc.exe            4232 Services                   0      8,384 K
svchost.exe                   4244 Services                   0      8,304 K
vmware-authd.exe              4280 Services                   0     10,104 K
vmware-usbarbitrator64.ex     4292 Services                   0      8,944 K
svchost.exe                   4308 Services                   0     11,484 K
svchost.exe                   4412 Services                   0     11,332 K
unsecapp.exe                  5212 Services                   0      6,424 K
WmiPrvSE.exe                  5596 Services                   0     12,280 K
NVDisplay.Container.exe       6068 Console                    1     23,456 K
esif_assist_64.exe            6048 Console                    1      3,436 K
sihost.exe                    5868 Console                    1     24,228 K
svchost.exe                   6156 Console                    1     19,520 K
PresentationFontCache.exe     6204 Services                   0     
>>: dir
19,676 K
svchost.exe                   6212 Console                    1     30,216 K
svchost.exe                   6364 Services                   0     13,028 K
taskhostw.exe                 6428 Console                    1     15,552 K
svchost.exe                   6560 Services                   0     16,736 K
svchost.exe                   6576 Services                   0      6,696 K
explorer.exe                  6628 Console                    1    161,144 K
ctfmon.exe                    6672 Console                    1     25,300 K
igfxEM.exe                    6996 Console                    1     10,920 K
ChsIME.exe                    6256 Console                    1      6,756 K
svchost.exe                   6952 Console                    1     16,488 K
ShellExperienceHost.exe       7180 Console                    1     70,040 K
SearchUI.exe                  7352 Console                    1     93,568 K
RuntimeBroker.exe             7444 Console                    1     26,628 K

>>: dir
RuntimeBroker.exe             7520 Console                    1     26,296 K
WindowsInternal.Composabl     7932 Console                    1     42,872 K
RuntimeBroker.exe             8144 Console                    1     12,312 K
360sd.exe                     5736 Console                    1      4,044 K
360rp.exe                     2820 Console                    1     27,900 K
360tray.exe                   4000 Console                    1    104,624 K
vmware-tray.exe               9184 Console                    1      7,732 K
RAVBg64.exe                   8932 Console                    1        980 K
RAVCpl64.exe                  7372 Console                    1      1,096 K
svchost.exe                   2072 Services                   0     20,852 K
svchost.exe                   8880 Services                   0      7,868 K
SgrmBroker.exe                8380 Services                   0      6,072 K
svchost.exe                   1744 Services                   0      9,376 K
svchost.ex
>>: dir
e                   2140 Console                    1     16,856 K
svchost.exe                   4380 Services                   0      8,108 K
svchost.exe                   1924 Services                   0      8,452 K
SogouCloud.exe                 968 Console                    1     24,448 K
svchost.exe                   1832 Services                   0      5,336 K
360bdoctor.exe                6064 Console                    1     11,320 K
smartscreen.exe               9016 Console                    1     20,440 K
QQ.exe                        1824 Console                    1    177,736 K
TXPlatform.exe                4668 Console                    1      2,764 K
chrome.exe                    2352 Console                    1    180,568 K
chrome.exe                    7492 Console                    1      7,852 K
chrome.exe                    5512 Console                    1      8,300 K
chrome.exe                    8520 Console                    1    181,680 K
chrome.exe          
>>: dir
          2076 Console                    1     31,268 K
chrome.exe                    2516 Console                    1     53,712 K
SystemSettingsBroker.exe      3452 Console                    1     14,440 K
chrome.exe                    4844 Console                    1    118,804 K
SogouImeBroker.exe            1516 Console                    1      6,668 K
dllhost.exe                   2008 Console                    1      9,556 K
WeChat.exe                   10532 Console                    1    123,036 K
WeChatWeb.exe                11144 Console                    1     18,492 K
lantern.exe                   4852 Console                    1     14,556 K
lantern.exe                   9432 Console                    1     84,104 K
sysproxy-cmd.exe              8548 Console                    1      4,600 K
wps.exe                       9660 Console                    1     47,504 K
wps.exe                       9200 Console                    1     83,356 K
wps.exe                      1
>>: dir
0896 Console                    1     20,800 K
WmiPrvSE.exe                  4932 Services                   0     13,888 K
wpscenter.exe                10876 Console                    1     61,264 K
wps.exe                       7616 Console                    1     21,304 K
pycharm64.exe                11200 Console                    1  1,043,416 K
fsnotifier64.exe              9800 Console                    1      1,832 K
conhost.exe                  10772 Console                    1      4,828 K
svchost.exe                   6584 Services                   0     16,908 K
svchost.exe                   7600 Services                   0      8,928 K
winpty-agent.exe             12384 Console                    1      5,044 K
conhost.exe                  13828 Console                    1      7,336 K
cmd.exe                      11196 Console                    1      3,268 K
ThunderPlatform.exe           9336 Console                    1     25,444 K
chrome.exe                   12948 Conso
>>: dir
le                    1     45,764 K
Pic_2345Svc.exe               1008 Services                   0     18,108 K
chrome.exe                    6572 Console                    1    150,296 K
chrome.exe                   13956 Console                    1     51,216 K
chrome.exe                    5812 Console                    1     16,192 K
svchost.exe                   2804 Services                   0      7,064 K
svchost.exe                   7876 Services                   0      5,108 K
winpty-agent.exe             13912 Console                    1      5,204 K
conhost.exe                  14352 Console                    1      7,504 K
cmd.exe                       2136 Console                    1      3,432 K
python.exe                    4536 Console                    1     11,008 K
conhost.exe                  10664 Console                    1     10,924 K
python.exe                   14976 Console                    1      9,996 K
conhost.exe                   2844 Console        
>>: dir
            1     10,916 K
dllhost.exe                  13024 Console                    1      6,816 K
dllhost.exe                  15024 Console                    1      8,236 K
cmd.exe                       4272 Console                    1      3,752 K
tasklist.exe                  4224 Console                    1      7,536 K
 驱动器 E 中的卷没有标签。
 卷的序列号是 CE9C-6DD5

 E:\PythonProject\new-python\python-test\BasicGrammer 的目录

2019/05/19 周日  12:14    <DIR>          .
2019/05/19 周日  12:14    <DIR>          ..
2019/05/19 周日  12:12    <DIR>          .idea
2019/05/19 周日  12:14               387 client.py
2019/05/17 周五  16:53               941 guessage.py
2019/05/19 周日  12:11             1,130 server.py
2019/05/19 周日  10:49                86 test.py
2019/05/17 周五  16:53            88,888 猜年龄的游戏.jpg
               5 个文件         91,432 字节
               3 个目录 71,271,063,552 可用字节
 驱动器 E 中的卷没有标签。
 卷的序列号是 CE9C-6DD5

 E:\PythonPr
>>: dir
oject\new-python\python-test\BasicGrammer 的目录

2019/05/19 周日  12:14    <DIR>          .
2019/05/19 周日  12:14    <DIR>          ..
2019/05/19 周日  12:12    <DIR>          .idea
2019/05/19 周日  12:14               387 client.py
2019/05/17 周五  16:53               941 guessage.py
2019/05/19 周日  12:11             1,130 server.py
2019/05/19 周日  10:49                86 test.py
2019/05/17 周五  16:53            88,888 猜年龄的游戏.jpg
               5 个文件         91,432 字节
               3 个目录 71,271,063,552 可用字节
 驱动器 E 中的卷没有标签。
 卷的序列号是 CE9C-6DD5

 E:\PythonProject\new-python\python-test\BasicGrammer 的目录

2019/05/19 周日  12:14    <DIR>          .
2019/05/19 周日  12:14    <DIR>          ..
2019/05/19 周日  12:12    <DIR>          .idea
2019/05/19 周日  12:14               387 client.py
2019/05/17 周五  16:53               941 guessage.py
2019/05/19 周日  12:11             1,130 server.py
2019/05/19 周日  10:49                86 test.py
2019/05/17
>>: dir
 周五  16:53            88,888 猜年龄的游戏.jpg
               5 个文件         91,432 字节
               3 个目录 71,271,063,552 可用字节
 驱动器 E 中的卷没有标签。
 卷的序列号是 CE9C-6DD5

 E:\PythonProject\new-python\python-test\BasicGrammer 的目录

2019/05/19 周日  12:14    <DIR>          .
2019/05/19 周日  12:14    <DIR>          ..
2019/05/19 周日  12:12    <DIR>          .idea
2019/05/19 周日  12:14               387 client.py
2019/05/17 周五  16:53               941 guessage.py
2019/05/19 周日  12:11             1,130 server.py
2019/05/19 周日  10:49                86 test.py
2019/05/17 周五  16:53            88,888 猜年龄的游戏.jpg
               5 个文件         91,432 字节
               3 个目录 71,271,063,552 可用字节
 驱动器 E 中的卷没有标签。
 卷的序列号是 CE9C-6DD5

 E:\PythonProject\new-python\python-test\BasicGrammer 的目录

2019/05/19 周日  12:14    <DIR>          .
2019/05/19 周日  12:14    <DIR>          ..
2019/05/19 周日  12:12    <DIR>          .idea
2019/05/
>>: 

socket

从上面的实验,我们可以看到执行完tasklist后,在执行dir,拿到的结果还是tasklist的内容,多次之后才拿到dir的结果
但是我们重新操作,执行dir命令,还是能够正确拿到结果的
为什么呢?
这就是粘包现象,tasklist命令的结果是13652字节,大于客户端的recv(1024),所以剩下的数据就放在了服务端缓存中,等客户端下次recv()时,再继续发送缓存的内容
本该第一次就发送完成的数据,却在第二次,第三次才发送过去,两次结果混合在了一起,这就是粘包

socket

5.2TCP粘包与UDP不粘包

socket
socket
socket
socket
socket
socket

总结:
1.TCP是面向连接的,面向流的,提供可靠性服务
   收发两端都有成对的socket方法,发送端为了将多个数据包有效的发送到接收端,使用了优化方法,Nagle算法,会把间隔较小且数据量较小的数据,合并为一个大数据块,然后进行封包。
     这样接收端就很难分辨多个消息之间的边界,就容易产生粘包
2.UDP是无连接的,面向消息的,提供高效率的服务。不使用Nagle合并算法。
 每个UDP消息中都有消息头(消息来源地址,端口),这样,接收端就容易区分消息边界了。就不会产生粘包现象。
3.TCP是基于数据流的,收发消息不能为空,这就需要客户端和服务端添加消息为空处理机制,防止程序卡主。
  UDP是基于数据报的,即使输入为空,也不是空消息,UDP协议会封装消息头。

5.3粘包的解决方法

5.3.1方法一:

"server"
import socket,json
import subprocess

ip_port = ('127.0.0.1', 8080)

tcp_socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
tcp_socket_server.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #一行代码搞定,写在bind之前
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(5)

def pack_msg_header(header,size):
    bytes_header = bytes(json.dumps(header),encoding="utf-8")
    fill_up_size = size -  len(bytes_header)
    print("need to fill up ",fill_up_size)

    header['fill'] = header['fill'].zfill(fill_up_size)
    print("new header",header)
    bytes_new_header = bytes(json.dumps(header),encoding="utf-8")
    return bytes_new_header

while True:
    conn, addr = tcp_socket_server.accept()
    print('客户端', addr)

    while True:
        cmd = conn.recv(1024)
        if len(cmd) == 0: break
        print("recv cmd",cmd)
        obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.PIPE)

        stdout = obj.stdout.read()
        stderr = obj.stderr.read()
        print("res length",len(stdout))

        msg_header = {
            'length':len(stdout + stderr),
            'fill':''
        }
        packed_header = pack_msg_header(msg_header,100)
        print("packed header size",packed_header,len(packed_header))
        conn.send(packed_header)
        conn.send(stdout + stderr)
"client"
import socket
import json

ip_port = ('127.0.0.1', 8080)

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
res = s.connect_ex(ip_port)

while True:
    msg = input('>>: ').strip()
    if len(msg) == 0: continue
    if msg == 'quit': break

    s.send(msg.encode('utf-8'))
    response_msg_header = s.recv(100).decode("utf-8")

    response_msg_header_data = json.loads(response_msg_header)
    msg_size = response_msg_header_data['length']
    receive_data = 0
    msg = b""
    while receive_data<msg_size:
        # print("recv")
        res = s.recv(1024)
        receive_data +=len(res)
        msg+=res
    print("received res size ",len(msg))
    print(msg.decode('gbk'), end='')

socket

5.3.2struct方式

"server"
import socket
import subprocess
import struct
import json

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
phone.bind(('127.0.0.1',9909)) #0-65535:0-1024给操作系统使用
phone.listen(5)

print('starting...')
while True: # 链接循环
    conn,client_addr=phone.accept()
    print(client_addr)

    while True: #通信循环
        try:
            #1、收命令
            cmd=conn.recv(8096)
            if not cmd:break #适用于linux操作系统

            #2、执行命令,拿到结果
            obj = subprocess.Popen(cmd.decode('utf-8'), shell=True,
                                   stdout=subprocess.PIPE,
                                   stderr=subprocess.PIPE)

            stdout=obj.stdout.read()
            stderr=obj.stderr.read()

            #3、把命令的结果返回给客户端
            #第一步:制作固定长度的报头
            header_dic={
                'filename':'a.txt',
                'md5':'xxdxxx',
                'total_size': len(stdout) + len(stderr)
            }

            header_json=json.dumps(header_dic)

            header_bytes=header_json.encode('utf-8')

            #第二步:先发送报头的长度
            conn.send(struct.pack('i',len(header_bytes)))

            #第三步:再发报头
            conn.send(header_bytes)

            #第四步:再发送真实的数据
            conn.send(stdout)
            conn.send(stderr)

        except ConnectionResetError: #适用于windows操作系统
            break
    conn.close()

phone.close()

"client"
import socket
import struct
import json

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

phone.connect(('127.0.0.1',9909))

while True:
    #1、发命令
    cmd=input('>>: ').strip() #ls /etc
    if not cmd:continue
    phone.send(cmd.encode('utf-8'))

    #2、拿命令的结果,并打印

    #第一步:先收报头的长度
    obj=phone.recv(4)
    header_size=struct.unpack('i',obj)[0]

    #第二步:再收报头
    header_bytes=phone.recv(header_size)

    #第三步:从报头中解析出对真实数据的描述信息
    header_json=header_bytes.decode('utf-8')
    header_dic=json.loads(header_json)
    print(header_dic)
    total_size=header_dic['total_size']

    #第四步:接收真实的数据
    recv_size=0
    recv_data=b''
    while recv_size < total_size:
        res=phone.recv(1024) #1024是一个坑
        recv_data+=res
        recv_size+=len(res)

    print(recv_data.decode('gbk'))

phone.close()

socket

6.socket发送文件

"server"
import socket
import struct
import json
import subprocess
import os

class MYTCPServer:
    address_family = socket.AF_INET

    socket_type = socket.SOCK_STREAM

    allow_reuse_address = False

    max_packet_size = 8192

    coding='utf-8'

    request_queue_size = 5

    server_dir='file_upload'

    def __init__(self, server_address, bind_and_activate=True):
        """Constructor.  May be extended, do not override."""
        self.server_address=server_address
        self.socket = socket.socket(self.address_family,
                                    self.socket_type)
        if bind_and_activate:
            try:
                self.server_bind()
                self.server_activate()
            except:
                self.server_close()
                raise

    def server_bind(self):
        """Called by constructor to bind the socket.
        """
        if self.allow_reuse_address:
            self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.bind(self.server_address)
        self.server_address = self.socket.getsockname()

    def server_activate(self):
        """Called by constructor to activate the server.
        """
        self.socket.listen(self.request_queue_size)

    def server_close(self):
        """Called to clean-up the server.
        """
        self.socket.close()

    def get_request(self):
        """Get the request and client address from the socket.
        """
        return self.socket.accept()

    def close_request(self, request):
        """Called to clean up an individual request."""
        request.close()

    def run(self):
        while True:
            self.conn,self.client_addr=self.get_request()
            print('from client ',self.client_addr)
            while True:
                try:
                    head_struct = self.conn.recv(4)
                    if not head_struct:break

                    head_len = struct.unpack('i', head_struct)[0]
                    head_json = self.conn.recv(head_len).decode(self.coding)
                    head_dic = json.loads(head_json)

                    print(head_dic)
                    #head_dic={'cmd':'put','filename':'a.txt','filesize':123123}
                    cmd=head_dic['cmd']
                    if hasattr(self,cmd):
                        func=getattr(self,cmd)
                        func(head_dic)
                except Exception:
                    break

    def put(self,args):
        file_path=os.path.normpath(os.path.join(
            self.server_dir,
            args['filename']
        ))

        filesize=args['filesize']
        recv_size=0
        print('----->',file_path)
        with open(file_path,'wb') as f:
            while recv_size < filesize:
                recv_data=self.conn.recv(self.max_packet_size)
                f.write(recv_data)
                recv_size+=len(recv_data)
                print('recvsize:%s filesize:%s' %(recv_size,filesize))

tcpserver1=MYTCPServer(('127.0.0.1',8080))

tcpserver1.run()
import socket
import struct
import json
import os

class MYTCPClient:
    address_family = socket.AF_INET

    socket_type = socket.SOCK_STREAM

    allow_reuse_address = False

    max_packet_size = 8192

    coding='utf-8'

    request_queue_size = 5

    def __init__(self, server_address, connect=True):
        self.server_address=server_address
        self.socket = socket.socket(self.address_family,
                                    self.socket_type)
        if connect:
            try:
                self.client_connect()
            except:
                self.client_close()
                raise

    def client_connect(self):
        self.socket.connect(self.server_address)

    def client_close(self):
        self.socket.close()

    def run(self):
        while True:
            inp=input(">>: ").strip()
            if not inp:continue
            l=inp.split()
            cmd=l[0]
            if hasattr(self,cmd):
                func=getattr(self,cmd)
                func(l)

    def put(self,args):
        cmd=args[0]
        filename=args[1]
        if not os.path.isfile(filename):
            print('file:%s is not exists' %filename)
            return
        else:
            filesize=os.path.getsize(filename)

        head_dic={'cmd':cmd,'filename':os.path.basename(filename),'filesize':filesize}
        print(head_dic)
        head_json=json.dumps(head_dic)
        head_json_bytes=bytes(head_json,encoding=self.coding)

        head_struct=struct.pack('i',len(head_json_bytes))
        self.socket.send(head_struct)
        self.socket.send(head_json_bytes)
        send_size=0
        with open(filename,'rb') as f:
            for line in f:
                self.socket.send(line)
                send_size+=len(line)
                print(send_size)
            else:
                print('upload successful')

client=MYTCPClient(('127.0.0.1',8080))

client.run()
随机阅读

Copyright © 2017 英语文化交流 All Rights Reserved.