网络编程

1 .简述socket 通信原理

 

 

如上图,socket通信建立在应用层与TCP/IP协议组通信(运输层)的中间软件抽象层,它是一组接口,在设计模式中,socket其实就是一个门面模式,它把复杂的TCP/IP协议组隐藏在Socket接口后面,对于用户来说,一组简单的接口就是全部,让socket去组织数据,以符合指定的协议。

所以,经常对用户来讲,socket就是ip+prot 即IP地址(识别互联网中主机的位置)+port是程序开启的端口号

 socket通信如下:

客户端

# _*_ coding: utf-8 _*_
import socket
 
ip_port = ('127.0.0.1',9696)
link = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
link.connect(ip_port)
print("开始发送数据")
cmd = input("client请输入要发送的数据>>>>").strip()
link.send(cmd.encode('utf-8'))
recv_data = link.recv(1024)
print("这是受到的消息:",recv_data)
link.close()

服务端

# _*_ coding: utf-8 _*_
import socket
 
ip_port = ('127.0.0.1',9696)
link = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
link.bind(ip_port)
link.listen(5)
 
conn,addr = link.accept()
#这里,因为我们知道自己写的少,所以1024够用
recv_data = conn.recv(1024)
print("这是受到的消息:",recv_data)
cmd = input("server请输入要发送的数据>>>>").strip()
conn.send(cmd.encode('utf-8'))
conn.close()
link.close()
2,粘包的原因和解决方法?

  

2. 粘包的原因和解决方法?

  TCP是面向流的协议,发送文件内容是按照一段一段字节流发送的,在接收方看来不知道文件的字节流从和开始,从何结束。

  UDP是面向消息的协议,每个UDP段都是一个消息,

直接原因:

    所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的
 
根本原因:
    发送方引起的粘包是由TCP协议本身造成的,TCP为了提高传送效率,发送方往往要收集到足够多的数据
才发送一个TCP段,若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成到一个TCP
段后一次发送过去,这样接收方就受到了粘包数据。
 

如果需要一直收发消息,加一个while True即可。但是这里有个1024的问题,即粘包,

  粘包的根源在于:接收端不知道发送端将要发的字节流的长度,所以解决粘包问题的方法就是围绕如何让发送端在发送数据前,把自己将要发送的字节流大小让接收段知道,然后接收端来一个死循环,接收完所有的数据即可。

  粘包解决的具体做法:为字节流加上自定义固定长度报头,报头中包含字节流长度,然后依次send到对端,对端在接受时,先从缓存中取出定长的报头,然后再取真是数据。

客户端

# _*_ coding: utf-8 _*_
import socket
import struct
import json
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('127.0.0.1',8080)) #连接服务器
while True:
    # 发收消息
    cmd = input('请你输入命令>>:').strip()
    if not cmd:continue
    phone.send(cmd.encode('utf-8')) #发送
    #先收报头的长度
    header_len = struct.unpack('i',phone.recv(4))[0]  #吧bytes类型的反解
    #在收报头
    header_bytes = phone.recv(header_len) #收过来的也是bytes类型
    header_json = header_bytes.decode('utf-8')   #拿到json格式的字典
    header_dic = json.loads(header_json)  #反序列化拿到字典了
    total_size = header_dic['total_size']  #就拿到数据的总长度了
    #最后收数据
    recv_size = 0
    total_data=b''
    while recv_size<total_size: #循环的收
        recv_data = phone.recv(1024) #1024只是一个最大的限制
        recv_size+=len(recv_data) #有可能接收的不是1024个字节,或许比1024多呢,
        # 那么接收的时候就接收不全,所以还要加上接收的那个长度
        total_data+=recv_data #最终的结果
    print('返回的消息:%s'%total_data.decode('gbk'))
phone.close()
  

服务端

# _*_ coding: utf-8 _*_
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',8080)) #绑定手机卡
phone.listen(5) #阻塞的最大数
print('start runing.....')
while True: #链接循环
    coon,addr = phone.accept()# 等待接电话
    print(coon,addr)
    while True: #通信循环
        # 收发消息
        cmd = coon.recv(1024) #接收的最大数
        print('接收的是:%s'%cmd.decode('utf-8'))
        #处理过程
        res = subprocess.Popen(cmd.decode('utf-8'),shell = True,
                                          stdout=subprocess.PIPE, #标准输出
                                          stderr=subprocess.PIPE #标准错误
                                )
        stdout = res.stdout.read()
        stderr = res.stderr.read()
        # 制作报头
        header_dic = {
            'total_size': len(stdout)+len(stderr),  # 总共的大小
            'filename': None,
            'md5': None
        }
        header_json = json.dumps(header_dic) #字符串类型
        header_bytes = header_json.encode('utf-8')  #转成bytes类型(但是长度是可变的)
        #先发报头的长度
        coon.send(struct.pack('i',len(header_bytes))) #发送固定长度的报头
        #再发报头
        coon.send(header_bytes)
        #最后发命令的结果
        coon.send(stdout)
        coon.send(stderr)
    coon.close()
phone.close()
3. TCP/IP协议详情

TCP和UDP协议在传输层


4. 简述3次握手,四次挥手?

三次握手:

    client发送请求建立通道;
    server收到请求并同意,同时也发送请求建通道;
    client收到请求并同意,建立完成
  
四次挥手:
    client发送请求断开通道;
    server收到请求并同意,同时还回复client上一条消息;
    server也发送请求断开通道;
    client受到消息结束
 
 
原文地址:https://www.cnblogs.com/wlike/p/12048125.html