zoukankan      html  css  js  c++  java
  • socket+网路编程

    1.网络编程

    通过某种计算机语言来实现不同设备间的资源共享和信息传递。计算机网络的创造可能比计算机本身意义更重大!!!(否则,你只能玩单机版游戏

    OSI模型

    OSI模型定义了不同计算机互联的标准,是设计和描述计算机网络通信的基本框架。OSI模型把网络通信的工作分为7层,分别是物理层、数据链路层、 网络层、传输层、会话层、表示层和应用层。

    网络通信的三要素:

    A:IP地址   (1) 用来标识网络上一台独立的主机
                       (2) IP地址 = 网络地址 + 主机地址(网络号:用于识别主机所在的网络/网段。主机号:用于识别该网络中的主机)
                       (3) 特殊的IP地址:127.0.0.1(本地回环地址、保留地址,点分十进制)可用于简单的测试网卡是否故障。表示本机。
    B:端口号:  (1) 用于标识进程的逻辑地址。不同的进程都有不同的端口标识。
                       (2) 端口:要将数据发送到对方指定的应用程序上,为了标识这些应用程序,所以给这些网络应用程序都用数字进行标识。为了方便称呼这些数字,则将这些数字称为端口。(此端口是一个逻辑端口)
    C: 传输协议:通讯的规则。例如:TCP、UDP协议(好比两个人得用同一种语言进行交流)

    2.  socket 编程  

    要想理解socket,就要先来理解TCP,UDP协议
    
         TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,定义了主机如何连入因特网及数据如何再它们之间传输的标准,
    
    从字面意思来看TCP/IP是TCP和IP协议的合称,但实际上TCP/IP协议是指因特网整个TCP/IP协议族。不同于ISO模型的七个分层,TCP/IP协议参考模型把所有的TCP/IP系列协议归类到四个抽象层中
    
    应用层:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等
    
    传输层:TCP,UDP
    
    网络层:IP,ICMP,OSPF,EIGRP,IGMP
    
    数据链路层:SLIP,CSLIP,PPP,MTU
    
    每一抽象层建立在低一层提供的服务上,并且为高一层提供服务,看起来大概是这样子的
    
        我们可以利用ip地址+协议+端口号唯一标示网络中的一个进程。能够唯一标示网络中的进程后,它们就可以利用socket进行通信了,我们经常把socket翻译为套接字,socket是在应用层和传输层(TCP/IP协议族通信)之间的一个抽象层,是一组接口,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。
    
        应用程序两端通过“套接字”向网络发出请求或者应答网络请求。可以把socket理解为通信的把手(hand)
    
        socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种"打开—读/写—关闭"模式的实现,服务器和客户端各自维护一个"文件",在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件。socket的英文原义是“插槽”或“插座”,就像我们家里座机一样,如果没有网线的那个插口,电话是无法通信的。Socket是实现TCP,UDP协议的接口,便于使用TCP,UDP。

    通信流程

    流程:

    # 流程描述:
    # 
    # 1 服务器根据地址类型(ipv4,ipv6)、socket类型、协议创建socket
    # 
    # 2 服务器为socket绑定ip地址和端口号
    # 
    # 3 服务器socket监听端口号请求,随时准备接收客户端发来的连接,这时候服务器的socket并没有被打开
    # 
    # 4 客户端创建socket
    # 
    # 5 客户端打开socket,根据服务器ip地址和端口号试图连接服务器socket
    # 
    # 6 服务器socket接收到客户端socket请求,被动打开,开始接收客户端请求,直到客户端返回连接信息。这时候socket进入阻塞状态,所谓阻塞即accept()方法一直等到客户端返回连接信息后才返回,开始接收下一个客户端连接请求
    # 
    # 7 客户端连接成功,向服务器发送连接状态信息
    # 
    # 8 服务器accept方法返回,连接成功
    # 
    # 9 客户端向socket写入信息(或服务端向socket写入信息)
    # 
    # 10 服务器读取信息(客户端读取信息)
    # 
    # 11 客户端关闭
    # 
    # 12 服务器端关闭

    来一个简单的例子来看一下,服务端和客户端之间相互通信

    2.1   serve端

     1 import socket
     2 sk = socket.socket()
     3 print(sk)
     4 
     5 address = ("127.0.0.1",9000)
     6 sk.bind(address)
     7 sk.listen(3)                        #表示最多只能等待三人
     8 print("waiting...............")
     9 conn,addr = sk.accept()
    10 #inp = input(">>>")
    11 #conn.send(byte(inp,"utf8"))
    12 data = conn.recv(1024)
    13 print(data)
    14 conn.close()
    15 sk.close()

    client端

     1 import socket
     2 sk = socket.socket()
     3 print(sk)
     4 
     5 address = ("127.0.0.1",9000)
     6 sk.connect(address)
     7 data = sk.send(bytes("haha","utf8"))   #阻塞
     8 
     9 #print(str(data,"utf8"))
    10 sk.close()
    11 print(sk)

    这样就简单实现了服务端和客户端之间的一次通信,但是有时候我们并不是只进行一次通信,我们也许会有好几次,也可以停止聊天和另外一个用户聊,所以呢这时候就可以用到循环了,可以再看下面一个例子就可以当做是屌丝追女神

    服务端:

     1 import socket
     2 sk = socket.socket()
     3 print(sk)
     4 address = ("127.0.0.1",9000)
     5 sk.bind(address)                        #为socket绑定IP和端口号       
     6 sk.listen(3)                            #监听设置端口等待客户端的请求
     7 print("waiting...............")
     8 while 1:
     9     conn,addr = sk.accept()              #accept阻塞,直到有客户端来连接
    10     #print(coon)
    11     #print(sk.accept())
    12     print(addr)
    13     while 1:
    14         data = conn.recv(1024)
    15         # try:          #如果在客户端直接暂停  进行异常处理
    16         #     data = conn.recv(1024)
    17         # except Exception:
    18         #     break
    19         print("......",str(data,"utf8"))
    20         if not data:break
    21         inp = input(">>>")
    22         conn.send(bytes(inp,"utf8"))
    23 sk.close()

    客户端:

     1 import socket
     2 sk = socket.socket()
     3 print(sk)
     4 
     5 address = ("127.0.0.1",9000)
     6 sk.connect(address)
     7 
     8 while True:
     9     inp = input(">>>")
    10     if inp == "exit":
    11         break
    12     sk.send(bytes(inp,"utf8"))
    13     data = sk.recv(1024)
    14     print(str(data,"utf8"))
    15 sk.close()
    注意:

    1  一收一发

    2   client_data=conn.recv(1024)        

    if  那边send一个空数据  这边recv为空,则recv继续阻塞,等待其他的数据。所以聊天的时候好好聊,别发空数据。 

    3. 远程执行命令

    我们希望不仅能在服务端和客户端之间进行交流,有时候我们也希望能在一端执行命令(dir,ifconfig,cd等等),另外一端显示结果     

    那么我们直接来看例子,

    cmd_serve端

     1 import socket,subprocess#词模块下只有一个参数
     2 sk = socket.socket()
     3 print(sk)
     4 address = ("127.0.0.1",9000)
     5 sk.bind(address)
     6 sk.listen(3)
     7 print("waiting...............")
     8 while 1:
     9     conn,addr = sk.accept()
    10     #print(coon)
    11     #print(sk.accept())
    12     print(addr)
    13     while 1:
    14         try:          #如果在客户端直接暂停处理异常
    15             data = conn.recv(1024)
    16         except Exception:
    17             break
    18         if not data:break
    19         print("......", str(data, "utf8"))
    20         obj = subprocess.Popen(data.decode("utf8"),shell = True,stdout = subprocess.PIPE)
    21         cmd_result = obj.stdout.read()
    22         result_len = bytes(str(len(cmd_result)),"utf8")
    23         print(">>>>>>>>>>",result_len)
    24         conn.sendall(result_len)               #粘包现象
    #解决粘包conn.recv(1024)
    25 conn.sendall(cmd_result) 26 sk.close()
    注意:

     1 sendall并不会把数据直接全部发送到客户端,而是将所有的数据都放到缓冲区(理解成一艘艘货船),缓冲区一次最多发送1024字节的数据到客户端

                  2 比如第一次的结果有1124字节,那么放在两艘船上,一艘满的,一艘只有100字节;那么这一次只会讲满的1024字节结果发送到客户端,程序并不会阻塞在这里,会继续向下执行,recv新的命令执行,比如第二次的结果有200字节数据,那么这两百字节的结果也会放到刚才未满的货船上总共300字节一起发送过去。 

    cmd_client端 

     1 import socket
     2 sk = socket.socket()
     3 print(sk)
     4 
     5 address = ("127.0.0.1",9000)
     6 sk.connect(address)
     7 
     8 while True:
     9     inp = input(">>>")
    10     if inp == "exit":
    11         break
    12     sk.send(bytes(inp,"utf8"))
    13     result_len = int(str(sk.recv(1024),"utf8"))
    14     sk.sendall('111') #解决粘包问题
    15     print(result_len)
    16     data = bytes()
    17     while len(data) != result_len:
    18         recv = sk.recv(1024)
    19         data += recv
    20     print(str(data,"gbk"))
    21 sk.close()

    这样就可以实现在客户端输入命令查看信息,在服务端就可以按到命令的端口号以及IP地址,也同事处理了粘包的问题

    4.   编码转换指示

    4.1  在PY3中:只有str和bytes2种编码

      str:unicode(万国码)

      bytes:(十六进制)

    1 s='hello袁浩'
    2 print(type(s))              #<class 'str'>

    4.2  编码规则:

      

     1         # 规则
     2 # str>>>>>>>>>>>>>>bytes:编码
     3 
     4 b=bytes(s,'utf8')
     5 print(b)#b'helloxe8xa2x81xe6xb5xa9'   #utf8规则下的bytes类型
     6 
     7 b2=s.encode('utf8')
     8 print(b2)#b'helloxe8xa2x81xe6xb5xa9'  #utf8规则下的bytes类型
     9 
    10 
    11 b3=s.encode('gbk')
    12 print('gbk编码下的bytes数据:',b2)# b'helloxd4xacxbaxc6'
    13 
    14 
    15 
    16 # bytes>>>>>str:解码
    17 
    18 # s=str(b2,'gbk')
    19 # print(s)#hello琚佹旦 乱码了
    20 
    21 # # 解码方法1:
    22 # s=str(b2,'utf8')
    23 # print(s)#hello袁浩  #str数据类型
    24 #
    25 # # 解码方法2:
    26 # s2=b2.decode('utf8')
    27 #
    28 # print(s2)#hello袁浩
    29 
    30 # s3=b3.decode('gbk')
    31 # print(s3)#hello袁浩

    5.文件上传:

     1 import socket,os
     2 ip_port=("127.0.0.1",8898)
     3 sk=socket.socket()
     4 sk.bind(ip_port)
     5 sk.listen(5)
     6 BASE_DIR=os.path.dirname(os.path.abspath(__file__))
     7 
     8 while True:
     9     print("waiting connect")
    10     conn,addr=sk.accept()
    11     flag = True
    12     while flag:
    13 
    14             client_bytes=conn.recv(1024)
    15             client_str=str(client_bytes,"utf8")
    16             func,file_byte_size,filename=client_str.split("|",2)
    17 
    18             path=os.path.join(BASE_DIR,'yuan',filename)
    19             has_received=0
    20             file_byte_size=int(file_byte_size)
    21 
    22             f=open(path,"wb")
    23             while has_received<file_byte_size:
    24                 data=conn.recv(1024)
    25                 f.write(data)
    26                 has_received+=len(data)
    27             print("ending")
    28             f.close()
    29 
    30 #----------------------------------------------client
    31 #----------------------------------------------
    32 import socket
    33 import re,os,sys
    34 ip_port=("127.0.0.1",8898)
    35 sk=socket.socket()
    36 sk.connect(ip_port)
    37 BASE_DIR=os.path.dirname(os.path.abspath(__file__))
    38 print("客户端启动....")
    39 
    40 while True:
    41     inp=input("please input:")
    42 
    43     if inp.startswith("post"):
    44         method,local_path=inp.split("|",1)
    45         local_path=os.path.join(BASE_DIR,local_path)
    46         file_byte_size=os.stat(local_path).st_size
    47         file_name=os.path.basename(local_path)
    48         post_info="post|%s|%s"%(file_byte_size,file_name)
    49         sk.sendall(bytes(post_info,"utf8"))
    50         has_sent=0
    51         file_obj=open(local_path,"rb")
    52         while has_sent<file_byte_size:
    53             data=file_obj.read(1024)
    54             sk.sendall(data)
    55             has_sent+=len(data)
    56         file_obj.close()
    57         print("上传成功")
  • 相关阅读:
    mysql常用命令
    CSS样式
    定位
    background
    文本属性和字体属性
    超链接导航案例
    margin塌陷
    浮动
    GIT 修改提交地址
    VUE ElementUI 表格多选框实现单选
  • 原文地址:https://www.cnblogs.com/mars527/p/5911971.html
Copyright © 2011-2022 走看看