网络编程

一、网络基础

# 同一台机器上的两个程序之间的通讯 就需要依赖文件
# 两台机器之间的两个程序之间的通讯 就需要依赖网络

# 两个程序之间的通信:
    # 基于文件的
    # 基于网络的

# 网卡 :身份证 mac地址 计算机在网络上的身份证
# 交换机 :负责一个网络内的多台机器之间的信息交换
# mac地址 :16进制的数  全球唯一
# 区域性
# ip地址
# 4位的点分十进制数  IPV4
# 192.168.10.xxx
# 0-255.0-255.0-255.0-255
# 00000000.00000000.00000000.00000000
# 11111111.11111111.11111111.11111111

# IPV6
# 6位的点分十进制数
# 0.0.0.0.0.0 - 255.255.255.255.255.255

# 127.0.0.1 :本地回环地址 本机的地址
# 0.0.0.0 : ip地址的、回环地址的所有的用户都能找到你这台机器

# 每台机器有两个地址 :mac ip地址
# 全球的机器 都连在一块儿

# 局域网
# 网关ip    不同局域网之间通信依赖的ip地址
# 子网掩码  判断两个ip地址是否在同一个网段内
# 网段

# 192.168.16.11
# 255.255.255.0
# 192.168.16.0

# 192.168.16.115
# 11000000.10101000.00001011.10011011
# 11111111.11111111.11111111.00000000
# 11000000.10101000.00001011.00000000 = 192.168.16.0


# 0.0.0.0 - 255.255.255.255
# 局域网的概念
# 外网ip 我们谁都能访问
# 内网ip 从外部不能访问,只能在内部环境中互相访问
# 外网ip永远不会和内网ip冲突?
# 0.0.0.0 - 255.255.255.255中间为内网保留了一些字段
# 192.168.0.0 - 192.168.255.255
# 10.0.0.0 - 10.255.255.255
# 172.16.0.0 - 172.31.255.255

# 127.0.0.1 回环地址 指的是在我们的测试过程中使用的一个地址
# 0.0.0.0   开发环境中

# 端口的概念 —— 帮助你找到一个应用
# 每一个网络服务都会占用计算机上的一个端口
# 计算机上的端口范围 0-65535
# 在同一时刻 同一台计算机上 不同的网络应用 占用的端口一定是不同的


# osi七层模型
# 应用层 http、https
# 传输层 TCP、UDP协议
# 网络层 ip协议
# 数据链路层 arp协议
# 物理层

# tcp 面向连接的 可靠的 但是慢
    # tcp协议
    # 两个应用之间要想通信 必须先建立连接
    # 然后基于连接来通信
    # 比较重要的文件 邮件的发送 下载安装包
# udp 无连接的 快 能够发送的信息长度是有限的
    # 快 但 不可靠 不能发送过长的数据
    # 即时通讯类的程序


# 局域网
    # mac地址
    # ip地址
    # 交换机
    # arp协议 通过ip地址找到mac地址 广播 单播
# 局域网之间的通信
    # 路由器
    # 网段、子网掩码
    # 网关ip
# 端口
    # 应用到应用
# tcp、udp是通过网络通信的两种方式
    # tcp 先建立连接再通信
        # 可靠
        #
    # udp 不需要建立连接直接通信
        #
        # 不可靠
# osi七层协议
# 应用层
# 传输层 tcp\udp协议
# 网络层 ipv4\ipv6
# 数据链路层 arp协议
# 物理层
View Code

二、socket编程

#基于tcp的socket
#server
import socket

sk = socket.socket()
sk.bind(('127.0.0.1',9000))
sk.listen()

while 1:
    conn,addr = sk.accept()
    print(conn,addr)
    while 1:
        msg = input('<<<').strip()
        conn.send(msg.encode('utf-8'))
        if msg == 'q':
            break

        re_msg = conn.recv(1024)
        if re_msg == b'q':
            break
        print(re_msg.decode('utf-8'))
    conn.close()
sk.close()

#client
import socket

sk = socket.socket()
sk.connect(('127.0.0.1',9000))
while 1:
    msg = sk.recv(1024)
    if msg == b'q':
        break
    print(msg.decode('utf-8'))

    send_msg = input('<<<')
    sk.send(send_msg.encode('utf-8'))
    if send_msg == 'q':
        break

sk.close()

#基于udp的socket
#server
import socket

sk = socket.socket(type=socket.SOCK_DGRAM)
sk.bind(('127.0.0.1',9000))
while 1:
    msg,addr = sk.recvfrom(1024)
    name,content = msg.decode('utf-8').split('|')
    print('%s:%s' %(name,content))

    send_msg = input('<<<').strip()
    if send_msg == 'q':
        break
    send_msg = '%s|%s' %('server',send_msg)
    sk.sendto(send_msg.encode('utf-8'),addr)
sk.close()

#client
import socket
sk = socket.socket(type=socket.SOCK_DGRAM)

while 1:
    send_msg = input('<<<').strip()
    if send_msg == 'q':
        break
    send_msg = '%s|%s' %('zfy',send_msg)
    sk.sendto(send_msg.encode('utf-8'),('127.0.0.1',9000))

    msg,addr = sk.recvfrom(1024)
    name,content = msg.decode('utf-8').split('|')
    print('%s:%s' %(name,content))

sk.close()
View Code

三、粘包现象

粘包现象
1、tcp是流式传输,字节流,数据与数据之间是没有边界的。
流式传输不限定长度,可靠传输
慢,和一个人的通信连接conn会一直占用我们的通信资源

2、udp协议是面向数据包的传输,不能传输过长的数据,不可靠。
快,由于不需要建立连接,所以谁发消息我都能收到

粘包:由于流式传输的特点,产生了数据连续发送的粘包现象
在一个conn建立起来的连接上传输的多条数据是没有
边界的,数据的发送和接收实际上不是在执行send和recv
的时候就立刻被发送或者接收,而是需要经过操作系统内核
Nagle算法,能够将发送间隔时间很近的短数据合成一个包
发送到接收端
拆包机制:当要发送的数据超过了网络上能传输的最大长度
就会被tcp协议强制拆包

如果是短数据:只需要告诉对方边界就可以了
如果是长数据:不仅要告诉对方边界,还要保证对方完整的接收了。
#解决粘包问题
#server
import socket
import struct

sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
ip = '127.0.0.1'
port = 9000
sk.bind((ip,port))
sk.listen()

while 1:
    print('wait connecting....')
    conn,addr = sk.accept()
    while 1:
        send_msg = input('<<<').strip().encode('utf-8')
        pack_num = struct.pack('i',len(send_msg))
        conn.send(pack_num)
        conn.send(send_msg)
        if send_msg.upper() == b'Q':break

        pack_num = conn.recv(4)
        num = struct.unpack('i',pack_num)[0]
        msg = conn.recv(num)
        if msg == b'q':break
        print(msg.decode('utf-8'))

    conn.close()
sk.close()

#client
import socket
import struct

sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sk.connect(('127.0.0.1',9000))

while 1:
    pack_num = sk.recv(4)
    num = struct.unpack('i',pack_num)[0]
    msg = sk.recv(num)
    if msg == b'q':break
    print(msg.decode('utf-8'))

    send_msg= input('<<<').strip().encode('utf-8')
    pack_num = struct.pack('i',len(send_msg))
    sk.send(pack_num)
    sk.send(send_msg)
    if send_msg == b'q':break

sk.close()
View Code

四、验证客户端连接的合法性

#server
import socket
import hmac
import os

def auth(conn):
    msg = os.urandom(32)
    conn.send(msg)
    # print(msg, type(msg))
    result = hmac.new(ser_key,msg)
    client_digest = conn.recv(1024)
    if result.hexdigest() == client_digest.decode('utf-8'):
        print('合法连接')
        return True
    else:
        print('不合法的连接')
        return False

ser_key = b'123qwe'
sk = socket.socket()
sk.bind(('127.0.0.1',9000))
sk.listen()

conn,addr = sk.accept()
if auth(conn):
    print(conn.recv(1024))
    conn.close()
else:
    conn.close()
sk.close()

#client
import socket
import hmac

def auth(sk):
    msg = sk.recv(32)
    # print(msg,type(msg))
    result = hmac.new(ser_key,msg)
    res = result.hexdigest()
    sk.send(res.encode('utf-8'))

ser_key = b'123qwe'
sk = socket.socket()
sk.connect(('127.0.0.1',9000))
auth(sk)
sk.send(b'upload')
sk.close()
View Code

五、socketserver模块

#server
import socketserver

class MyServer(socketserver.BaseRequestHandler):
    def handle(self):
        conn = self.request
        while 1:
            conn.send(b'hello')
            print(conn.recv(1024))


socketserver.TCPServer.allow_reuse_address = True
server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),MyServer)
server.serve_forever()

#client
import socket

sk = socket.socket()
sk.connect(('127.0.0.1',9000))

while 1:
    ret = sk.recv(1024)
    print(ret)
    sk.send(b'byebye')
sk.close()
View Code

 

 
posted on 2018-09-12 19:33  旧巷子里的猫  阅读(123)  评论(0编辑  收藏  举报