2019-04-11-day030-网络编程并发

时间:2022-09-02 10:59:17

内容回顾

黏包

  • tcp协议为了保证数据的可靠传输和传输效率
    • 合包机制 : 连续多条短数据会合并成一条
    • 拆包机制 : 一个过大的数据会在发出之前被拆成几个小包
  • tcp的黏包发生在两端:
    • 发送端 : 合包机制导致
    • 接收端 : 接收不及时
  • 发生黏包的本质:由于tcp协议流式传输的特点导致数据与数据之间边界不清晰
  • 自定义协议
    • struct:能够把一个任意的数字变成固定的四个字节
    • 基础版本 :先发送数据的长度(固定4字节),再发送数据本身
    • 进阶版本 :先发送字典的长度(固定4字节),再发送字典,再发送数据本身

总结

  • 很多概念
    • osi五层协议
      tcp协议和udp协议
    • tcp协议 三次握手 四次挥手
    • 代码
    • 区别
      • tcp 面向连接,流式传输,慢,可靠,全双工
      • udp 面向数据包,快,不可靠,无连接
  • tcp协议 黏包
    • 自定义协议来解决问题 struct
    • 为什么会发生黏包
  • tcp协议能够处理多个client的请求 - socketserver
  • 验证客户端合法性 hmac/hashilib

课上代码

并发的socketserver

Server

import time
import socketserver

class Myserver(socketserver.BaseRequestHandler):
    def handle(self):
        conn = self.request
        for i in range(200):
            conn.send(('hello%s'%i).encode('utf-8'))
            print(conn.recv(1024))
            time.sleep(0.5)

server = socketserver.ThreadingTCPServer(('127.0.0.1',9001),Myserver)
server.serve_forever()

ClientI

import socket

sk = socket.socket()
sk.connect(('127.0.0.1',9001))
while True:
    msg = sk.recv(1024)
    print(msg)
    sk.send(b'byebye')
sk.close()

ClientII

import socket

sk = socket.socket()
sk.connect(('127.0.0.1',9001))
while True:
    msg = sk.recv(1024)
    print(msg)
    sk.send(b'byebye')
sk.close()

验证客户端的合法性

  • 不是一个面向用户的 需要用户自己输入用户名和密码的
  • 而是面向一台server的所有的(500台)客户端
  • 都以一种我们共同的方式来进行一个验证

Server

import os
import hashlib
import socket

secret_key = b'alex sb'
#os.urandom(32) 给每一客户端发送一个随机的字符串,来保证即使数据被拦截你也不能使用这个消息
sk = socket.socket()
sk.bind(('127.0.0.1',9001))
sk.listen()

conn,addr = sk.accept()
rand = os.urandom(32)
conn.send(rand)

sha = hashlib.sha1(secret_key)
sha.update(rand)
res = sha.hexdigest()

ret = conn.recv(1024).decode('utf-8')
if ret == res:
    print('是合法的客户端')
else:
    print('不是合法的客户端')
    conn.close()

Client

import socket
import hashlib

secret_key = b'alexsb'
sk = socket.socket()
sk.connect(('127.0.0.1',9001))

rand = sk.recv(32)

sha = hashlib.sha1(secret_key)
sha.update(rand)
res = sha.hexdigest()

sk.send(res.encode('utf-8'))

sk.close()

hmac

import os
import hmac

hmac = hmac.new(b'alex sb',os.urandom(32))
print(hmac.digest())

验证客户端的合法性_hmac

Server

import os
import hmac
import socket

secret_key = b'alex sb'
#os.urandom(32) 给每一客户端发送一个随机的字符串,来保证即使数据被拦截你也不能使用这个消息
sk = socket.socket()
sk.bind(('127.0.0.1',9001))
sk.listen()

conn,addr = sk.accept()
rand = os.urandom(32)
conn.send(rand)

hmac = hmac.new(secret_key,rand)
res = hmac.digest()

ret = conn.recv(1024)
if ret == res:
    print('是合法的客户端')
else:
    print('不是合法的客户端')
    conn.close()

Client

import hmac
import socket

secret_key = b'alex sb'
sk = socket.socket()
sk.connect(('127.0.0.1',9001))

rand = sk.recv(32)

hmac = hmac.new(secret_key,rand)
res = hmac.digest()

sk.send(res)

sk.close()

作业_大文件的传输

Server

import json
import struct
import socket

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

conn,addr = sk.accept()
len_bytes = conn.recv(4)
num = struct.unpack('i',len_bytes)[0]
str_dic = conn.recv(num).decode('utf-8')
dic = json.loads(str_dic)

with open(dic['filename'],'wb') as f:
    while dic['filesize']:
        content = conn.recv(2048)
        f.write(content)
        dic['filesize'] -= len(content)

Client

import os
import json
import struct
import socket

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

file_path = input('>>>')
filename = os.path.basename(file_path)
filesize = os.path.getsize(file_path)
dic = {'filename':filename,'filesize':filesize}
bytes_dic = json.dumps(dic).encode('utf-8')

len_bytes = struct.pack('i',len(bytes_dic))
sk.send(len_bytes)
sk.send(bytes_dic)

with open(file_path,'rb') as f:
    while filesize > 2048:
        content = f.read(2048)
        sk.send(content)
        filesize -= 2048
    else:
        content = f.read()
        sk.send(content)

sk.close()
# 175,060,348
# 152,117,248

作业_认证+上传

Server

import json
import socket
import struct
import hashlib
def get_md5(usr,pwd):
    md5 = hashlib.md5(usr.encode('utf-8'))
    md5.update(pwd.encode('utf-8'))
    return md5.hexdigest()

def login(conn):
    msg = conn.recv(1024).decode('utf-8')
    dic = json.loads(msg)
    with open('userinfo', encoding='utf-8') as f:
        for line in f:
            username, password = line.strip().split('|')
            if username == dic['user'] and password == get_md5(dic['user'], dic['passwd']):
                res = json.dumps({'flag': True}).encode('utf-8')
                conn.send(res)
                return True
        else:
            res = json.dumps({'flag': False}).encode('utf-8')
            conn.send(res)
            return False

def upload(conn):
    len_bytes = conn.recv(4)
    num = struct.unpack('i', len_bytes)[0]
    str_dic = conn.recv(num).decode('utf-8')
    dic = json.loads(str_dic)

    with open(dic['filename'], 'wb') as f:
        while dic['filesize']:
            content = conn.recv(2048)
            f.write(content)
            dic['filesize'] -= len(content)

sk = socket.socket()
sk.bind(('127.0.0.1',9001))
sk.listen()
while True:
    try:
        conn,addr = sk.accept()
        ret = login(conn)
        if ret:
            upload(conn)
    except Exception as e:
        print(e)
    finally:
        conn.close()
sk.close()

Client

import os
import json
import socket
import struct

def upload(sk):
    # 上传文件
    file_path = input('>>>')
    filename = os.path.basename(file_path)
    filesize = os.path.getsize(file_path)
    dic = {'filename': filename, 'filesize': filesize}
    bytes_dic = json.dumps(dic).encode('utf-8')

    len_bytes = struct.pack('i', len(bytes_dic))
    sk.send(len_bytes)
    sk.send(bytes_dic)

    with open(file_path, 'rb') as f:
        while filesize > 2048:
            content = f.read(2048)
            sk.send(content)
            filesize -= 2048
        else:
            content = f.read()
            sk.send(content)

usr = input('username :')
pwd = input('password :')
dic = {'operate':'login','user':usr,'passwd':pwd}
bytes_dic = json.dumps(dic).encode('utf-8')

sk = socket.socket()
sk.connect(('127.0.0.1',9001))
sk.send(bytes_dic)

res = sk.recv(1024).decode('utf-8')
dic = json.loads(res)

if dic['flag']:
    print('登录成功')
    upload(sk)
else:
    print('登录失败')

sk.close()

ClientII

import os
import json
import socket
import struct

def upload(sk):
    # 上传文件
    file_path = input('>>>')
    filename = os.path.basename(file_path)
    filesize = os.path.getsize(file_path)
    dic = {'filename': filename, 'filesize': filesize}
    bytes_dic = json.dumps(dic).encode('utf-8')

    len_bytes = struct.pack('i', len(bytes_dic))
    sk.send(len_bytes)
    sk.send(bytes_dic)

    with open(file_path, 'rb') as f:
        while filesize > 2048:
            content = f.read(2048)
            sk.send(content)
            filesize -= 2048
        else:
            content = f.read()
            sk.send(content)

usr = input('username :')
pwd = input('password :')
dic = {'operate':'login','user':usr,'passwd':pwd}
bytes_dic = json.dumps(dic).encode('utf-8')
sk = socket.socket()
sk.connect(('127.0.0.1',9001))
sk.send(bytes_dic)

res = sk.recv(1024).decode('utf-8')
dic = json.loads(res)
if dic['flag']:
    print('登录成功')
    upload(sk)
else:
    print('登录失败')

sk.close()

2019-04-11-day030-网络编程并发的更多相关文章

  1. UNIX网络编程——并发服务器(TCP)

    在迭代服务器中,服务器只能处理一个客户端的请求,如何同时服务多个客户端呢?在未讲到select/poll/epoll等高级IO之前,比较老土的办法是使用fork来实现. 网络服务器通常用fork来同时 ...

  2. 网络编程并发 多进程 进程池,互斥锁,信号量,IO模型

    进程:程序正在执行的过程,就是一个正在执行的任务,而负责执行任务的就是cpu 操作系统:操作系统就是一个协调.管理和控制计算机硬件资源和软件资源的控制程序. 操作系统的作用: 1:隐藏丑陋复杂的硬件接 ...

  3. [19/04/14-星期日] 网络编程_java.net包(InetAddress类、InetSocketAddress类、URL类)

    一.概念 Java为了可移植性,不允许直接调用操作系统,而是由java.net包来提供网络功能.Java虚拟机负责提供与操作系统的实际连接. InetAddress    作用:封装计算机的IP地址和 ...

  4. ubuntu14.04下unix网络编程环境的配置

    建议 unpv13e/README看一下,忽略一下内容 ===================================================================== 操作 ...

  5. 2019.04.11 第四次训练 【 2017 United Kingdom and Ireland Programming Contest】

    题目链接:  https://codeforces.com/gym/101606 A: ✅ B: C: ✅ D: ✅ https://blog.csdn.net/Cassie_zkq/article/ ...

  6. ubuntu14.04下unix网络编程 环境的配置

    在ubuntu下 首先:在unpv13e文件加下 ./configure cd lib make cd ../libfree make cd ../liggai make cd .. vim lib/ ...

  7. UNIX网络编程——非阻塞connect:时间获取客户程序

    #include "unp.h" int connect_nonb(int sockfd, const SA *saptr, socklen_t salen, int nsec) ...

  8. UNIX网络编程——使用select函数编写客户端和服务器

    首先看原先<UNIX网络编程--并发服务器(TCP)>的代码,服务器代码serv.c: #include<stdio.h> #include<sys/types.h&gt ...

  9. 11Java网络编程

    十一.网络编程       11.1 网络通信协议 网络通信协议:通信协议是对计算机必须遵守的规则,只有遵守这些规则,计算机之间才能进行通信.这就好比在道路中行驶的汽车一定要遵守交通规则一样,协议中对 ...

  10. C&plus;&plus;11网络编程

    Handy是一个简洁优雅的C++11网络库,适用于linux与Mac平台.十行代码即可完成一个完整的网络服务器. 下面是echo服务器的代码: #include <handy/handy.h&g ...

随机推荐

  1. F&num;之旅9 - 正则表达式

    今天,cozy群有个群友发了条正则,问正则匹配相关的问题.虽然他的问题用html selector去处理可能更好,但是我也再一次发现:我忘了正则怎么写的了! 忘掉正则是有原因的,这篇文章会简单记录下F ...

  2. patch 打补丁,和diff 生成制作补丁

    一.diff 命令: diff命令就是比较两个文件的差异,然后生成差异文件,即补丁文件. 参数:diff --help获得,最常用的 1.-N --new-file 在比较时,如果没有就拿一个空的文件 ...

  3. 轻量级Web API实现,带接口界面的Jayrock JsonRPC接口组件升级版

    升级功能如下: 1.增加模块名称.输入参数.输出参数注释 2.增加Sign验证.输入数据解密.输出数据解密重写方法 3.增加集成Demo规范 4.增加模块分类.接口快速定位.接口调用说明.接口输入输出 ...

  4. Java继承时的初始化顺序

    Java程序在启动和运行时,需要首先完成初始化的工作.在涉及到继承.static成员变量等因素时,初始化的顺序就复杂起来.下面以一个例子说明继承时的Java初始化顺序. 例子: class Insec ...

  5. 折腾Java设计模式之访问者模式

    博客原文地址:折腾Java设计模式之访问者模式 访问者模式 Represent an operation to be performed on the elements of an object st ...

  6. 利用ZYNQ SOC快速打开算法验证通路(6)——LWIP实现千兆TCP&sol;IP网络传输

    一.前言 之前ZYNQ与PC之间的网络连接依赖于外接硬件协议栈芯片,虽然C驱动非常简单,但网络带宽受限.现采用LWIP+PS端MAC控制器+PHY芯片的通用架构.关于LWIP库,已经有很多现成的资料和 ...

  7. 一&period; IntelliJ IDEA详细配置文档之初始环境搭建

    前言 对于用惯了eclipse的同学来说, 突然切换为idea不是一件那么容易的事情, 所以我会发布一系列只讲解idea使用技巧的文章, 请大家多多关注.  本系列文章的配置参考网上某教程的讲解, 本 ...

  8. WinForm DataGridView 绑定泛型List&lpar;List&lt&semi;T&gt&semi;&rpar;&sol;ArrayList不显示的原因和解决

    背景:无意间遇到了一个不大不小的问题,希望对一些遇到的人有所帮助! 一.问题 WinForm DataGridView 绑定泛型List (List<T>)/ArrayList不显示,UI ...

  9. 【题解】洛谷P4707重返现世

    在跨年的晚上玩手机被妈妈骂了赶来写题……呜呜呜……但是A题了还是很开心啦,起码没有把去年的题目留到明年去做ヾ(◍°∇°◍)ノ゙也祝大家2019快乐! 这题显然的 kth min-max 容斥就不说了, ...

  10. Xcode括号自动补全以及二次编译后不显示输入

    今天遇到了一个大坑,在使用栈来进行计算表达式的时候,发现输入括号就报错,以及二次编译后不显示. 测试了好久,经过无数次debug后. 二次编译不显示还是没搞明白,不过输入倒是没什么问题,就是不显示出来 ...