python的网络编程

一、系统和网络

1、系统

操作系统: (Operating System,简称OS)是管理和控制计算机硬件与软件资源的计算机程序,是直接运行在“裸机”上的最基本的系统软件,任何其他软件都必须在操作系统的支持下才能运行。

2、osi七层协议

osi七层:

    物理层

    数据链路层

    网络层

    传输层

    会话层

    表示层

    应用层

tcp/ip五层:

    物理层

    数据链路层

    网络层

    传输层

    应用层

tcp/ip四层:

    网络接口层

    网络层

    传输层

    应用层


3、数据链路层

以太网协议:

       # 一组电信号构成一个数据包,叫做‘帧’

       # 每一数据帧分成:报头head和数据data两部分


head包含:(固定18个字节)

        发送者/源地址,6个字节

        接收者/目标地址,6个字节

        数据类型,6个字节

data包含:(最短46字节,最长1500字节)


数据包的具体内容:

head长度+data长度=最短64字节,最长1518字节,超过最大限制就分片发送


4、网络层

ip数据包也分为head和data部分

  head:长度为20到60字节

  data:最长为65,515字节

而以太网数据包的”数据”部分,最长只有1500字节。因此,如果IP数据包超过了1500字节,它就需要分割成几个以太网数据包,分开发送了。


5、传输层

tcp  三次握手.png

二、socket

1、介绍

socket图例.png

我们经常把socket翻译为套接字,socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。

2、 套接字工作流程

套接字工作流程.png


服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束

3、套接字函数

#1、服务端套接字函数

s.bind()    绑定(主机,端口号)到套接字

s.listen()  开始TCP监听

s.accept()  被动接受TCP客户的连接,(阻塞式)等待连接的到来

#2、客户端套接字函数

s.connect()     主动初始化TCP服务器连接

s.connect_ex()  connect()函数的扩展版本,出错时返回出错码,而不是抛出异常

#3、公共用途的套接字函数

s.recv()            接收TCP数据

s.send()            发送TCP数据(send在待发送数据量大于己端缓存区剩余空间时,数据丢失,不会发完)

s.sendall()         发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于己端缓存区剩余空间时,数据不丢失,循环调用send直到发完)

s.recvfrom()        接收UDP数据

s.sendto()          发送UDP数据

s.getpeername()     连接到当前套接字的远端的地址

s.getsockname()     当前套接字的地址

s.getsockopt()      返回指定套接字的参数

s.setsockopt()      设置指定套接字的参数

s.close()           关闭套接字

#4、面向锁的套接字方法

s.setblocking()     设置套接字的阻塞与非阻塞模式

s.settimeout()      设置阻塞套接字操作的超时时间

s.gettimeout()      得到阻塞套接字操作的超时时间

#5、面向文件的套接字的函数

s.fileno()          套接字的文件描述符

s.makefile()        创建一个与该套接字相关的文件


4、实现基于TCP的套接字(先启动服务端)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#服务端:
import  socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  #tcp协议
phone.bind(( '127.0.0.1' , 8081 ))       #绑定ip和端口,让客户端连接
phone.listen( 5 )                 #半连接池大小
print ( 'starting...' )
conn,client_addr = phone.accept()      #等待客户端连接
print (conn,client_addr)
data = conn.recv( 1024 )              #基于建立好的conn链接对象收发消息
conn.send(data.upper())
conn.close()                    #断开链接
phone.close()                    #终止服务
#客户端:
import  socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  #tcp协议
phone.connect(( '127.0.0.1' , 8081 ))         #连接服务器的ip和端口
phone.send( 'hello' .encode( 'utf-8' ))
data = phone.recv( 1024 )
print (data)
phone.close()

5、最终版基于TCP的套接字

上面的值实现发送一条消息和连接一个客户端,所以要对程序进行修改

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#服务端:
import  socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  #tcp协议
phone.bind(( '127.0.0.1' , 8081 ))                  #绑定ip和端口,让客户端连接
phone.listen( 5 )                           #半连接池大小
while  True :
     conn,client_addr = phone.accept()            #等待客户端连接
     print (conn,client_addr)
     while  True :
         data = conn.recv( 1024 )               #基于建立好的conn链接对象收发消息
         conn.send(data.upper()) 
     conn.close()                        #断开链接
phone.close()                            #终止服务
#客户端:
import  socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)  #tcp协议
phone.connect(( '127.0.0.1' , 8081 ))         #连接服务器的ip和端口
while  True :
     msg = input ( '>>: ' ).strip()
     if  len (msg)  = =  0 : continue
     phone.send(msg.encode( 'utf-8' ))
     data = phone.recv( 1024 )
     print (data)
phone.close()

6、粘包

应用程序所看到的数据是一个整体,或说是一个流(stream),一条消息有多少字节对应用程序是不可见的,因此TCP协议是面向流的协议,这也是容易出现粘包问题的原因。

所谓粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据所造成的。

此外,发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一个TCP段。

若连续几次需要send的数据都很少,通常TCP会根据优化算法把这些数据合成一个TCP段后一次发送出去,这样接收方就收到了粘包数据。

7、解决粘包的处理方法

程序流程:客户端发送命令,服务端在本地执行后,返回得到的结果给客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# 服务端:
from  socket  import  *
import  subprocess
import  struct
server = socket(AF_INET,SOCK_STREAM)
server.bind(( '127.0.0.1' , 8088 ))
server.listen( 5 )
while  True :
     conn,client_addr = server.accept()
     print (client_addr)
     while  True :
         try :
             cmd = conn.recv( 8096 )
             if  not  cmd: break
             obj = subprocess.Popen(cmd.decode( 'utf-8' ),shell = True ,
                              stdout = subprocess.PIPE,
                              stderr = subprocess.PIPE
                              )
             stdout = obj.stdout.read()
             stderr = obj.stderr.read()
             total_size  =  len (stdout)  +  len (stderr)      #制作固定长度的报头
             headers = struct.pack( 'i' ,total_size)
             conn.send(headers)
             conn.send(stdout)                         #发送命令的执行结果
             conn.send(stderr)
         except  ConnectionResetError:
             break
     conn.close()
server.close()
# 客户端:
from  socket  import  *
import  struct
client = socket(AF_INET,SOCK_STREAM)
client.connect(( '127.0.0.1' , 8088 ))
while  True :
     cmd = input ( '>>: ' ).strip()
     if  not  cmd: continue
     client.send(cmd.encode( 'utf-8' ))    #发送命令
     headers = client.recv( 4 )        #先接收命令长度,struct模块生成一个4个字节的结果
     total_size  =  struct.unpack( 'i' , headers)[ 0 ]
     recv_size = 0                 #再收命令的结果
     data = b''
     while  recv_size < total_size:
         recv_data = client.recv( 1024 )
         data + = recv_data
         recv_size + = len (recv_data)
     print (data.decode( 'gbk' ))
client.close()

8、解决粘包的处理方法加强版

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#服务端:
from  socket  import  *
import  subprocess
import  struct
import  json
server = socket(AF_INET,SOCK_STREAM)
server.bind(( '127.0.0.1' , 8093 ))
server.listen( 5 )
while  True :
     conn,client_addr = server.accept()
     print (client_addr)
     while  True :
         try :
             cmd = conn.recv( 8096 )
             if  not  cmd: break
             obj = subprocess.Popen(cmd.decode( 'utf-8' ),shell = True ,
                              stdout = subprocess.PIPE,
                              stderr = subprocess.PIPE
                              )
             stdout = obj.stdout.read()
             stderr = obj.stderr.read()
             headers  =  {                                         #制作报头
                 'filepath' 'a.txt' ,
                 'md5' '123sxd123x123' ,
                 'total_size' len (stdout)  +  len (stderr)
             }
             headers_json  =  json.dumps(headers)                 #把headers转为json格式
             headers_bytes  =  headers_json.encode( 'utf-8' )      #前面的json结果得到字节形式
             conn.send(struct.pack( 'i' , len (headers_bytes)))     #先发报头的长度
             conn.send(headers_bytes)                           #发送报头
             conn.send(stdout)                                  #发送真实数据,正确的stdout,错误的stderr
             conn.send(stderr)
         except  ConnectionResetError:
             break
     conn.close()
server.close()
#客户端:
from  socket  import  *
import  struct
import  json
client = socket(AF_INET,SOCK_STREAM)
client.connect(( '127.0.0.1' , 8093 ))
while  True :
     cmd = input ( '>>: ' ).strip()
     if  not  cmd: continue
     client.send(cmd.encode( 'utf-8' ))
     headers_size = struct.unpack( 'i' ,client.recv( 4 ))[ 0 ]
     headers_bytes = client.recv(headers_size)
     headers_json = headers_bytes.decode( 'utf-8' )
     headers_dic = json.loads(headers_json)
     print ( '========>' ,headers_dic)
     total_size = headers_dic[ 'total_size' ]
     recv_size = 0
     data = b''
     while  recv_size < total_size:
         recv_data = client.recv( 1024 )
         data + = recv_data
         recv_size + = len (recv_data)
     print (data.decode( 'gbk' ))
client.close()

9、文件下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#服务端
import  socket
import  os
import  json
import  struct
SHARE_DIR = r 'F:\SHARE'           #目标文件路径
class  FtpServer:
     def  __init__( self ,host,port):
         self .host = host
         self .port = port
         self .server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
         self .server.bind(( self .host, self .port))
         self .server.listen( 5 )
     def  serve_forever( self ):
         print ( 'server starting...' )
         while  True :
             self .conn, self .client_addr = self .server.accept()
             print ( self .client_addr)
             while  True :
                 try :
                     data = self .conn.recv( 1024 )   #params_json.encode('utf-8')
                     if  not  data: break
                     params = json.loads(data.decode( 'utf-8' ))  #params=['get','a.txt']
                     cmd = params[ 0 ]
                     if  hasattr ( self ,cmd):
                         func = getattr ( self ,cmd)
                         func(params)
                     else :
                         print ( '\033[45mcmd not exists\033[0m' )
                 except  ConnectionResetError:
                     break
             self .conn.close()
         self .server.close()
     def  get( self ,params):  #params=['get','a.txt']
         filename = params[ 1 #filename='a.txt'
         filepath = os.path.join(SHARE_DIR,filename)
         if  os.path.exists(filepath):
             headers  =  {                                            #制作报头
                 'filename' : filename,
                 'md5' '123sxd123x123' ,
                 'filesize' : os.path.getsize(filepath)
             }
             headers_json  =  json.dumps(headers)
             headers_bytes  =  headers_json.encode( 'utf-8' )
             self .conn.send(struct.pack( 'i' , len (headers_bytes)))   #先发报头的长度
             self .conn.send(headers_bytes)                         #发送报头
             with  open (filepath, 'rb' ) as f:                      #发送真实的数据
                 for  line  in  f:
                     self .conn.send(line)
     def  put( self ):
         pass
if  __name__  = =  '__main__' :
     server = FtpServer( '127.0.0.1' , 8081 )
     server.serve_forever()
##客户端
import  socket
import  struct
import  json
import  os
DOWNLOAD_DIR = r 'F:\DOWNLOAD'     #下载路径
class  FtpClient:
     def  __init__( self ,host,port):
         self .host = host
         self .port = port
         self .client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
         self .client.connect(( self .host, self .port))
     def  interactive( self ):
         while  True :
             data = input ( '>>: ' ).strip()  #get a.txt
             if  not  data: continue
             params = data.split()  #parmas=['get','a.txt']
             cmd = params[ 0 #cmd='get'
             if  hasattr ( self ,cmd):
                 func = getattr ( self ,cmd)
                 func(params)  #func(['get','a.txt'])
     def  get( self ,params):
         params_json = json.dumps(params)
         self .client.send(params_json.encode( 'utf-8' ))
         headers_size  =  struct.unpack( 'i' self .client.recv( 4 ))[ 0 ]    #接收报头长度
         headers_bytes  =  self .client.recv(headers_size)          #接收报头
         headers_json  =  headers_bytes.decode( 'utf-8' )
         headers_dic  =  json.loads(headers_json)
         # print('========>', headers_dic)
         filename  =  headers_dic[ 'filename' ]
         filesize  =  headers_dic[ 'filesize' ]
         filepath  =  os.path.join(DOWNLOAD_DIR, filename)
         with  open (filepath,  'wb' ) as f:                    #接收真实数据
             recv_size  =  0
             while  recv_size < filesize:
                 line  =  self .client.recv( 1024 )
                 recv_size  + =  len (line)
                 f.write(line)
             print ( '===>下载成功' )
if  __name__  = =  '__main__' :
     client = FtpClient( '127.0.0.1' , 8081 )
     client.interactive()





本文转自 宋鹏超 51CTO博客,原文链接:http://blog.51cto.com/qidian510/2066654,如需转载请自行联系原作者

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值