MyException - 我的异常网
当前位置:我的异常网» 编程 » day8-socket网络编程进阶

day8-socket网络编程进阶

www.MyException.Cn  网友分享于:2013-08-22  浏览:0次
day8--socket网络编程进阶

    socket:socket就是实现服务器和客户端数据的交换,服务器端接收并发送数据,客户端发送并接收数据,并且需要注意的是,在python3中,socket值接收字节。因为客户端在发送连接给服务器的时候,要转换为字节码;服务器端在返回给客户端的时候,也要转换为字节码。

    如下所示:

服务器端:

import socket,os
server = socket.socket()
server.bind(("localhost",9999))
server.listen()

while True:
    conn,addr = server.accept()
    print("new conn:",addr)
    while True:
        data = conn.recv(1024)
        if not data:
            print("客户端已断开!")
            break
        print("执行指令:",data)
        cmd_res = os.popen(data.decode()).read()
        print("before send",len(cmd_res))
        if len(cmd_res) == 0:
            cmd_res = "cmd has no output....."
        conn.send(cmd_res.encode('utf-8'))
        print("send done")

server.close()

    上面是服务器端,使用os.popen()实现数据的处理,不过只能处理字符串,因此需要decode()成字符串格式,然后发送的时候要转换为字节码,encode()。

客户端:

import socket
client = socket.socket()
client.connect(("localhost",9999))

while True:
    cmd = input(">>:").strip()
    if len(cmd) == 0:
        continue
    client.send(cmd.encode('utf-8'))
    '''服务器端发送为空,客户端是卡主的'''
    cmd_res = client.recv(1024)

    print(cmd_res.decode())

client.close()

    首先启动服务器端,然后启动客户端,如下所示:

    服务器端发送数据:

>>:1
cmd has no output.....
>>:dir
build_server.py  get_ip.py       socket_server.py  客户端创建过程.py
class_method.py  lib           s_server.py         类的方法.py
class的方法.py     property属性.py   static_method.py  人类.py
error_handle.py  s_client.py       动态导入.py         上节内容
get_attr.py     socket_client.py  反射.py

>>:ls
build_server.py
class_method.py
class的方法.py
error_handle.py
get_attr.py
get_ip.py
lib
property属性.py
s_client.py
socket_client.py
socket_server.py
s_server.py
static_method.py
动态导入.py
反射.py
客户端创建过程.py
类的方法.py
人类.py
上节内容

    客户端接收数据:

new conn: ('127.0.0.1', 55324)
执行指令: b'1'
before send 0
send done
/bin/sh: 1: 1: not found
执行指令: b'dir'
before send 249
send done
执行指令: b'ls'
before send 219
send done

    当客户端卡顿的时候,说明服务器端是没有数据发送过来的,因为客户端不能接收空的数据服务器端也不能接收空的数据。这样就会造成卡顿的情况。

    在接收的时候,会有一个缓存,缓存不满的时候,是不会发送数据的,客户端就接收不到数据。

    由于缓存的存在,客户端接收数据有时候接收不完整,如何避免呢?要求服务器告诉客户端数据的大小,客户端根据数据的大小来接收数据。

    收发一样大,告诉客户端接收数据的大小,如下:

服务器端:

import socket,os,time
server = socket.socket()
server.bind(("localhost",9998))
server.listen()

while True:
    conn,addr = server.accept()
    print("new conn:",addr)
    while True:
        data = conn.recv(1024)
        if not data:
            print("客户端已断开!")
            break
        print("执行指令:",data)
        cmd_res = os.popen(data.decode()).read()
        print("before send",len(cmd_res))
        if len(cmd_res) == 0:
            cmd_res = "cmd has no output....."
        conn.send(str(len(cmd_res)).encode('utf-8'))     #先发送数据的大小给客户端 第一个send()发送数据
        time.sleep(1)    #连续发送数据会造成粘包现象,因此要有区分,不然容易粘包,这里让程序休眠一秒,先另外一个接收执行
        conn.send(cmd_res.encode('utf-8'))     #第二个send()发送数据
        print("send done")

server.close()

    在服务器端上,我们计算了发送给客户端的数据大小,先把数据的大小告知客户端,接收多大的数据,然后在发送真正的数据给客户端。在数据发送的时候,要防止粘包,因为send()两次同时发送,造成粘包的情况,因此让程序休眠一秒,time.sleep(1),让客户端先接收数据,过了一秒重新接收数据,这样就不会粘包。

    粘包情况如下;

 

>>:1
命令结果大小: b'22cmd has no output.....'
Traceback (most recent call last):
  File "/home/zhuzhu/第七天/s_client.py", line 15, in <module>
    while received_size < int(cmd_res_size.decode()):
ValueError: invalid literal for int() with base 10: '22cmd has no output.....'

 

    客户端发送数据的时候,服务器返回的时候,由于两个send()同时发送数据造成粘包的情况,会出现错误。两条数据发送的时候连接到一起,造成粘包。两次数据发送链接到一起,为什么造成粘包,是因为程序执行的太快,客户端接收数据很快,速度赶在下一次发送之前。

 

客户端:

import socket
client = socket.socket()
client.connect(("localhost",9998))

while True:
    cmd = input(">>:").strip()
    if len(cmd) == 0:
        continue
    client.send(cmd.encode('utf-8'))
    '''服务器端发送为空,客户端是卡主的'''
    cmd_res_size = client.recv(1024)     #接收命令结果的长度(服务器发送的)
    print("命令结果大小:",cmd_res_size)

    received_size = 0
    while received_size < int(cmd_res_size.decode()):
        data = client.recv(1024)
        received_size += len(data)    #每次收到的有可能小于1024,所以必须用len()判断
        print(data.decode())
        print(received_size)
    else:
        print("cmd res receive done......",received_size)

    # cmd_res = client.recv(1024)
    #
    # print(cmd_res.decode())

client.close()

    在客户端中,我们先接收服务器发来的数据的大小,然后开始接收数据,当数据长度小于接收长度时,继续接收,一直等到没有数据接收为止。

    在客户端中,接收的数据不一定等于规定的长度。并且要统一格式,我们知道,汉字和英文的长度是不一致的,汉字是由3个字节组成,而因为是由两个字节组成的。

    首先启动客户端,然后启动服务器端,如下:

客户端发送数据:
>>:1
命令结果大小: b'22'
cmd has no output.....
22
cmd res receive done...... 22
>>:ls
命令结果大小: b'275'
build_server.py
class_method.py
class的方法.py
error_handle.py
get_attr.py
get_ip.py
lib
property属性.py
s_client.py
socket_client.py
socket_server.py
s_server.py
static_method.py
动态导入.py
反射.py
客户端创建过程.py
类的方法.py
人类.py
上节内容

275
cmd res receive done...... 275
>>:dir
命令结果大小: b'305'
build_server.py  get_ip.py       socket_server.py  客户端创建过程.py
class_method.py  lib           s_server.py         类的方法.py
class的方法.py     property属性.py   static_method.py  人类.py
error_handle.py  s_client.py       动态导入.py         上节内容
get_attr.py     socket_client.py  反射.py

305
cmd res receive done...... 305

    从接收数据可以看出,如果有中文的话,接收数据的长度是不一致的,就是由于中文的字节是3个,因此都要转换为统一格式,现在调整服务器端,让服务器发送数据的长度的时候也是按照字节的形式发送长度,如下:

import socket,os,time
server = socket.socket()
server.bind(("localhost",9998))
server.listen()

while True:
    conn,addr = server.accept()
    print("new conn:",addr)
    while True:
        data = conn.recv(1024)
        if not data:
            print("客户端已断开!")
            break
        print("执行指令:",data)
        cmd_res = os.popen(data.decode()).read()
        print("before send",len(cmd_res))
        if len(cmd_res) == 0:
            cmd_res = "cmd has no output....."
        conn.send(str(len(cmd_res.encode('utf-8'))).encode('utf-8'))     #先发送数据的大小给客户端
        time.sleep(1)    #连续发送数据会造成粘包现象,因此要有区分,不然容易粘包,这里让程序休眠一秒,先另外一个接收执行
        conn.send(cmd_res.encode('utf-8'))
        print("send done")

server.close()

    重新启动服务器,启动客户端发送数据,如下:

客户端输入指令:
>>:1
命令结果大小: b'22'
cmd has no output.....
22
cmd res receive done...... 22
>>:ls
命令结果大小: b'275'
build_server.py
class_method.py
class的方法.py
error_handle.py
get_attr.py
get_ip.py
lib
property属性.py
s_client.py
socket_client.py
socket_server.py
s_server.py
static_method.py
动态导入.py
反射.py
客户端创建过程.py
类的方法.py
人类.py
上节内容

275
cmd res receive done...... 275
>>:dir
命令结果大小: b'305'
build_server.py  get_ip.py       socket_server.py  客户端创建过程.py
class_method.py  lib           s_server.py         类的方法.py
class的方法.py     property属性.py   static_method.py  人类.py
error_handle.py  s_client.py       动态导入.py         上节内容
get_attr.py     socket_client.py  反射.py

305
cmd res receive done...... 305

    从上面可以看出,当调整服务器端发送长度的方式之后,接收数据的长度和服务器告知客户端的长度是一致的。因此,在接收和发送数据的时候要以字节码方式统一计算长度,格式统一很重要,在socket中,计算长度统一格式为:字节码,发送和接收数据都是以字节码的形式操作。

    socket网络编程,其实就是收发数据,只能收发字节的形式,要统一字节格式,要进行及时传唤,要告知客户端数据的大小。然后接收的时候,按照大小来接收。接收完成之后在继续进行执行程序。

    粘包:两次发送数据粘到一起。

    如何解决粘包:

  (1)sleep(),停留一秒,time.sleep(0.5);

服务器端:

 

import socket,os,time
server = socket.socket()
server.bind(("localhost",9999))
server.listen()

while True:
    conn,addr = server.accept()
    print("new conn:",addr)
    while True:
        data = conn.recv(1024)
        if not data:
            print("客户端已断开!")
            break
        print("执行指令:",data)
        cmd_res = os.popen(data.decode()).read()
        print("before send",len(cmd_res))
        if len(cmd_res) == 0:
            cmd_res = "cmd has no output....."
        conn.send(str(len(cmd_res.encode('utf-8'))).encode('utf-8'))     #先发送数据的大小给客户端
        time.sleep(1)    #连续发送数据会造成粘包现象,因此要有区分,不然容易粘包,这里让程序休眠一秒,先另外一个接收执行
        conn.send(cmd_res.encode('utf-8'))
        print("send done")

server.close()

 

    服务器两次发送数据的时间有间隔,这样就能避免数据发送粘包的情况。不过使用sleep()太low了。

    (2)第一次发送之后,接收数据,接收客户端发来的第一次数据发送接收成功的消息,进行第二次发送,这样就能隔断数据的发送

服务器端:

import socket,os,time
server = socket.socket()
server.bind(("localhost",9998))
server.listen()

while True:
    conn,addr = server.accept()
    print("new conn:",addr)
    while True:
        data = conn.recv(1024)
        if not data:
            print("客户端已断开!")
            break
        print("执行指令:",data)
        cmd_res = os.popen(data.decode()).read()
        print("before send",len(cmd_res))
        if len(cmd_res) == 0:
            cmd_res = "cmd has no output....."
        conn.send(str(len(cmd_res.encode('utf-8'))).encode('utf-8'))     #先发送数据的大小给客户端
        # time.sleep(1)    #连续发送数据会造成粘包现象,因此要有区分,不然容易粘包,这里让程序休眠一秒,先另外一个接收执行
        clicent_ack = conn.recv(1024)     #等待接收数据,让下面的发送send()暂时不执行,要收到客户端的数据大小进行响应。wait to confirm
        print("ack from client:",clicent_ack)
        conn.send(cmd_res.encode('utf-8'))
        print("send done")

server.close()

    服务器端,要想防止粘包,则在两次发送数据直接增加一个第一次发送数据成功的确认,即接收成功发送数据的指令,如上面所示,conn.recv()其实也没有什么功能,就是隔断第二次发送的执行时间,让第二次发送数据在第一次执行完成之后再进行执行;

客户端:

import socket
client = socket.socket()
client.connect(("localhost",9998))

while True:
    cmd = input(">>:").strip()
    if len(cmd) == 0:
        continue
    client.send(cmd.encode('utf-8'))
    '''服务器端发送为空,客户端是卡主的'''
    cmd_res_size = client.recv(1024)     #接收命令结果的长度(服务器发送的)
    print("命令结果大小:",cmd_res_size)
    client.send("准备好接收了,loser,可以发了".encode('utf-8'))

    received_size = 0
    while received_size < int(cmd_res_size.decode()):
        data = client.recv(1024)
        received_size += len(data)    #每次收到的有可能小于1024,所以必须用len()判断
        print(data.decode())
        print(received_size)
    else:
        print("cmd res receive done......",received_size)

    # cmd_res = client.recv(1024)
    #
    # print(cmd_res.decode())

client.close()

    客户端上,数据接收完成之后,发送一条指令,即让服务器接着发送第二条指令。第一条指令是发送数据的大小,第二条指令是发送数据;

    上面就解决了粘包的情况。

 

    其实,socket在发送和就是数据的时候都是同步的,要想隔断,只能进行成功验证,先隔断不让后面send()执行,只有第一次发送成功之后,并且通过验证,再来进行第二次执行。

文章评论

不懂技术不要对懂技术的人说这很容易实现
不懂技术不要对懂技术的人说这很容易实现
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
那些争议最大的编程观点
那些争议最大的编程观点
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
代码女神横空出世
代码女神横空出世
总结2014中国互联网十大段子
总结2014中国互联网十大段子
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
程序员都该阅读的书
程序员都该阅读的书
老程序员的下场
老程序员的下场
编程语言是女人
编程语言是女人
如何成为一名黑客
如何成为一名黑客
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
一个程序员的时间管理
一个程序员的时间管理
漫画:程序员的工作
漫画:程序员的工作
每天工作4小时的程序员
每天工作4小时的程序员
中美印日四国程序员比较
中美印日四国程序员比较
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
程序员必看的十大电影
程序员必看的十大电影
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
我的丈夫是个程序员
我的丈夫是个程序员
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
程序员应该关注的一些事儿
程序员应该关注的一些事儿
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
我是如何打败拖延症的
我是如何打败拖延症的
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
鲜为人知的编程真相
鲜为人知的编程真相
旅行,写作,编程
旅行,写作,编程
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
程序员和编码员之间的区别
程序员和编码员之间的区别
 程序员的样子
程序员的样子
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有