python全栈开发基础【第十八篇】网络编程(socket)

1、网络协议html

客户端/服务器架构python

1.硬件C/S架构(打印机)shell

2.软件C/S架构(互联网中到处是C/S架构):B/S架构也是C/S架构的一种,B/S是浏览器/服务器编程

C/S架构与socket的关系:咱们用socket就是为了完成C/S架构的开发设计模式

osi七层浏览器

引子:服务器

须知一个完整的计算机系统是由硬件、操做系统、应用软件三者组成,具有了这三个条件,一台计算机系统就能够本身跟本身玩了(打个单机游戏,玩个扫雷啥的)网络

若是你要跟别人一块儿玩,那你就须要上网了,什么是互联网?架构

互联网的核心就是由一堆协议组成,协议就是标准,好比全世界人通讯的标准是英语并发

若是把计算机比做人,互联网协议就是计算机界的英语。全部的计算机都学会了互联网协议,那全部的计算机都就能够按照统一的标准去收发信息从而完成通讯了。

人们按照分工不一样把互联网协议从逻辑上划分了层级,

详见网络通讯原理:http://www.cnblogs.com/linhaifeng/articles/5937962.html

为什么学习socket必定要先学习互联网协议?

  首先C/S架构是基于网络通讯的

  而后网络的核心即一堆网络协议,也就是协议标准。若是你想开发一款基于网络通讯的软件,就必须遵循这些标准

 

socke层

 

2、socket是什么?

socket是应用层与TCP/IP协议通讯的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来讲,一组简单的接口就是所有,让Socket去组织数据,以符合指定的协议。

因此,咱们无需深刻理解tcp/udp协议,socket已经为咱们封装好了,咱们只须要遵循socket的规定去编程,写出的程序天然就是遵循tcp/udp标准的。

 

3、基于TCP协议的socket

套接字的分类:

  基于文件类型的套接字家族:AF_UNIX(在Unix系统上,一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程同时运行在同一机器,能够经过访问同一个文件系统间接完成通讯)

  基于网络类型的套接字家族:AF_INET  (python支持不少种地址家族,可是因为咱们只关心网络编程,因此大部分时候咱们只使用AF_INET)

套接字的工做流程:

下面咱们举个打电话的小例子来讲明一下

若是你要给你的一个朋友打电话,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就创建起了链接,就能够讲话了。等交流结束,挂断电话结束这次交谈。 生活中的场景就解释了这工做原理。

(若是你去一家餐馆吃饭,假设哪里的老板就是服务端,而你本身就是客户端,当你去吃饭的时候,你确定的知道那个餐馆,也就是服务端的地址吧,可是对于你本身来讲,餐馆的老板不须要知道你的地址吧)

最简单的套接字函数!!!

#1.服务端端套接字函数
import socket
2 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
3 # 1.服务端套接字函数
4 phone.bind('主机ip地址',端口号)  #绑定到(主机,端口号)套接字
5 phone.listen() #开始TCP监听
6 phone.accept() #被动接受TCP客户的链接,等待链接的到来

#2.客户端套接字函数
2 import socket
3 phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#买手机
4 phone.connect()  #主动链接服务端的ip和端口
5 phone.connect_ex()  #connect()函数的扩展版本,出错的时候返回错码,而不是抛出异常
#3.服务端和客户端的公共用途的嵌套字函数
phone.recv() #接受TCP数据
phone.send() #发送TCP数据
phone.recvfrom() #接受UDP数据
phone.sendto() #发送UDP数据
phone.getpeername() #接收到当前套接字远端的地址
phone.getsockname() #返回指定套接字的参数
phone.setsockopt() #设置指定套接字的参数
phone.close() #关闭套接字
#面向锁的套接字方法
phone.setblocking()  #设置套接字的阻塞与非阻塞模式
phone.settimeout()  #设置阻塞套接字操做的超时时间
phone.gettimeout()  #获得阻塞套接字操做的超时时间

#面向文件的套接字函数
phone.fileno()  # 套接字的文件描述符
phone.makefile() #建立一个与该套接字相关的文件

TCP是基于连接的,必须先启动服务器,而后再启动客户端去连接服务端(三次握手创建链接后,才发送数据)

服务端:

import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#买手机
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1) #能够屡次启动
#执行屡次的时候会报错,那么怎么办呢、?就在绑卡前面加上上面那句setsockopt方法就ok了
phone.bind(('192.168.20.44',8080))#绑定手机卡(ip,端口)
# 端口号在1024之前的是系统用的,1024之后的都是你本身写的程序去定义的端口

print('starting run......')
phone.listen(5) #开机   5表明的是最多挂起5个,也能够好多个
while True: #连接循环
    coon,client_addr=phone.accept()#等待接电话,(coon是创建的连接,客户端的ip和端口号组成的元组)
    print(coon,client_addr)

    #收发消息
    while True:  #通讯循环
        try:  #若是不加try...except ,就会报错,由于它不知道你何时断开连接的,服务器还觉得你在运行
            data = coon.recv(1024) #收了1024个字节的消息
            print('client data 收到消息:%s'%data.decode('utf-8'))
            coon.send(data.upper())  #发消息
        except Exception:  #由于你不知道客户端何时断开连接,
            break
    coon.close() #挂电话
phone.close() #关机


# 处理逻辑错误的两种方式:
    # if 判断
    # try...except 异常处理
# 异常处理
# 当你知道直接错误的条件时就用if判断了
# 当程序错误必定发生,可是你又预知不了它出错的条件是什么的时候,就用try...except

客户端:  

import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#买手机
phone.connect(('192.168.20.44',8080))  #直接链接服务端的ip和端口

# 发收消息
while True:
    msg = input('>>:').strip()  #用户输入
    if not msg:continue  #若是为空就继续输
    phone.send(msg.encode('utf-8'))  #  发送你输入的消息
    # phone.send('hello'.encode('utf-8'))
    data = phone.recv(1024)  #在接收一下
    print('server back res服务端返回结果:>>%s'%data.decode('utf-8'))

phone.close()

注意:

若是你在重启服务端的时候可能遇到这样的问题:

这个是因为你的服务端仍然存在四次挥手的time_wait状态在占用地址(若是不懂,请深刻研究1.tcp三次握手,四次挥手 2.syn洪水攻击 3.服务器高并发状况下会有大量的time_wait状态的优化方法)。那么怎么解决呢?

#加入一条socket配置,重用ip和端口
 
phone=socket(AF_INET,SOCK_STREAM)
phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
phone.bind(('127.0.0.1',8080))

  

4、基于TCP协议模拟ssh远程执行命令  

#服务端
import socket import subprocess phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#买手机 phone.bind(('192.168.20.44',8081))#绑定手机卡 phone.listen(5)#阻塞的最大个数 print('starting....') while True: conn,addr=phone.accept()#等待链接 print(addr,conn) while True: cmd=conn.recv(10240)#接收的最大值 # if not cmd :break print('接收的是:%s'%cmd.decode('utf-8')) #处理过程 res=subprocess.Popen(cmd.decode('utf-8'),shell=True, #Popen是执行命令的方法 stdout=subprocess.PIPE, stderr=subprocess.PIPE ) stdout=res.stdout.read() stuerr=res.stderr.read() conn.send(stdout+stuerr) conn.close() phone.close()
#客户端
import socket

phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('192.168.20.44',8081))#绑定端口
while True:
    cmd=input('>>请输入').strip()
    if  not cmd: continue
    phone.send(cmd.encode('utf-8'))
    data=phone.recv(10240)
    print('返回的是%s'%data.decode('gbk'))
phone.close()

  

6、基于UDP协议的socket

#服务端
from socket import *
udp_server = socket(AF_INET,SOCK_DGRAM)
udp_server.bind(('127.0.0.1',8080)) #绑定
while True:#通信循环
    msg,client_addr= udp_server.recvfrom(1024)
    print('收到的消息是:%s'%msg.decode('utf-8'))
    udp_server.sendto(msg.upper(),client_addr)
udp_server.close()

# 客户端
# udp 无连接,因此发送数据钱不须要先创建链接
from socket import *
udp_client = socket(AF_INET,SOCK_DGRAM)

while True:
    msg = input('>>:').strip()
    udp_client.sendto(msg.encode('utf-8'),('127.0.0.1',8080))
    res,sever_addr = udp_client.recvfrom(1024)
    print('返回的结果是:%s'%res.decode('utf-8'))
udp_client.close()

基于UDP协议的socket的应用(模拟QQ聊天)

# 服务端
from socket import *
udp_server= socket(AF_INET,SOCK_DGRAM)
udp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
udp_server.bind(('127.0.0.1',8080))
print('start running...')

while True:
    qq_msg,addr = udp_server.recvfrom(1024)
    print('来自[%s:%s]的一条消息:\033[44m%s\033[0m'%(addr[0],addr[1],qq_msg.decode('utf-8')))
    back_msg = input('回复消息:>>').strip()
    udp_server.sendto(back_msg.encode('utf-8'),addr)
udp_server.close()

#客户端
from socket import *
udp_client = socket(AF_INET,SOCK_DGRAM)
qq_name_dic = {
    '房得成':('127.0.0.1',8080),
    '陈凤琴':('127.0.0.1',8080),
    '王雅玲':('127.0.0.1',8080),
    '喜洋洋':('127.0.0.1',8080)
}
while True:
    qq_name = input('请输入聊天对象:>>').strip()
    if qq_name not in qq_name_dic: continue
    while True:
        msg = input('请输入消息,回车发送:').strip()
        if msg=='quit':break
        if not msg or not qq_name or qq_name not in qq_name_dic:continue
        udp_client.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])
        back_msg,addr = udp_client.recvfrom(1024)
        print('来自[%s:%s]的一条消息:\033[41m%s\033[0m'%(addr[0],addr[1],back_msg.decode('utf-8')))
udp_client.close()

运行结果截图

 

5、subprocess子进程模块

import subprocess
#Popen方法是用来执行系统命令的,直接把结果打印到终端了
res =subprocess.Popen(r'dir',shell=True,
                       #r'dsfsdfr',shell=True,
                      # stdin= #标准输入(不经常使用)
                       stdout=subprocess.PIPE,#stdout 标准输出
                       stderr=subprocess.PIPE) #stderr 标准错误
# 拿到的是‘gbk’编码的结果,
# 这个命令可能有正确结果,也可能有错误结果
print(res.stdout.read().decode('gbk'))
print('========')
print(res.stdout.read().decode('gbk'))  #说明只能读一次
print(res.stderr.read().decode('gbk'))  #若是是错误的就会提示

 

6、struct模块

#该模块能够把一个类型,如数字,转成固定长度的bytes类型
import struct
# res = struct.pack('i',12345)
# print(res,len(res),type(res))  #长度是4

res2 = struct.pack('i',12345111)
print(res2,len(res2),type(res2))  #长度也是4

unpack_res =struct.unpack('i',res2)
print(unpack_res)  #(12345111,)
# print(unpack_res[0]) #12345111
相关文章
相关标签/搜索