博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
day 08
阅读量:5153 次
发布时间:2019-06-13

本文共 6104 字,大约阅读时间需要 20 分钟。

 

day08

socket 网络编程初级

先看看这个图片

这个图片是socket 的简单演示

socket连接演示

什么是socket
socket 起源于linux,Linux中的进程间的通信就是靠socket 来实现的.    在Linux中,一切皆文件,也就是说我所有的东西都可以通过文件来实现,    网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。    Socket也具有一个类似于打开文件的函数调用:Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。

如果还是想研究看这个连接

socket 如何通信(网络中的)
首要解决的问题是如何唯一标识一个进程,否则通信无从谈起!    在本地可以通过进程PID来唯一标识一个进程,但是在网络中这是行不通的。    其实TCP/IP协议族已经帮我们解决了这个问题,网络层的“ip地址”可以唯一标识网络中的主机,而传输层的“协议+端口”可以唯一标识主机中的应用程序(进程)。    这样利用三元组(ip地址,协议,端口)就可以标识网络的进程了,网络中需要互相通信的进程,    就可以利用这个标志在他们之间进行交互。请看下面这个TCP/IP协议结构图

结构图

从这个图来看,当我们写有关网络程序的时候,我们可以通过socket来做,这样一些TCP/IP协议    之类我们可以暂且不用管他.    只需关心socket就好
socket的家族:
socket.AF_UNIX unix本机进程间通信    socket.AF_INET IPV4     socket.AF_INET6  IPV6
Socket Types
socket.SOCK_STREAM  #for tcp    socket.SOCK_DGRAM   #for udp下面两个不常用:    socket.SOCK_RAW   #原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;    其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。    socket.SOCK_RDM  #是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。    SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
开始一个简单的socket
#!/usr/bin/env python        #coding:utf-8        '''这个是服务端'''        from socket import *   #注意导入包的方式        phone = socket(AF_INET,SOCK_STREAM)        phone.bind(('127.0.0.1',8080))        phone.listen(5)  #制定backlog 的值        print('starting....')  #这样就开始接客了..        conn,addr=phone.accept()        #打印一下,这两个东西是什么        print('连接号码',conn)        print('client addr',addr)        print('ready to read msg' )        client_msg=conn.recv(1024) #收消息  注意此时的1024        print('client msg: %s' %client_msg)        conn.send((str(client_msg)+'我是土豆,我是土豆').encode('utf-8')) #发消息        conn.close()  #关闭这个连接        phone.close() #关闭这个服务        #!/usr/bin/env python        #coding:utf-8        '''这个是客户端'''        from socket import *   #注意导入包的方式        phone = socket(AF_INET,SOCK_STREAM)        phone.connect(('127.0.0.1',8080))        phone.send('hello'.encode('utf-8'))        back_msg=phone.recv(1024).decode('utf-8')        print('返回的消息: '+back_msg)        phone.close()
  • 上面程序很简单,所以暴露出的问题也很多:

    1. 当我重复开启端口的时候,突然报了这个问题.说是端口被占用,但是我的程序明明是终止了啊..

      有可能出现这种情况
      这种情况就是你的tcp链接还在,可以修改内核参数啊(linux)或者添加下面一句话
      phone.setsockopt(SQL_SOCKET,SO_REUSEADDR,1)

    2. 当我们发送一个空消息的时候,server 端就会不停的等待,一直等待. 解决方法,那就判断如果发过来的是空的,那就终端这个链接

      try:#针对windows平台下客户端断开链接
      client_msg=conn.recv(1024) #收消息
      if not client_msg:break #针对linux系统平台下客户端断开链接
      print(‘client msg: %s’ %client_msg)
      conn.send(client_msg.upper()) #发消息
      except Exception:
      break

  • 当我们用上面的程序进行拓展的时候,比如远程执行个命令之类的时候可能会出现 粘包问题

    代码如下:

    from socket import  *    import   subprocess    '''这个是服务端'''    phone = socket(AF_INET,SOCK_STREAM)    phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)    phone.bind(('127.0.0.1',8080))    phone.listen(5)    while  True:    print('开始接客......')    conn,addr=phone.accept()    print('cliet addr', addr)    while True:        try:            cmd = conn.recv(1024)            print(cmd.decode('utf-8'))            if not   cmd:break            print('开始执行命令')            res = subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)            err = res.stderr.read()            if err:                send_res =err            else:                send_res = res.stdout.read()            conn.send(send_res)        except TypeError:   #这个的异常处理不用管它            break    conn.close()    phone.close()    #!/usr/bin/env python       #coding:utf-8    '''这个是客户端'''    from socket import  *    import   subprocess    phone = socket(AF_INET,SOCK_STREAM)    phone.connect(('127.0.0.1',8080))    while  True:        msg = input('cmd: ').strip()        if not msg:continue        phone.send(msg.encode('utf-8'))        back_msg = phone.recv(1024)        print(back_msg.decode('gbk'))  #这个的gbk 编码是因为我在win的平台下,默认是gbk编码    phone.close()    在做测试的时候就输入两遍dir 命令,在输入ipconfig  就会出现粘包现象
  • 粘包问题

    可以看这篇文章 说的很细致

    主要是怎么去解决这个问题.()如果你是udp 的协议,那你就不用管了)如果是tcp的协议:    1.  自定义时间间隔,也就是说隔一段时间在发    2.  自定义收取的大小但是问题很明显,处理的过程都是很死的.而且效率很差其实这个问题的根源在于:    接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕如何让发送端在发送数据前,    把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据
  • 解决粘包问题

    可以为字节流加上自定义固定长度报头,报头中包含字节流长度,然后一次send到对端,    对端在接收时,先从缓存中取出定长的报头,然后再取真实数据    在py中,有一个模块 struct 该模块可以把一个类型,如数字,转成固定长度的bytes .    看代码:    #!/usr/bin/env python       #coding:utf-8    '''解决粘包的问题'''    from socket import  *    import  json    import  subprocess    import  struct    phone = socket(AF_INET,SOCK_STREAM)    phone.bind(('127.0.0.1',8080))    phone.listen(5)    while True:    print('开始接听.....')    conn,addr = phone.accept()    while True:        cmd = conn.recv(1024)        if not cmd:break        res = subprocess.Popen(cmd.decode('utf-8'),shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)        err = res.stderr.read()        if err :            cmd_res = err        else:            cmd_res = res.stdout.read()        #先制作报头,里面有着各种各样的说明        head_dic={'filename':None,'hash':None,'total_size':len(cmd_res)}        #序列化一下        head_json = json.dumps(head_dic)        head_bytes = head_json.encode('utf-8')        #先发送抱头的长度        conn.send(struct.pack('i',len(head_bytes)))        #再发送包头的bytes        conn.send(head_bytes)        #最后发送真实的数据        conn.send(cmd_res)    conn.close()    phone.close()    #!/usr/bin/env python       #coding:utf-8    from   socket  import  *    import  json    import  struct    client = socket(AF_INET,SOCK_STREAM)    client.connect(('127.0.0.1',8080))    while  True:        msg = input('cmd: ').strip()        if not  msg:continue        client.send(msg.encode('utf-8'))        #收包头的长度了        head_struct=client.recv(4)        head_len=struct.unpack('i',head_struct)[0]        #再接收抱头的bytes        head_bytes = client.recv(head_len)        head_json=head_bytes.decode('utf-8')        head_dic= json.loads(head_json)        #最后根据报头取真实的信息和数据        print(head_dic)        total_size=head_dic['total_size']        recv_size = 0        data = b''        while  recv_size <  total_size:            recv_data = client.recv(1024)            data += recv_data            recv_size += len(recv_data)        print(data.decode('gbk'))    client.close()这样就可以完美的解决粘包问题

转载于:https://www.cnblogs.com/liukang/p/7072695.html

你可能感兴趣的文章