网络编程

应用层: 它只负责产生相应格式的数据 ssh ftp nfs cifs dns http smtp pop3 
传输层: 定义数据传输的两种模式:
TCP(传输控制协议:面向连接,可靠的,效率相对不高) 
UDP(用户数据报协议:非面向连接,不可靠的,但效率高)
网络层: 连接不同的网络如以太网、令牌环网
IP (路由,分片) 、ICMP、 IGMP
ARP ( 地址解析协议,作用是将IP解析成MAC )
数据链路层: 以太网传输
物理层: 主要任务是规定各种传输介质和接口与传输信号相关的一些特性

tcp协议:三次握手四次挥手

socket(套接字):

tcp:

import socket
sk = socket.socket()
sk.bind(('127.0.0.1',8898))  #把地址绑定到套接字
sk.listen()          #监听链接
conn,addr = sk.accept() #接受客户端链接
ret = conn.recv(1024)  #接收客户端信息
print(ret)       #打印客户端信息
conn.send(b'hi')        #向客户端发送信息
conn.close()       #关闭客户端套接字
sk.close()        #关闭服务器套接字(可选)
server端
import socket
sk = socket.socket()           # 创建客户套接字
sk.connect(('127.0.0.1',8898))    # 尝试连接服务器
sk.send(b'hello!')
ret = sk.recv(1024)         # 对话(发送/接收)
print(ret)
sk.close()            # 关闭客户套接字
client端

udp

import socket
udp_sk = socket.socket(type=socket.SOCK_DGRAM)   #创建一个服务器的套接字
udp_sk.bind(('127.0.0.1',9000))        #绑定服务器套接字
msg,addr = udp_sk.recvfrom(1024)
print(msg)
udp_sk.sendto(b'hi',addr)                 # 对话(接收与发送)
udp_sk.close()                         # 关闭服务器套接字
server端
import socket
ip_port=('127.0.0.1',9000)
udp_sk=socket.socket(type=socket.SOCK_DGRAM)
udp_sk.sendto(b'hello',ip_port)
back_msg,addr=udp_sk.recvfrom(1024)
print(back_msg.decode('utf-8'),addr)
client端

黏包问题:

黏包现象只发生在tcp协议中:

1.从表面上看,黏包问题主要是因为发送方和接收方的缓存机制、tcp协议面向流通信的特点。

2.实际上,主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的

解决黏包问题

struct模块

该模块可以把一个类型,如数字,转成固定长度的bytes

import socket
import struct
sk=socket.socket()
sk.bind(('127.0.0.1',9000))
sk.listen()
conn,addr = sk.accept()
while True:
    info = input("<<<")
    if info =="q":
        break
    conn.send(info.encode('utf-8'))

    msg = conn.recv(4)
    ret=struct.unpack('i',msg)[0]
    ret=conn.recv(ret)
    print(ret.decode('gbk'))

conn.close()
sk.close()
server端
import socket
import subprocess
import struct
sk = socket.socket()
sk.connect(('127.0.0.1',9000))
while True:
    cmd=sk.recv(1024)
    if cmd == 'q':
        break
    res = subprocess.Popen(cmd.decode('utf-8'), shell=True, stderr=subprocess.PIPE, stdin=subprocess.PIPE,
                           stdout=subprocess.PIPE)
    stderr = res.stderr.read()
    stdout = res.stdout.read()
    ret=len(stderr)+len(stdout)
    ret=struct.pack('i',ret)
    sk.send(ret)
    sk.send(stderr)
    sk.send(stdout)
sk.close()
client端

上传下载文件:

import socket
import os
import json
import struct
buffer=1024
sk=socket.socket()
sk.bind(('127.0.0.1',8880))#把地址绑定到套接字
sk.listen()#监听链接
conn,addr = sk.accept() #接受客户端链接
head_len = conn.recv(4)
head_len = struct.unpack('i',head_len)[0]
json_head = conn.recv(head_len).decode('utf-8')
head = json.loads(json_head)
file_size = head['file_size']
with open(head['file_name'],'wb')as f:
    while file_size:
        if file_size >= buffer:
            c=conn.recv(buffer)
            f.write(c)

            file_size -= buffer
        else:
            c=conn.recv(file_size)
            f.write(c)
            break

conn.close()
sk.close()
server端
import socket
import os
import json
import struct
sk = socket.socket()           # 创建客户套接字
sk.connect(('127.0.0.1',8880))
buffer = 1024
head = {'file_size': None, 'file_path' : r'C:\Users\tom\Pictures\2016-10', 'file_name': 'IMG_0116.JPG'}
filepath = os.path.join(head['file_path'], head['file_name'])
file_size = os.path.getsize(filepath)
head['file_size'] = file_size
head_json = json.dumps(head)#字典转字符串
head_bytes = head_json.encode('utf-8')#转bytes

head_len = len(head_bytes)
pack_len = struct.pack('i',head_len)
sk.send(pack_len) #先发报头的长度,4个bytes
sk.send(head_bytes) #再发报头的字节格式
with open(filepath,'rb') as f:
    while file_size :
        if file_size > buffer:
            c=f.read(buffer)

            sk.send(c)
            file_size-=buffer
        else:
            c = f.read(file_size)
            sk.send(c)
            break


sk.close()
client端

 

posted @ 2019-04-08 13:43  pypypy  阅读(4572)  评论(0编辑  收藏  举报