day7_直播_网络编程篇(元昊老师著)

网络编程篇
计算机网络:
多台独立的计算机用网络通信设备连接起来的网络。实现资源共享和数据传递。
比如,我们之前的学过的知识可以将D盘的一个文件传到C盘,但如果你想从你的电脑传一个文件到我的电脑上目前是做不到的; 或者我们现在是不是只能设计单机版的游戏而网络编程则可以解决这些问题。
网络编程:
通过某种计算机语言来实现不同设备间的资源共享和信息传递。
计算机网络的创造可能比计算机本身意义更重大!!!(否则,你只能玩单机版游戏)


OSI模型定义了不同计算机互联的标准,是设计和描述计算机网络通信的基本框架。OSI模型把网络通信的工作分为7层,分别是物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。

网络通信要素:
A:IP地址:(1)用来标识网络上一台独立的主机
  (2) IP地址 = 网络地址 + 主机地址(网络号:用于识别主机所在的网络/网段。主机号:用于识别该网络中的主机)
(3)特殊的IP地址:127.0.0.1(本地回环地址、保留地址,点分十进制)可用于简单的测试网卡是否故障。表示本机。
B:端口号:(1)用于标识进程的逻辑地址。不同的进程都有不同的端口标识。
       (2) 端口:要将数据发送到对方指定的应用程序上,为了标识这些应用程序,所以给这些网络应用程序都用数字进行标识。为了方便称呼这些数字,则将这些数字称为端口。(此端口是一个逻辑端口)
C: 传输协议:通讯的规则。例如:TCP、UDP协议(好比两个人得用同一种语言进行交流)
①、UDP:User Datagram Protocol用户数据报协议
特点:
•  面向无连接:传输数据之前源端和目的端不需要建立连接。
•  每个数据报的大小都限制在64K(8个字节)以内。
•  面向报文的不可靠协议。(即:发送出去的数据不一定会接收得到)
•  传输速率快,效率高。
     现实生活实例:邮局寄件、实时在线聊天、视频会议…等。
      
②、TCP:Transmission Control Protocol传输控制协议
特点:
•  面向连接:传输数据之前需要建立连接。
•  在连接过程中进行大量数据传输。
•  通过“三次握手”的方式完成连接,是安全可靠协议。
•  传输速度慢,效率低。

网络通讯步骤:
     确定对端IP地址→ 确定应用程序端口 → 确定通讯协议
 
    总结:网络通讯的过程其实就是一个(源端)不断封装数据包和(目的端)不断拆数据包的过程。
简单来说就是:发送方利用应用软件将上层应用程序产生的数据前后加上相应的层标识不断的往下层传输(封包过程),最终到达物理层通过看得见摸得着的物理层设备,例如:网线、光纤…等将数据包传输到数据接收方,然后接收方则通过完全相反的操作不断的读取和去除每一层的标识信息(拆包过程),最终将数据传递到最高层的指定的应用程序端口,并进行处理。


SOCKET:

要想理解socket,就要先来理解TCP,UDP协议
 TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,定义了主机如何连入因特网及数据如何再它们之间传输的标准,
从字面意思来看TCP/IP是TCP和IP协议的合称,但实际上TCP/IP协议是指因特网整个TCP/IP协议族。不同于ISO模型的七个分层,TCP/IP协议参考模型把所有的TCP/IP系列协议归类到四个抽象层中
应用层:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等等
传输层:TCP,UDP
网络层:IP,ICMP,OSPF,EIGRP,IGMP
数据链路层:SLIP,CSLIP,PPP,MTU
每一抽象层建立在低一层提供的服务上,并且为高一层提供服务,看起来大概是这样子的

我们可以利用ip地址+协议+端口号唯一标示网络中的一个进程。能够唯一标示网络中的进程后,它们就可以利用socket进行通信了,我们经常把socket翻译为套接字,socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。
socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种"打开—读/写—关闭"模式的实现,服务器和客户端各自维护一个"文件",在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件。socket的英文原义是“插槽”或“插座”,就像我们家里座机一样,如果没有网线的那个插口,电话是无法通信的。Socket是实现TCP,UDP协议的接口,便于使用TCP,UDP。
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。Socket通常也称作“套接字”,用于描述IP地址和端口。应用程序两端通过“套接字”向网络发出请求或者应答网络请求。可以把socket理解为通信的把手(hand)

socket通信流程

1服务器根据地址类型(ipv4,ipv6)、socket类型、协议创建socket
2服务器为socket绑定ip地址和端口号
3服务器socket监听端口号请求,随时准备接收客户端发来的连接,这时候服务器的socket并没有被打开
4客户端创建socket
5客户端打开socket,根据服务器ip地址和端口号试图连接服务器socket
6服务器socket接收到客户端socket请求,被动打开,开始接收客户端请求,直到客户端返回连接信息。这时候socket进入阻塞状态,所谓阻塞即accept()方法一直到客户端返回连接信息后才返回,开始接收下一个客户端谅解请求
7客户端连接成功,向服务器发送连接状态信息
8服务器accept方法返回,连接成功
9客户端向socket写入信息
10服务器读取信息
11客户端关闭
12服务器端关闭
注意:在TCP/IP协议中,TCP协议通过三次握手建立一个可靠的连接
“我能给你讲个关于tcp的笑话吗?”
“行,给我讲个tcp笑话.”
“好吧那我就给你讲个tcp笑话.”、
OK,现在让我们进入python 的网络编程

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import socket
ip_port = ('127.0.0.1',8888)
sk = socket.socket()
sk.bind(ip_port)
sk.listen(5)
while True:
conn,address = sk.accept()
conn.sendall('欢迎致电 10086,请输入1xxx,0转人工服务.')
Flag = True
while Flag:
data = conn.recv(1024)
if data == 'exit':
Flag = False
elif data == '0':
conn.sendall('通过可能会被录音.balabala一大推')
else:
conn.sendall('请重新输入.')
conn.close()
-------------------------------------------
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socket

ip_port = ('127.0.0.1',8005)
sk = socket.socket()
sk.connect(ip_port)
sk.settimeout(5)
while True:
data = sk.recv(1024)
print 'receive:',data
inp = raw_input('please input:')
sk.sendall(inp)
if inp == 'exit':
break
sk.close()
-------------------------------------------
sk.bind(address)
  s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。
sk.listen(backlog)
  开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。
      backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5
      这个值不能无限大,因为要在内核中维护连接队列
sk.setblocking(bool)
  是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。
sk.accept()
  接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。
  接收TCP 客户的连接(阻塞式)等待连接的到来
sk.connect(address)
  连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。
sk.connect_ex(address)
  同上,只不过会有返回值,连接成功时返回 0 ,连接失败时候返回编码,例如:10061
sk.close()
  关闭套接字
sk.recv(bufsize[,flag])
  接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。
sk.recvfrom(bufsize[.flag])
  与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。
sk.send(string[,flag])
  将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。
sk.sendall(string[,flag])
  将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。
      内部通过递归调用send,将所有内容发送出去。
sk.sendto(string[,flag],address)
  将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。
sk.settimeout(timeout)
  设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,因为它们可能用于连接的操作(如 client 连接最多等待5s )
sk.getpeername()
  返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。
sk.getsockname()
  返回套接字自己的地址。通常是一个元组(ipaddr,port)
sk.fileno()
  套接字的文件描述符
--------------------------------------------------------------------------------------------------
#!/usr/bin/env python
# __*__coding:utf-8 __*__

import SocketServer
import os
class MyServer(SocketServer.BaseRequestHandler):
def handle(self):
print '--got connection from',self.client_address
conn=self.request
while True:

data=conn.recv(1024)
print'Recv from cmd:%s'%(data)
cmd_res=os.popen(data).read()
conn.sendall(str(len(cmd_res)))
print len(cmd_res)
conn.sendall(cmd_res)


if __name__=='__main__':
server=SocketServer.ThreadingTCPServer(('127.0.0.1',8009),MyServer)
server.serve_forever()
---------------------------------------------
#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socket

ip_port = ('127.0.0.1',8009)
sk = socket.socket()
sk.connect(ip_port)

while True:
inp = raw_input('please input:')
sk.sendall(inp)
total=int(sk.recv(1024))#3333
tem=0

while True:
data = sk.recv(1024)
tem+=len(data)
if tem==total:
print data
break
print data

if inp == 'exit':
break
sk.close()
----------------------------------------------------------
1exit,为什么报错
2print111 为什么打印两次
3客户端recv为什么循环执行
4self.request.recv self.client_address self是什么
5乱码问题
----------------------------------------------------------
IO多路复用:
先让我们了解下阻塞与非阻塞,同步与异步的概念:

------------------------------------------------------------------------------------------
老张爱喝茶,废话不说,煮开水。
出场人物:老张,水壶两把(普通水壶,简称水壶;会响的水壶,简称响水壶)。
1 老张把水壶放到火上,立等水开。(同步阻塞)
老张觉得自己有点傻
2 老张把水壶放到火上,去客厅看电视,时不时去厨房看看水开没有。(同步非阻塞)
老张还是觉得自己有点傻,于是变高端了,买了把会响笛的那种水壶。水开之后,能大声发出嘀~~~的噪音。
3 老张把响水壶放到火上,立等水开。(异步阻塞)
老张觉得这样傻等意义不大
4 老张把响水壶放到火上,去客厅看电视,水壶响之前不再去看它了,响了再去拿壶。(异步非阻塞)
老张觉得自己聪明了。
所谓同步异步,只是对于水壶而言。
普通水壶,同步;响水壶,异步。
虽然都能干活,但响水壶可以在自己完工之后,提示老张水开了。这是普通水壶所不能及的。
同步只能让调用者去轮询自己(情况2中),造成老张效率的低下。
所谓阻塞非阻塞,仅仅对于老张而言。
立等的老张,阻塞;看电视的老张,非阻塞。
情况1和情况3中老张就是阻塞的,媳妇喊他都不知道。虽然3中响水壶是异步的,可对于立等的老张没有太大的意义。所以一般异步是配合非阻塞使用的,这样才能发挥异步的效用。
------------------------------------------------------------------------------------------
监听终端例子:
疑问?
import select
import threading
import sys
while True:
readable,writeable,error=select.select([sys.stdin,sys.stdout,],[],[],1)
if sys.stdin in readable:
print 'select got stdin',sys.stdin.readline()

--------------------------------------------------------------
import select
import threading
import sys
import socket
import select
ip_port=('127.0.0.1',8888)
sk=socket.socket()
sk.bind(ip_port)
sk.listen(5)
sk.setblocking(False)
while True:
readable,writeable,error=select.select([sk],[],[],2)
if sk in readable:
print 'select got stdin'
为什么循环执行了呢?

------------------------------------------------------------------------------------------
OK:
import select
import threading
import sys
import socket
import select
ip_port=('127.0.0.1',8990)
sk=socket.socket()
sk.bind(ip_port)
sk.listen(5)


ip_port=('127.0.0.1',8992)
sk1=socket.socket()
sk1.bind(ip_port)
sk1.listen(5)


#sk.setblocking(False)
'''
while True:
readable,writeable,error=select.select([sk],[],[],2)
if sk in readable:
print 'select got stdin'
'''
while True:
rlist,w,e=select.select([sk,sk1],[],[],2)
for r in rlist:
conn,addresss=r.accept()
print addresss,conn
------------------------------------------------------------------------------------------



原文地址:https://www.cnblogs.com/zhming26/p/5545371.html