昨日内容回顾

网络的基础概念
arp协议 :通过ip地址找到mac地址
五层模型 : 应用层 传输层 网络层 数据链路层 物理层
tcp协议 : 可靠的 面向连接 全双工
  三次握手
  四次挥手
udp协议 : 不可靠的 面向数据包的 高效的
socket :
  是模块 是和应用层直接交互,
    向下封装了,应用层之下的相关协议的一组网络通信的接口

全双工,表示双向连接。
为什么四次挥手的2次连接,不可以合并?
第一次断开,不会立即断开,如果还有数据,可以发送。所以不可以合并。

python代码,属于应用层

socket网络通信

新建文件server.py,内容如下:

  1. import socket
  2. sk = socket.socket()
  3. sk.bind(('127.0.0.1',9000))
  4. sk.listen() # 参数n表示同一时间可以有n个链接等待与server段通信
  5. conn,addr = sk.accept()
  6. ret = conn.recv(1024).decode('utf-8')
  7. print(ret)
  8. conn.send(b'hello')
  9. conn.close()
  10. sk.close()

新建文件client.py,内容如下:

  1. import socket
  2. sk = socket.socket()
  3. sk.connect(('127.0.0.1',9000))
  4. sk.send(b'hello')
  5. print(sk.recv(1024).decode('utf-8'))
  6. sk.close()

先执行server.py,再执行client.py,输出:

hello

但这是一次性的,能不能server与client交互呢?

修改server.py,内容如下:

  1. import socket
  2.  
  3. sk = socket.socket()
  4. sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
  5. sk.bind(('127.0.0.1',9000))
  6. sk.listen() # 参数n表示同一时间可以有n个链接等待与server端通信
  7.  
  8. while True:
  9. conn,addr = sk.accept()
  10. while True:
  11. ret = conn.recv(1024).decode('utf-8')
  12. if ret == 'q':break
  13. print(ret)
  14. inp = input('>>>')
  15. conn.send(inp.encode('utf-8'))
  16. if inp == 'q':break
  17. conn.close()
  18.  
  19. sk.close()

修改client.py,内容如下:

  1. import socket
  2.  
  3. sk = socket.socket()
  4. sk.connect(('127.0.0.1',9000))
  5. while True:
  6. inp = input('>>>')
  7. sk.send(inp.encode('utf-8'))
  8. if inp == 'q':break
  9. ret = sk.recv(1024).decode('utf-8')
  10. if ret == 'q':break
  11. print(ret)
  12. sk.close()

先执行server.py,再执行client.py,效果如下:

server可以主动关闭client,如果client再次开启,还可以继续聊。

如果client和server已经建立连接了。这个时候,再来一个客户端,新来的就不能用了。为啥呢?

对于一个tcp连接,客户端和server是一直占用的。其他所有客户端,全都不能占用。

因为连接是阻塞的。

如果一个server没有执行sk.colse(),再开一个进程时,就会出现

解决方案:

  1. sk = socket.socket()
  2. sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) # 表示允许端口复用
  3. sk.bind(('127.0.0.1',9000))

总结:

tcp协议适用于 文件的上传和下载 发送邮件 发送重要的文件
每和一个客户端建立链接 都会在自己的操作系统上占用一个资源
同一时间 只能 和一个客户端通信

数据交互是从上至下,然后再从下至上

下图,表示2台机器

基于UDP协议的socket

udp是无链接的,启动服务之后可以直接接受消息不需要提前建立链接

简单使用

server端

  1. import socket
  2.  
  3. sk = socket.socket(type=socket.SOCK_DGRAM)
  4. sk.bind(('127.0.0.1',9000))
  5. msg,client_addr = sk.recvfrom(1024) # udp协议不同建立链接
  6. print(msg)
  7. sk.sendto(b'world',client_addr)
  8. sk.close()

client端

  1. import socket
  2.  
  3. sk = socket.socket(type=socket.SOCK_DGRAM)
  4. sk.sendto(b'hello',('127.0.0.1',9000))
  5. ret = sk.recvfrom(1024)
  6. print(ret)
  7. sk.close()

先执行server.py,再执行client.py,执行输出:

(b'world', ('127.0.0.1', 9000))

server.py,输出: b'hello'

上面的代码,执行一次就结束了,改成多次执行的。

server.py内容如下:

  1. import socket
  2. sk = socket.socket(type=socket.SOCK_DGRAM)
  3. sk.bind(('127.0.0.1',9090))
  4. while True:
  5. msg,client_addr = sk.recvfrom(1024)
  6. print(msg.decode('utf-8'))
  7. inp = input('>>>')
  8. sk.sendto(inp.encode('utf-8'),client_addr)
  9. sk.close()

client.py内容如下:

  1. import socket
  2. sk = socket.socket(type=socket.SOCK_DGRAM)
  3. while True:
  4. inp = input('>>>').strip()
  5. sk.sendto(inp.encode('utf-8'),('127.0.0.1',9090))
  6. msg,addr = sk.recvfrom(1024)
  7. print(msg.decode('utf-8'))
  8. sk.close()

在udp中,数据必须是server端先接收

先执行server.py,再执行client.py,效果如下:

client和server可以相互通信。如果再新开一个client,也是可以和server通信的。

那么问题来了,如果2个client,同时发给server,server先不回复。

最后再回复时,server会回复给谁呢?

在网络中,是有先后顺序的。即是是0.0001秒的差距,谁最后给server,那么server就会回复给它。

再增加一个功能,显示人名。

修改client.py,内容如下:

  1. import socket
  2.  
  3. sk = socket.socket(type=socket.SOCK_DGRAM)
  4. name = input('请输入名字: ')
  5. while True:
  6. inp = input('请输入发送内容: ')
  7. sk.sendto(('%s : %s'%(name,inp)).encode('utf-8'),('127.0.0.1',9090))
  8. msg,addr = sk.recvfrom(1024)
  9. print(msg.decode('utf-8'))
  10.  
  11. sk.close()

server.py不需要修改。先执行server.py,再执行client.py,效果如下:

再增加一个功能,人名显示不同的颜色。

修改server.py,内容如下:

  1. import socket
  2. lst = {'egon':'\033[1;31m','yuan':'\033[1;34m'}
  3. sk = socket.socket(type=socket.SOCK_DGRAM)
  4. sk.bind(('127.0.0.1',9090))
  5. while True:
  6. msg,client_addr= sk.recvfrom(1024) # udp协议不用建立链接
  7. name,mesg = msg.decode('utf-8').split(':')
  8. color = lst.get(name.strip(),'')
  9. print('%s%s\033[0m'%(color,msg.decode('utf-8')))
  10. inp = input('>>>')
  11. sk.sendto(inp.encode('utf-8'),client_addr)
  12.  
  13. sk.close()

client.py不需要修改,先执行server.py,再执行client.py,效果如下:

切换到eva和yuan,就会有不同的颜色显示。

现在有一个需求,utf-8转码很麻烦,能不能自动转换呢?

是可以的,写一个类,这个类继承了socket,它能满足定制需求。

新建一个文件mysocket.py,内容如下:

  1. from socket import * # 导入socket模块
  2. class Mysocket(socket): # 继承socket
  3. def __init__(self,coding='utf-8'): # 默认编码为utf-8
  4. self.coding = coding
  5. super().__init__(type=SOCK_DGRAM) # 设定为udp协议
  6. def my_recv(self,num): # num表示最大字节,比如1024
  7. msg,addr = self.recvfrom(num)
  8. return msg.decode(self.coding),addr # 返回解码后的接收信息
  9. def my_send(self,msg,addr): # msg和addr分别表示发送信息和连接ip:端口
  10. return self.sendto(msg.encode(self.coding),addr) # 发送编码后的信息

修改server.py,内容如下:

  1. from mysocket import Mysocket
  2. sk = Mysocket() # 可以指定编码,默认为utf-8
  3. lst = {'eva':'\033[1;31m','yuan':'\033[1;34m'}
  4. sk.bind(('127.0.0.1',9090))
  5. while True:
  6. msg,client_addr= sk.my_recv(1024) # udp协议不用建立链接
  7. name,mesg = msg.split(':')
  8. color = lst.get(name.strip(),'')
  9. print('%s%s\033[0m'%(color,msg))
  10. inp = input('>>>')
  11. sk.my_send(inp,client_addr)
  12.  
  13. sk.close()

client.py,内容如下:

  1. from mysocket import Mysocket
  2. sk = Mysocket()
  3. name = input('请输入名字: ')
  4. while True:
  5. inp = input('请输入发送内容: ')
  6. sk.my_send(('%s : %s'%(name,inp)),('127.0.0.1',9090))
  7. msg,addr = sk.my_recv(1024)
  8. print(msg)
  9.  
  10. sk.close()

先执行server.py,再执行client,py。执行效果同上!

时间同步服务                                                                                                                    
基于udp协议完成的
公司有4,5台服务器,00:00 执行任务,从一个文件里面 读取一些数据,作为明天的搜索关键字。
假如有一台服务器,慢了1小时,那么就会影响客户端访问。
现在机房里面,有一台标准时间的服务器。
机房里所有的机器,每隔一段时间,就去请求这台服务器,来获取一个标准时间

比如一个有标准时间的server,client可以从server获取标准时间,需要使用udp协议。
server不能关,它必须先接收客户端的ip,端口,信息,它才能将新消息,发给指定的人。

修改server.py,内容如下:

  1. import time
  2. import socket
  3. sk = socket.socket(type = socket.SOCK_DGRAM)
  4. sk.bind(('127.0.0.1',9090))
  5. while True:
  6. msg,addr = sk.recvfrom(1024)
  7. sk.sendto(time.strftime(msg.decode('utf-8')).encode('utf-8'),addr)
  8. sk.close()

修改client.py,内容如下:

  1. import time
  2. import socket
  3.  
  4. sk = socket.socket(type=socket.SOCK_DGRAM)
  5. while True:
  6. sk.sendto('%Y/%m/%d %H:%M:%S'.encode('utf-8'),('127.0.0.1',9090)) # 执行时间格式
  7. ret,addr = sk.recvfrom(1024)
  8. print(ret.decode('utf-8'))
  9. time.sleep(1) # 暂停1秒执行
  10. sk.close()

先执行server.py,再执行client,py,执行输出:

2018/05/04 16:33:08
2018/05/04 16:33:09
2018/05/04 16:33:10

...

在真正的工作中,不会让client 一直whilte true的
是用linux任务计划执行的

socket参数的详解

  1. socket.socket(family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None)
  1. 创建socket对象的参数说明:
family 地址系列应为AF_INET(默认值),AF_INET6,AF_UNIX,AF_CAN或AF_RDS。
(AF_UNIX 域实际上是使用本地 socket 文件来通信)
type 套接字类型应为SOCK_STREAM(默认值),SOCK_DGRAM,SOCK_RAW或其他SOCK_常量之一。
SOCK_STREAM 是基于TCP的,有保障的(即能保证数据正确传送到对方)面向连接的SOCKET,多用于资料传送。 
SOCK_DGRAM 是基于UDP的,无保障的面向消息的socket,多用于在网络上发广播信息。
proto 协议号通常为零,可以省略,或者在地址族为AF_CAN的情况下,协议应为CAN_RAW或CAN_BCM之一。
fileno 如果指定了fileno,则其他参数将被忽略,导致带有指定文件描述符的套接字返回。
与socket.fromfd()不同,fileno将返回相同的套接字,而不是重复的。
这可能有助于使用socket.close()关闭一个独立的插座。

 最主要用到的参数是type

看下图:

左边是TCP,右边是UDP

对比可以看出,UDP的步骤比较少一点。

熟练掌握以下3个例子
时间同步
tcp协议的聊天
udp协议的聊天

周一默写 重写socket类的那一套,有3个文件。

server.py,内容如下:

  1. from mysocket import Mysocket
  2.  
  3. sk = Mysocket()
  4. sk.bind(('127.0.0.1',9090))
  5. while True:
  6. msg,client_addr= sk.my_recv(1024) # udp协议不用建立链接
  7. print(msg)
  8. if msg == 'q':sk.close()
  9. inp = input('>>>')
  10. sk.my_send(inp,client_addr)
  11. if inp == 'q':sk.close()
  12. sk.close()

mysocket.py,内容如下:

  1. from socket import *
  2. class Mysocket(socket):
  3. def __init__(self,coding='utf-8'):
  4. self.coding = coding
  5. super().__init__(type=SOCK_DGRAM)
  6.  
  7. def my_recv(self,num):
  8. msg,addr = self.recvfrom(num)
  9. return msg.decode(self.coding),addr
  10.  
  11. def my_send(self,msg,addr):
  12. return self.sendto(msg.encode(self.coding),addr)

client.py,内容如下:

  1. from mysocket import Mysocket
  2.  
  3. sk = Mysocket()
  4. while True:
  5. inp = input('>>>')
  6. sk.my_send(inp,('127.0.0.1',9090))
  7. msg,addr = sk.my_recv(1024)
  8. print(msg)
  9.  
  10. sk.close()

周末作业:

  1. 1.继续完成聊天 使用udp协议
  2. 加上重写socket + 名字 + 颜色
  3. 进阶功能:
  4. 1.添加好友,删除好友。
  5. 2.client简单登录,用来表明身份,以登录用户来聊天。
  6.  
  7. 2.cs架构的程序的登陆
  8. ftp做准备
  9. hashilib密文 client端简单加密一次 + server端加密
  10. 使用tcp协议
  11. 进阶功能:
  12. 1.远程创建文件夹

第一个作业,请参考文章

http://www.py3study.com/Article/details/id/237.html

第二个作业,请参考文章

http://www.py3study.com/Article/details/id/238.html

python 全栈开发,Day34(基于UDP协议的socket)的更多相关文章

  1. python 全栈开发,Day35(TCP协议 粘包现象 和解决方案)

    一.TCP协议 粘包现象 和解决方案 黏包现象让我们基于tcp先制作一个远程执行命令的程序(命令ls -l ; lllllll ; pwd)执行远程命令的模块 需要用到模块subprocess sub ...

  2. python全栈开发从入门到放弃之socket网络编程基础

    网络编程基础 一 客户端/服务器架构 1.硬件C/S架构(打印机) 2.软件C/S架构 互联网中处处是C/S架构 如黄色网站是服务端,你的浏览器是客户端(B/S架构也是C/S架构的一种) 腾讯作为服务 ...

  3. python全栈开发从入门到放弃之socket并发编程之IO模型

    一 IO模型介绍 同步(synchronous) IO和异步(asynchronous) IO,阻塞(blocking) IO和非阻塞(non-blocking)IO分别是什么,到底有什么区别?这个问 ...

  4. 巨蟒python全栈开发django8:基于对象和基于双下划线的多表查询

    1.编辑删除&&多对多关系的其他方法 提交,数据,得到结果 查看运行 给编辑和删除,添加样式 我们点击删除,可以成功删除 打印sql语句的,在settings.py里边的配置 LOGG ...

  5. python全栈开发从入门到放弃之socket并发编程多进程

    1.1 multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_count()查看),在python中大部分情况需要使用多进程 ...

  6. python全栈开发从入门到放弃之socket并发编程之协程

    一.为什么会有协程 本节的主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只有一个)情况下实现并发,为此我们需要先回顾下并发的本质:切换+保存状态 cpu正在运行一个任务,会在两种情 ...

  7. python全栈开发从入门到放弃之socket并发编程多线程

    一 threading模块介绍 multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍 二 开启线程的两种方式 from threadi ...

  8. python全栈开发从入门到放弃之socket并发编程多线程GIL

    一 介绍 ''' 定义: In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple nati ...

  9. Python全栈开发【面向对象进阶】

    Python全栈开发[面向对象进阶] 本节内容: isinstance(obj,cls)和issubclass(sub,super) 反射 __setattr__,__delattr__,__geta ...

随机推荐

  1. 关于Springboot打包错误的问题 | Failed to execute goal org.springframework.boot:spring-boot-maven-plugin

    最近在使用spring-boot整合多模块,但是在父pom中打包maven install时总会报错:Failed to execute goal org.springframework.boot:s ...

  2. Dapper总结(二)---事务和存储过程

    一  dapper使用事务 string sql1 = "insert into UserInfo values('user8',27,'s')"; string sql2 = & ...

  3. xml合并工具【原】

    我的xml文章 xml转换: xml/map转换器 xml合并: xml合并 dupliate()方法思路图: 左报文为: <PACKET> <HEAD> <REQUES ...

  4. 安装阿里云github提供的修改版minikube

    由于kubenetes域名背墙(gcr.io),如kubernetes-dashboard服务依赖不能正常使用. $ docker pull gcr.io/google_containers/paus ...

  5. HDU - 3973 AC's String(Hash+线段树)

    http://acm.hdu.edu.cn/showproblem.php?pid=3973 题意 给一个词典和一个主串.有两种操作,查询主串某个区间,问这主串区间中包含多少词典中的词语.修改主串某一 ...

  6. JS——取消事件冒泡,实现div的显示与隐藏 event.cancelBubble = true;

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  7. android app与服务器交互

    package mydemo.mycom.demo2.service; import org.apache.http.HttpResponse; import org.apache.http.Name ...

  8. MySQL备份可能遇到的坑

    MySQL备份工具,支持各种参数选项,使用不同的选项极有可能影响备份处理过程.本文使用我们常规认为合理的备份参数,测试/验证是否存在容易忽视的坑 # 常规备份参数 # mysqldump shell& ...

  9. JavaScript学习 - 基础(二) - 基础类型/类型转换

    基础类型 - 数字类型(Number) 1.最基本的数据类型 2.不区分整型数值和浮点型数值 3.所有数字采用64位浮点格式存储,相当于Java和C语言中double格式 4.能表示的最大值 +- 1 ...

  10. Debian 9 源配置

    Debian 9: deb http://mirrors.163.com/debian/ stretch main non-free contrib deb http://mirrors.163.co ...