Python 之 网络编程——SOCKET开发

一、预备知识

对于我们,主要掌握5层协议就行。

物理层:
  转成二进制数序列
数据链路层:

  形成统一的协议:Internet协议
  包括数据头(18个字节,前6个字节原地址,中间6个字节为目标地址,后6个字节为数据的描述)和数据
网络层:

  有IP协议,包括IP头和数据
传输层:
  包括tcp、UDP两个协议:基于端口(0-65535)的协议
应用层:
  包括http、ftp协议

TCP协议:流式协议,先把管道修好
      客户端           服务端
       C-------------------------------->S
         <--------------------------------
      发包:
        C请求,S同意后并我也要挖隧道,C才可以挖隧道到S。(三次握手)
      结束发包:
        C请求,S确认,S请求,C确认(四次挥手)
UDP协议:传输不可靠,但不需要建管道,直接按IP发过去
总结:①TCP传输可靠,但效率低
      ②UDP传输不可靠,但效率高

二、网络编程SOCKET

语法:

1 socket.socket(socket.AF_INET,socket.SOCK_STREAM)

其中:

socket.AF_UNIX:用于本机进程间通讯,为了实现两个进程间的通讯,可以通过创建一个本地的socket来完成(一个机器两个不同的软件)。

socket.AF_INET:我们只关心网络编程,因此大多使用这个(还有socket.AF_INET6被用于ipv6。)

socket.SOCK_STREAM:制动使用面向流的TCP协议。

socket.SOCK_DGRAM:指向UDP协议。

2.1 socket套接字

  • s.recv(1024)接受数据
  • s.send(1024)发送数据
  • s.recvfrom()接收所有数据
  • s.sendall()发送所有数据(本质是循环调用send)
  • s.sendto(信息,(IP地址,端口号)),将发给服务端的消息、(IP地址,端口号)发给服务端。
  • s.close()关闭套接字

一个sendto对应一个recvfrom

2.2 TCP

2.2.1 服务端

由上图可知,服务端需要先建立SOCKET链接,首先需要导入socket模块,并链接。

1 import socket
2 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

之后就需要绑定(主机,端口号)到套接字,开始监听。其中绑定时,IP号和端口号是元组,并且端口号是0-65535,但其中0-1024是给操作系统的,使用需要管理员权限。监听,其中5代表最大链接数量。

s.bind((‘127.0.0.1‘,8080))#0-65535:0-1024给操作系统使用
s.listen(5)

紧接着,服务器通过一个永久循环来接收来自客户端的连接,accept()会一直等待,知道客户端发来信息(暂只考虑单线程情况)。

1 while True:#链接循环
2     conn,client_addr=s.accept()

接下来就是收发消息了,并需要进行通信循环。

1     #收发消息
2     while True:#通信循环
3         try:
4             data=conn.recv(1024) #1024表示接收数据的最大数,单位是bytes
5             print(‘客户端的数据‘,data)
6             conn.send(data.upper())
7         except ConnectionResetError:
8             break
9     conn.close()

接下来就是关闭套接字。

1 s.close()

2.2.2 客户端

首先和服务端一样,需要先建立SOCKET链接,首先需要导入socket模块,并链接。

1 import socket
2 s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

之后通过(主机IP号,端口号)到套接字连接。

1 s.connect((‘127.0.0.1‘,8080))

之后发收消息,同样有着通信循环,和服务端相比,由于没有等待连接,因此少个链接循环。

1 #发收消息
2 while True:#通信循环
3     msg=input(‘>>‘).strip()
4     phone.send(msg.encode(‘utf-8‘))
5     data=phone.recv(1024)
6     print(data.decode(‘utf-8‘))

接下来就是关闭套接字。

1 s.close()

2.3 UDP协议

相比TCP协议,UDP是面向无连接的协议,因此使用UDP协议时,不需要建立连接,只需要知道对方的IP地址和端口号,就可以发送数据包,其不管是否发送到达。

和TCP协议类似,也是服务端和客户端。

2.3.1 服务端

服务端需要先建立SOCKET链接,首先需要导入socket模块,并绑定端口。

1 import socket
2 server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
3 server.bind((‘127.0.0.1‘,8080))

其不需要监听和连接,即不需要listen()和accept(),而是直接接收来自客户端的数据。

1 while True:
2     data,cliend_addr=server.recvfrom(1024)
3     print(data)
4     server.sendto(data.upper(),cliend_addr)

最后关闭套接字。

1 server.close()

2.3.2 客户端

同样,也需要先建立SOCKET链接,首先需要导入socket模块。

1 import socket
2 client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

但不需要调用connect(),直接通过sendto()给服务端发数据。

1 while True:
2     msg=input(‘>>:‘).strip()
3     data=client.sendto(msg.encode(‘utf-8‘),(‘127.0.0.1‘,8080))
4     data,server_addr=client.recvfrom(1024)
5     print(data,server_addr)

最后关闭套接字。

1 server.close()

2.4 粘包现象及解决方案

2.4.1 粘包现象

  何为粘包,在上文中,我们一直使用s.recv(1024)来接收数据,但如果需要接收的数据比1024长,那么剩余的数据会在发送端的IO缓冲区暂存下来,等下次接收端来接收数据时,先将缓冲区的数据发送出去,再接收下次的数据。当然,我们可以将1024改为8192,但数据比这个还大呢,我们接收的额定值就不能变大了,还是会发生这样的事件。因此,这样的事件我们称之为粘包现象。当然,粘包现象仅存在于TCP协议中,UDP协议中不存在。

2.4.2 解决方案

  粘包问题的根源在于,接收端不知道发送端将要传送的字节流的长度,所以解决粘包的方法就是围绕,如何让发送端在发送数据前,把自己将要发送的字节流总大小让接收端知晓,然后接收端来一个死循环接收完所有数据。此处,我们就需要借助于第三方模块struct。用法为:

 1 import json,struct
 2 #为避免粘包,必须制作固定长度的报头
 3 header_dic={‘file_size‘:1073741824,‘file_name‘:‘a.txt‘,‘md5‘:‘8f6fbf8347faa4924a76856701edb0f3‘} #1G文件大小,文件名和md5值
 4
 5 #为了该报头能传送,需要序列化并且转为bytes,用于传输
 6 header_json = json.dumps(header_dic)  # 转成字符串类型
 7 header_bytes = header_json.encode(‘utf-8‘)
 8
 9 #为了让客户端知道报头的长度,用struck将报头长度这个数字转成固定长度:4个字节
10 head_len_bytes=struct.pack(‘i‘,len(head_bytes)) #这4个字节里只包含了一个数字,该数字是报头的长度
11
12 #客户端开始发送报文长度
13 conn.send(head_len_bytes) #先发报头的长度,4个bytes
14 #再发报头的字节格式
15 conn.send(head_bytes)
16 #然后发真实内容的字节格式
17 conn.sendall(文件内容)
18
19 #服务端开始接收
20 head_len_bytes=s.recv(4) #先收报头4个bytes,得到报头长度的字节格式
21 x=struct.unpack(‘i‘,head_len_bytes)[0] #提取报头的长度
22
23 header_bytes=s.recv(x) #按照报头长度x,收取报头的bytes格式
24 header_str=header_bytes.decode(‘utf-8‘)
25 header_dic=json.loads(header_str) #提取报头
26
27 #最后根据报头的内容提取真实的数据,比如数据的长度
28 real_data_len=s.recv(header_dic[‘file_size‘])
29 s.recv(real_data_len)

因此对于一个文件传输:

服务端:

 1 import socket
 2 import os
 3 import struct
 4 import json
 5 share_dir=r‘C:\Users\。。。\Desktop\python\oldboypython\day6\10文件传输\服务端\share‘
 6
 7 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 8 # phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
 9 phone.bind((‘127.0.0.1‘,9901)) #0-65535:0-1024给操作系统使用
10 phone.listen(5)
11 print(‘starting...‘)
12 while True: # 链接循环
13     conn,client_addr=phone.accept()
14     print(client_addr)
15     while True: #通信循环
16         try:
17             #1、收命令
18             res=conn.recv(8096)#b‘get a.txt‘
19             if not res:break #适用于linux操作系统
20             #2、解析命令,提取相应的命令参数
21             cmds=res.decode(‘utf-8‘).split()#[‘get‘,‘a.txt‘]
22             filename=cmds[1]
23
24             #3、以读的方式打开文件,读取文件内容发送给客户端
25             #3.1 制作固定长度的报头
26             header_dic={
27                 ‘filename‘:filename,
28                 ‘md5‘:‘xxdxxx‘,
29                 ‘file_size‘:os.path.getsize(‘%s/%s‘%(share_dir,filename))
30             }
31             header_json=json.dumps(header_dic)#转成字符串类型
32             header_bytes=header_json.encode(‘utf-8‘)
33
34             #3.2 先发送报头的长度
35             conn.send(struct.pack(‘i‘,len(header_bytes)))
36
37             #3.3 再发报头
38             conn.send(header_bytes)
39
40             #3.4 发真实的数据
41             # conn.send(stdout+stderr) #+是一个可以优化的点
42             with open(‘%s/%s‘%(share_dir,filename),‘rb‘) as f:
43                 # conn.send(f.read())
44                 for line in f:
45                     conn.send(line)
46         except ConnectionResetError: #适用于windows操作系统
47             break
48     conn.close()
49
50 phone.close()

文件传输服务端

客户端:

 1 import socket
 2 import struct
 3 import json
 4
 5 download_dir=r‘C:\Users\。。。\Desktop\python\oldboypython\day6\10文件传输\客户端\download‘
 6 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
 7 phone.connect((‘127.0.0.1‘,9901))
 8 while True:
 9     #1、发命令
10     cmd=input(‘>>: ‘).strip() #get a.txt
11     if not cmd:continue
12     phone.send(cmd.encode(‘utf-8‘))
13     #2、接收文件的内容,以写的方式打开新文件,接收服务端发来的文件的内容写入客户端的新文件
14     #2.1 先收报头的长度
15     obj=phone.recv(4)
16     header_size=struct.unpack(‘i‘,obj)[0]
17     #2.2 在收报头
18     header_bytes=phone.recv(header_size)
19     #2.3 从包头中解析出对真实数据的描述的信息
20     header_json=header_bytes.decode(‘utf-8‘)
21     header_dic=json.loads(header_json)
22     print(header_dic)
23     total_size=header_dic[‘file_size‘]
24     file_name=header_dic[‘filename‘]
25     #2.4 接收数据
26     with open(‘%s/%s‘%(download_dir,file_name),‘wb‘) as f:
27         recv_size=0
28         while recv_size<total_size:
29             line=phone.recv(1024)
30             f.write(line)
31             recv_size+=len(line)
32             print(‘总大小:%s,已下载大小:%s‘%(total_size,recv_size))
33 phone.close()

文件传输客户端

原文地址:https://www.cnblogs.com/Umay-wm/p/9139299.html

时间: 2024-07-29 01:31:32

Python 之 网络编程——SOCKET开发的相关文章

网络编程-SOCKET开发

计算机基础知识 计算机分三层:应用程序. 操作系统.硬件.硬件是用来干活的,应用程序想要实现一定的功能就要调用硬件工作.但是如果每一个软件开发,都要写怎么操作硬件的代码,就会有大量重复的代码,而且十分复杂.因此,操作系统就出现了,操作系统相当于提供了一套接口,软件的功能通过操作系统,操作系统再让硬件工作.应用程序无法直接让硬件工作 只能间接地通过操作系统使硬件工作实现特定的功能. 作为应用开发程序员,我们开发的软件都是应用软件,而应用软件必须运行于操作系统之上,操作系统则运行于硬件之上, 应用软

第六章|网络编程-socket开发

1.计算机基础 作为应用开发程序员,我们开发的软件都是应用软件,而应用软件必须运行于操作系统之上,操作系统则运行于硬件之上,应用软件是无法直接操作硬件的,应用软件对硬件的操作必须调用操作系统的接口,由操作系统操控硬件. 比如客户端软件想要基于网络发送一条消息给服务端软件,流程是: 1.客户端软件产生数据,存放于客户端软件的内存中,然后调用接口将自己内存中的数据发送/拷贝给操作系统内存 2.客户端操作系统收到数据后,按照客户端软件指定的规则(即协议).调用网卡发送数据 3.网络传输数据 4.服务端

练习题|网络编程-socket开发

1.什么是C/S架构? C指的是client(客户端软件),S指的是Server(服务端软件),C/S架构的软件,实现服务端软件与客户端软件基于网络通信. 2.互联网协议是什么?分别介绍五层协议中每一层的功能? 互联网协议就是计算机界通用的语言:互联网协议分为osi七层或tcp/ip五层或tcp/ip四层: 物理层功能:主要是基于电器特性发送高低电压(电信号),高电压对应数字1,低电压对应数字0. 数据链路层的功能:定义了电信号的分组方式按照以太网协议:一组电信号构成一个数据包,叫做一组数据'帧

python学习 网络编程--socket

网络通信标准---网络协议互联网协议--osi七层协议五层协议:应用层:应用层.表示层.会话层          传输层:传输层    网络层:网络层    数据链路层:数据链路层    物理层:物理层物理层就是用来发送电信号的数据链路层跑协议,分组标准. ethernet以太网协议,规定电信号如何分组电信号拿来后是一堆数据,只要规定了怎么分组才能拿到正确数据ethernet规定一组电信号构成一个数据报,叫做帧每一数据帧分成:报头head和数据data两部分但凡接入互联网必须要有网卡每个网卡出厂

Python的网络编程 Socket编程

Socket是进程间通信的一种方式,与其他进程间通信的一个主要不同是:能实现不同主机间的进程间通信,网络上各种各样的服务大多都是基于Socket来完成通信的,要解决网络上两台主机间的通信问题,首先要唯一标识该进程,在TCP/IP网络协议中,就是通过(IP地址,协议,端口号)三元组来标识进程的,解决了进程标识问题,就有了通信的基础了 TCP是一种面向连接的传输层协议,TCP Socket是基于一种Client-Server的编程模型,服务端监听客户端的连接请求,一旦建立连接即可以进行传输数据,那么

第六章 网络编程-SOCKET开发——续2

6.5--粘包现象与解决方案 简单远程执行命令程序开发 是时候用户socket干点正事呀,我们来写一个远程执行命令的程序,写一个socket client端在windows端发送指令,一个socket server在Linux端执行命令并返回结果给客户端 执行命令的话,肯定是用我们学过的subprocess模块啦,但注意注意注意: res=subprocess.Popen(cmd.deocde('utf-8'),shell=subprocess.PIPE,stdout=subprocess.PI

Python之网络编程 Socket编程

本节内容: Socket语法及相关 SocketServer实现多并发 Socket语法及相关 socket概念 socket本质上就是在2台网络互通的电脑之间,架设一个通道,两台电脑通过这个通道来实现数据的互相传递. 我们知道网络 通信 都 是基于 ip+port 方能定位到目标的具体机器上的具体服务,操作系统有0-65535个端口,每个端口都可以独立对外提供服务,如果 把一个公司比做一台电脑 ,那公司的总机号码就相当于ip地址, 每个员工的分机号就相当于端口, 你想找公司某个人,必须 先打电

Python的网络编程socket

(1)利用socket进行简单的链接 Python里面的socket支持UDP.TCP.以及进程间的通信,socket可以把我们想要发送的东西封装起来,发送过去,然后反解成原来的样子,事实上网路通信可以理解成都是建立在socket之上,下面的代码是演示利用socket进行简单的链接 #要成一次通信,至少要有两个人,也就是一个服务端,一个客户端 #服务端 '''必须先开启着,等待客户端来进行链接请求, 所以自己要先有个地址,也就是IP,也要现有自己的端口,没有端口进不去''' import soc

网络编程-SOCKET开发之----3. socket通信工作流程

1. TCP的socket通信流程 服务端 1)socket----创建socket对象. 2)bind----绑定本机ip+port. 3)listen----监听来电,若在监听到来电,则建立起连接. 4)accept----再创建一个socket对象给其收发消息.原因是现实中服务端都是面对多个客户端,那么为了区分各个客户端,则每个客户端都需再分配一个socket对象进行收发消息. 5)read.write----就是收发消息了. 客户端 1)socket----创建socket对象. 2)c