zoukankan      html  css  js  c++  java
  • python(socket)

    C/S架构(客户端/服务器架构),分为硬件C/S架构(打印机)和软件C/S架构。

    网络通信基础知识

    互联网协议按照功能不同分为osi七层或tcp/ip五层或tcp/ip四层

    各层常见物理设备

    ip(找到所在局域网)+mac(找到物理主机)+端口(找到应用程序)绑定可以找到目标设备上的目标应用程序。

    程序的PID:同一台机器上不同进程或线程的标识。

    套接字工作原理

    先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

    模拟客户端与服务端交互

    客户端

    #_*_coding:utf-8_*_
    __author__ = 'Linhaifeng'
    import socket
    ip_port=('127.0.0.1',9000)
    BUFSIZE=1024
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    
    s.connect_ex(ip_port)           #连接到地址
    
    s.send('linhaifeng nb'.encode('utf-8'))         #发消息,编码方式
    
    feedback=s.recv(BUFSIZE)                           #收消息
    print(feedback.decode('utf-8'))
    
    s.close()                                       #退出
    

    服务端

    # -*- coding: utf-8 -*-
    import  socket
    ip_port=('127.0.0.1',9000)  #通信地址
    BUFSIZE=1024                #收发消息的尺寸
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #地址族和通信协议族
    s.bind(ip_port) #绑定地址
    s.listen(5)     #可以接受最多5个
    
    
    conn,addr=s.accept()            #等待接受消息
    # print(conn)
    # print(addr)
    print('接到来自%s的电话' %addr[0])
    
    msg=conn.recv(BUFSIZE)             #听消息,听话
    print(msg,type(msg))
    
    conn.send(msg.upper())          #发消息
    
    conn.close()                    #关闭进程
    
    s.close()                       #退出
    

    运行客户端,输入信息,在服务端就可以看到

     断链接四次握手上面两条线(客户端发起)表示主动断开连接,下面两条(服务端发起)表示被动断开连接。

    因为刚开始建立连接没有数据,服务端应答客户端和向客户端发起请求可以并在一起。但是,在断开连接的时候,有可能有数据在上传或者下载,所以不能合并。

    客户端循环接受信息

    客户端

    #_*_coding:utf-8_*_
    __author__ = 'Linhaifeng'
    import socket
    ip_port=('127.0.0.1',9000)
    BUFSIZE=1024
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    
    s.connect_ex(ip_port)           #连接到地址
    while True:
        msg=input('输入:')
        s.send(msg.encode('utf-8'))         #发消息,编码方式
    
        feedback=s.recv(BUFSIZE)                           #收消息
        print(feedback.decode('utf-8'))
    
    s.close()                                       #退出
    

    服务端

    # -*- coding: utf-8 -*-
    import  socket
    ip_port=('127.0.0.1',9000)  #通信地址
    BUFSIZE=1024                #收发消息的尺寸
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #地址族和通信协议族
    s.bind(ip_port) #绑定地址
    s.listen(5)     #可以接受最多5个
    
    
    conn,addr=s.accept()            #等待接受消息
    # print(conn)
    # print(addr)
    print('接到来自%s的电话' %addr[0])
    while True:
        msg=conn.recv(BUFSIZE)             #听消息,听话
        print('接收到来自客户端的消息',msg)
    
        conn.send(msg.upper())          #发消息
    
    conn.close()                    #关闭进程
    
    s.close()                       #退出
    

    conn是一个套接字对象

    socket收发消息的原理

    服务端循环提供服务

    客户端

    #_*_coding:utf-8_*_
    import socket
    ip_port=('127.0.0.1',9000)
    BUFSIZE=1024
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    
    s.connect_ex(ip_port)           #连接到地址
    while True:
        msg=input('输入:')
        if not msg:continue                 #解决只有回车没有数据卡住的情况
        s.send(msg.encode('utf-8'))         #发消息,编码方式
    
        feedback=s.recv(BUFSIZE)                           #收消息
        print(feedback.decode('utf-8'))
    
    s.close()                                       #退出
    

    服务端

    # -*- coding: utf-8 -*-
    import  socket
    ip_port=('127.0.0.1',9000)  #通信地址
    BUFSIZE=1024                #收发消息的尺寸
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #地址族和通信协议族
    s.bind(ip_port) #绑定地址
    s.listen(5)     #可以接受最多5个
    
    while True:                         #和多个用户通信
        conn,addr=s.accept()            #等待接受消息
        # print(conn)
        # print(addr)
        print('接到来自%s的电话' %addr[0])
        while True:                             #循环接收信息
            try:
                msg=conn.recv(BUFSIZE)             #听消息,听话
                print('接收到来自客户端的消息',msg)
    
                conn.send(msg.upper())          #发消息
            except Exception:
                break
        conn.close()                    #关闭进程
    
    s.close()                       #退出  

    udp套接字

    udp没有三次握手和四次握手,所以没有监听

    客户端

    #_*_coding:utf-8_*_
    import socket
    ip_port=('127.0.0.1',9000)
    BUFSIZE=1024
    udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    
    while True:                         #做到循环发消息
        msg=input('>>: ').strip()
        if not msg:continue             #解决只有回车没有数据卡住的情况
    
        udp_server_client.sendto(msg.encode('utf-8'),ip_port)   #发信息的内容和端口
    
        back_msg,addr=udp_server_client.recvfrom(BUFSIZE)       #收到的消息和地址
        print(back_msg.decode('utf-8'),addr)
    

    服务端

    # -*- coding: utf-8 -*-
    import socket
    ip_port=('127.0.0.1',9000)      #地址和端口
    BUFSIZE=1024                    #字节
    udp_server_client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    
    udp_server_client.bind(ip_port)         #端口绑定
    
    while True:
        msg,addr=udp_server_client.recvfrom(BUFSIZE)    #接受消息和地址
        print(msg,addr)
    
        udp_server_client.sendto(msg.upper(),addr)      #发消息和地址

    时间服务器

    客户端

    #_*_coding:utf-8_*_
    from socket import *
    ip_port=('127.0.0.1',9000)
    bufsize=1024
    
    tcp_client=socket(AF_INET,SOCK_DGRAM)
    
    
    
    while True:
        msg=input('请输入时间格式(例%Y %m %d)>>: ').strip()
        tcp_client.sendto(msg.encode('utf-8'),ip_port)
    
        data=tcp_client.recv(bufsize)
    
        print(data.decode('utf-8'))
    
    tcp_client.close()
    

    服务端

    # -*- coding: utf-8 -*-
    from socket import *
    from time import strftime
    
    ip_port = ('127.0.0.1', 9000)
    bufsize = 1024
    
    tcp_server = socket(AF_INET, SOCK_DGRAM)
    tcp_server.bind(ip_port)
    
    while True:
        msg, addr = tcp_server.recvfrom(bufsize)
        print('===>', msg)
    
        if not msg:
            time_fmt = '%Y-%m-%d %X'
        else:
            time_fmt = msg.decode('utf-8')
        back_msg = strftime(time_fmt)
    
        tcp_server.sendto(back_msg.encode('utf-8'), addr)
    
    tcp_server.close()
    

    基于TCP实现远程执行命令

     服务端

    # -*- coding: utf-8 -*-
    from socket import *
    import subprocess
    
    ip_port = ('127.0.0.1', 9000)
    back_log=5
    buffer_size = 1024
    
    tcp_server = socket(AF_INET, SOCK_STREAM)
    tcp_server.bind(ip_port)
    tcp_server.listen(back_log)
    
    while True:
        conn,addr = tcp_server.accept()
        print('新的客户端连接===>',addr)
        while True:
            try:                      #处理异常中断
                cmd=conn.recv(buffer_size)
    
                print('客户端连接===>', cmd)
                #执行cmd命令
                res=subprocess.Popen(cmd.decode('utf-8'),shell=True,stderr=subprocess.PIPE,
                                          stdout=subprocess.PIPE,
                                          stdin=subprocess.PIPE)
                err=res.stdout.read()
                if err:            #判断结果
                    cmd_res=err
                else:
                    cmd_res=res.stdout.read()
                conn.send(cmd_res)
            except Exception as e:
                print(e)
                break
    
    
    
    
        conn.close()
    
    # tcp_server.close()

    客户端

    #_*_coding:utf-8_*_
    from socket import *
    
    ip_port = ('127.0.0.1', 9000)
    back_log=5
    buffer_size = 1024
    
    tcp_client = socket(AF_INET, SOCK_STREAM)
    tcp_client.connect(ip_port)
    
    while True:
        cmd=input('输入cmd命令》》》').strip()
        if not cmd:continue    #处理输入为空
        if cmd=='quit':break
        tcp_client.send(cmd.encode('utf-8'))
        cmd_res=tcp_client.recv(buffer_size)
        print('命令执行结果',cmd_res.decode('gbk'))
    
    tcp_client.close()

    上面无法做到并发,每次只能建立一对连接。

    windows环境下,res.stdout.read()读出的就是GBK编码的,在接收端需要用GBK解码

    粘包(由于获取的内容超过缓存区,执行下一个命令时,系统依旧读取上一条命令没有读取完的内容)

    粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

    只有TCP有粘包现象,UDP永远不会粘包(udp只收一次,不收就丢)。

    TCP是字节流(没有消息头),UDP是消息流(有消息头)

     解决TCP粘包问题:把报头做成字典,字典里包含将要发送的真实数据的详细信息,然后json序列化,然后用struck将序列化后的数据长度打包成4个字节(4个自己足够用了)

    发送时:

    先发报头长度

    再编码报头内容然后发送

    最后发真实内容

    接收时:

    先手报头长度,用struct取出来

    根据取出的长度收取报头内容,然后解码,反序列化

    从反序列化的结果中取出待取数据的详细信息,然后去取真实的数据内容

    服务端实现

    # -*- coding: utf-8 -*-
    import socket,struct,json
    import subprocess
    phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #就是它,在bind前加
    
    phone.bind(('127.0.0.1',8080))
    
    phone.listen(5)
    
    while True:
        conn,addr=phone.accept()
        while True:
            cmd=conn.recv(1024)
            if not cmd:break
            print('cmd: %s' %cmd)
    
            res=subprocess.Popen(cmd.decode('utf-8'),
                                 shell=True,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)
            err=res.stderr.read()
            print(err)
            if err:
                back_msg=err
            else:
                back_msg=res.stdout.read()
    
            #改变
            conn.send(struct.pack('i',len(back_msg))) #先发back_msg的长度
            conn.sendall(back_msg) #在发真实的内容
    
        conn.close()

    客户端实现

    #_*_coding:utf-8_*_
    import socket,time,struct
    
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    res=s.connect_ex(('127.0.0.1',8080))
    
    while True:
        msg=input('>>: ').strip()
        if len(msg) == 0:continue
        if msg == 'quit':break
    
        s.send(msg.encode('utf-8'))
        #改变
        l=s.recv(4)
        #获取接收内容大小
        x=struct.unpack('i',l)[0]
        print(type(x),x)
        # print(struct.unpack('I',l))
        r_s=0
        data=b''
        while r_s < x:
            r_d=s.recv(1024)
            data+=r_d
            r_s+=len(r_d)
    
        # print(data.decode('utf-8'))
        print(data.decode('gbk')) #windows默认gbk编码
    

      

  • 相关阅读:
    hdu 4114 Disney's FastPass 状压dp
    lightoj 1381
    bzoj 2428: [HAOI2006]均分数据 随机化
    bzoj 3969: [WF2013]Low Power 二分
    套题:wf2013 (1/8)
    hdu 4119 Isabella's Message 模拟题
    hdu 4118 Holiday's Accommodation 树形dp
    UESTC 2015dp专题 N 导弹拦截 dp
    UESTC 2015dp专题 j 男神的约会 bfs
    UESTC 2015dp专题 H 邱老师选妹子 数位dp
  • 原文地址:https://www.cnblogs.com/2018-1025/p/12432174.html
Copyright © 2011-2022 走看看