网络编程

网络编程

1.CS架构和BS架构

CS:
client<======================>server
客户端                         服务端

BS:
browser<======================>server
浏览器                         服务端 

2.osi协议

见Linux

5.套接字

5.1简介

起源:
	一开始,套接字被设计用在同一台主机上多个应用程序之间的通讯。这也被称进程间通讯,或IPC。
    

分为2个种族:
	基于文件类型的套接字家族:AF_UNIX
    unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信。
    
    基于网络类型的套接字家族:AF_INET
    现在常用的。

5.2基于TCP协议的socket模板

"""
服务端
"""
import socket
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 配置套接字家族和tcp模式,生成服务端,可以不传参数,默认是基于网络的TCP
phone.bind(('127.0.0.1', 8082))  # 绑定自己的IP和端口号
phone.listen(5)  # 配置半链接池
while True:                    # 链接循环,可以不停的建立链接
    con, add = phone.accept()  # 接收链接,返回链接和对方的(IP,端口号)
    while True:                # 通信循环
        try:                   # 在windows系统中,如果客户端非法断开链接,则会抛出异常
            con.send("你好啊,hello".encode("utf-8"))  # 发送消息,只能发bytes类型,bytes只能由字符串转换得到
            data = con.recv(1024)    # 接收消息,设置一次接收的字节数
            # if len(data) ==0:      # 在UNIX系统中,如果接收到空,则意味着客户端非法断开链接,在windows中不用写
            #     break
            # else:
            #     print(data.decode("utf-8"))
        except Exception:
            break
    con.close()  # 关闭链接


    
"""
客户端
"""
import socket
phone = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # 配置套接字家族和tcp模式,生成客户端
phone.connect(("127.0.0.1", 8082))  # 发起链接,发送对方的(IP,端口号)
while True:
    data = phone.recv(1024)
    print(data.decode("utf-8"))
    data = input("输入").strip()
    if data == "quit":              # 设置中断通讯条件
        break
    phone.send(data.encode("utf-8"))
phone.close()




#注:
#配置中套接字家族有2种:AF_UNIX 或 AF_INET
#配置中传输层模式有2种:SOCK_STREAM 或 SOCK_DGRAM (即TCP和UDP协议)
#可以发送空内容,但接收不能为空,不然会认为没有接收到,继续等待;故不应发送空
#如果接收到空,则是UNIX系统中客户端非法断开链接
#发送的内容只能是bytes,不能是其它类型

5.3基于UDP协议的socket模板

"""
服务端
"""
import socket
phone = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 配置套接字家族和tcp模式,生成服务端
phone.bind(("127.0.0.1", 8083))          #不需要建立链接,但仍要绑定自己的iP和端口号
while True:                              # 链接循环,可以不停的建立链接
    data, add = phone.recvfrom(1024)     #接收数据,元组类型(数据,(IP,端口号))
    data = data.decode("utf-8")
    data = eval(data)
    data = str(data)                         # 套接字只能发送字符串
    phone.sendto(data.encode("utf-8"), add)  # 发送数据,要指定对方IP和端口号
    
    
"""
客户端
"""
import socket
phone = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)  # 配置套接字家族和tcp模式,生成客户端
while True:
    data = input("输入").strip()
    if data == "quit":                                   # 设置中断通讯条件
        break
    phone.sendto(data.encode("utf-8"), ("127.0.0.1", 8083))  #发送数据,要指定对方IP和端口号
    data, add = phone.recvfrom(1024)                         #接收数据,元组类型(数据,(IP,端口号))
    print(data.decode("utf-8"))
phone.close()

5.4TCP和UDP协议的区别

#tcp协议是流式协议,发送数据为空时,底层发的就是空,所以接收会出错
#udp协议发送的是数据包,发送数据为空的时候,底层发送的数据包不为空,所以可以成功收到,不会出错

5.5粘包

5.5.1沾包的简介

定义:
    粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节而造成的数据混乱。
    
发生粘包的2种情况:
	1.发送端需要等缓冲区满才发送出去,造成粘包(发送数据时间间隔很短,数据了很小,会合到一起,产生粘包)
	2.接收方不及时接收缓冲区的包,造成多个包接收(客户端发送了一段数据,服务端只收了一小部分,服务端下次再收的时候还是从缓冲区拿上次遗留的数据,产生粘包) 

    
注:
	#TCP一次取出缓存中指定大小的数据,不管是不是一次发的,故会沾包;udp收不满也只收一次的数据,收不完就扔,故不会沾包。
	#udp中sendto和recvfrom是一一对应的,tcp中不需要。

5.5.2解决办法

	核心思想:加头,如果是字符串,头就是长度;如果是文件,头就是字典,先将字典json成字符串。
    
模板:
服务端
import struct
data_len= len(data)
data_len = struct.pack('i',data_len)
con.send(data_len)

客户端
res = struct.unpack('i', phone.recv(4)) # 接收头部,即数据长度,并将其由bytes型转化成int型,放入元组
data_size=res[0]                #unpack返回的是一个元组(len,)

5.6.基于socketserver实现并发编程

5.6.1TCP服务端

"""
服务端
"""
import socketserver

class My_bingfa(socketserver.BaseRequestHandler): #自定义一个类,必须继承socketserver.BaseRequestHandler
    def handle(self):                             #必须有一个handler,用来写通讯循环的
        print(self.server)                        #self.request就是socket对象,相当于phone
        print(self.request)                       #self.request就是通讯链接,相当于con
        print(self.client_address)                #self.client_address就是(地址,端口号)
        while True:                               #通讯循环
            data = input("输入:").strip()
            res = self.request.send(data.encode("utf-8"))

phone=socketserver.ThreadingTCPServer(('127.0.0.1',8080),My_bingfa)  #指定TCP协议,绑定IP和端口号,绑定上面自定义的类
phone.serve_forever()                                                #启动服务

5.6.2UDP服务端

"""
服务端
"""
import socketserver

class My_bingfa(socketserver.BaseRequestHandler): #自定义一个类,必须继承socketserver.BaseRequestHandler
    def handle(self):                             #必须有一个handler,用来写通讯循环的
        print(self.request)                       #self.request是元组(客户端发送的数据,socket对象)
        print(self.client_address)                #self.client_address就是(地址,端口号)
        while True:                               #通讯循环
            data = input("输入:").strip()
            res = self.request[1].sendto(data.encode("utf-8"),self.client_address)

phone=socketserver.ThreadingUDPServer(('127.0.0.1',8080),My_bingfa) #指定UDP协议,绑定IP和端口号,绑定上面自定义的类
phone.serve_forever()                                                #启动服务

5.6.3客户端

并发编程是指同时有多个客户端访问服务端,但客户端并不需要特殊处理。故客户端仍保用以前的就可以。

补充:

https的默认端口是443
http的默认端口是80
mysql的默认端口是3306
posted @ 2021-07-30 16:26  hai437  阅读(22)  评论(0编辑  收藏  举报