天天看点

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、传输层

二、socket

1、介绍

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

2、 套接字工作流程

服务器端先初始化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

<code>#服务端:</code>

<code>import</code> <code>socket</code>

<code>phone</code><code>=</code><code>socket.socket(socket.AF_INET,socket.SOCK_STREAM) </code><code>#tcp协议</code>

<code>phone.bind((</code><code>'127.0.0.1'</code><code>,</code><code>8081</code><code>))      </code><code>#绑定ip和端口,让客户端连接</code>

<code>phone.listen(</code><code>5</code><code>)                </code><code>#半连接池大小</code>

<code>print</code><code>(</code><code>'starting...'</code><code>)</code>

<code>conn,client_addr</code><code>=</code><code>phone.accept()     </code><code>#等待客户端连接</code>

<code>print</code><code>(conn,client_addr)</code>

<code>data</code><code>=</code><code>conn.recv(</code><code>1024</code><code>)             </code><code>#基于建立好的conn链接对象收发消息</code>

<code>conn.send(data.upper())</code>

<code>conn.close()                   </code><code>#断开链接</code>

<code>phone.close()                   </code><code>#终止服务</code>

<code>#客户端:</code>

<code>phone.connect((</code><code>'127.0.0.1'</code><code>,</code><code>8081</code><code>))        </code><code>#连接服务器的ip和端口</code>

<code>phone.send(</code><code>'hello'</code><code>.encode(</code><code>'utf-8'</code><code>))</code>

<code>data</code><code>=</code><code>phone.recv(</code><code>1024</code><code>)</code>

<code>print</code><code>(data)</code>

<code>phone.close()</code>

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

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

21

22

23

24

<code>phone.bind((</code><code>'127.0.0.1'</code><code>,</code><code>8081</code><code>))                 </code><code>#绑定ip和端口,让客户端连接</code>

<code>phone.listen(</code><code>5</code><code>)                          </code><code>#半连接池大小</code>

<code>while</code> <code>True</code><code>:</code>

<code>    </code><code>conn,client_addr</code><code>=</code><code>phone.accept()           </code><code>#等待客户端连接</code>

<code>    </code><code>print</code><code>(conn,client_addr)</code>

<code>    </code><code>while</code> <code>True</code><code>:</code>

<code>        </code><code>data</code><code>=</code><code>conn.recv(</code><code>1024</code><code>)              </code><code>#基于建立好的conn链接对象收发消息</code>

<code>        </code><code>conn.send(data.upper()) </code>

<code>    </code><code>conn.close()                       </code><code>#断开链接</code>

<code>phone.close()                           </code><code>#终止服务</code>

<code>    </code><code>msg</code><code>=</code><code>input</code><code>(</code><code>'&gt;&gt;: '</code><code>).strip()</code>

<code>    </code><code>if</code> <code>len</code><code>(msg) </code><code>=</code><code>=</code> <code>0</code><code>:</code><code>continue</code>

<code>    </code><code>phone.send(msg.encode(</code><code>'utf-8'</code><code>))</code>

<code>    </code><code>data</code><code>=</code><code>phone.recv(</code><code>1024</code><code>)</code>

<code>    </code><code>print</code><code>(data)</code>

6、粘包

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

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

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

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

7、解决粘包的处理方法

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

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

<code># 服务端:</code>

<code>from</code> <code>socket </code><code>import</code> <code>*</code>

<code>import</code> <code>subprocess</code>

<code>import</code> <code>struct</code>

<code>server</code><code>=</code><code>socket(AF_INET,SOCK_STREAM)</code>

<code>server.bind((</code><code>'127.0.0.1'</code><code>,</code><code>8088</code><code>))</code>

<code>server.listen(</code><code>5</code><code>)</code>

<code>    </code><code>conn,client_addr</code><code>=</code><code>server.accept()</code>

<code>    </code><code>print</code><code>(client_addr)</code>

<code>        </code><code>try</code><code>:</code>

<code>            </code><code>cmd</code><code>=</code><code>conn.recv(</code><code>8096</code><code>)</code>

<code>            </code><code>if</code> <code>not</code> <code>cmd:</code><code>break</code>

<code>            </code><code>obj</code><code>=</code><code>subprocess.Popen(cmd.decode(</code><code>'utf-8'</code><code>),shell</code><code>=</code><code>True</code><code>,</code>

<code>                             </code><code>stdout</code><code>=</code><code>subprocess.PIPE,</code>

<code>                             </code><code>stderr</code><code>=</code><code>subprocess.PIPE</code>

<code>                             </code><code>)</code>

<code>            </code><code>stdout</code><code>=</code><code>obj.stdout.read()</code>

<code>            </code><code>stderr</code><code>=</code><code>obj.stderr.read()</code>

<code>            </code><code>total_size </code><code>=</code> <code>len</code><code>(stdout) </code><code>+</code> <code>len</code><code>(stderr)     </code><code>#制作固定长度的报头</code>

<code>            </code><code>headers</code><code>=</code><code>struct.pack(</code><code>'i'</code><code>,total_size)</code>

<code>            </code><code>conn.send(headers)</code>

<code>            </code><code>conn.send(stdout)                        </code><code>#发送命令的执行结果</code>

<code>            </code><code>conn.send(stderr)</code>

<code>        </code><code>except</code> <code>ConnectionResetError:</code>

<code>            </code><code>break</code>

<code>    </code><code>conn.close()</code>

<code>server.close()</code>

<code># 客户端:</code>

<code>client</code><code>=</code><code>socket(AF_INET,SOCK_STREAM)</code>

<code>client.connect((</code><code>'127.0.0.1'</code><code>,</code><code>8088</code><code>))</code>

<code>    </code><code>cmd</code><code>=</code><code>input</code><code>(</code><code>'&gt;&gt;: '</code><code>).strip()</code>

<code>    </code><code>if</code> <code>not</code> <code>cmd:</code><code>continue</code>

<code>    </code><code>client.send(cmd.encode(</code><code>'utf-8'</code><code>))   </code><code>#发送命令</code>

<code>    </code><code>headers</code><code>=</code><code>client.recv(</code><code>4</code><code>)       </code><code>#先接收命令长度,struct模块生成一个4个字节的结果</code>

<code>    </code><code>total_size </code><code>=</code> <code>struct.unpack(</code><code>'i'</code><code>, headers)[</code><code>0</code><code>]</code>

<code>    </code><code>recv_size</code><code>=</code><code>0</code>                <code>#再收命令的结果</code>

<code>    </code><code>data</code><code>=</code><code>b''</code>

<code>    </code><code>while</code> <code>recv_size &lt; total_size:</code>

<code>        </code><code>recv_data</code><code>=</code><code>client.recv(</code><code>1024</code><code>)</code>

<code>        </code><code>data</code><code>+</code><code>=</code><code>recv_data</code>

<code>        </code><code>recv_size</code><code>+</code><code>=</code><code>len</code><code>(recv_data)</code>

<code>    </code><code>print</code><code>(data.decode(</code><code>'gbk'</code><code>))</code>

<code>client.close()</code>

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

49

50

51

52

53

54

55

56

57

58

59

60

<code>import</code> <code>json</code>

<code>server.bind((</code><code>'127.0.0.1'</code><code>,</code><code>8093</code><code>))</code>

<code>            </code><code>headers </code><code>=</code> <code>{                                        </code><code>#制作报头</code>

<code>                </code><code>'filepath'</code><code>: </code><code>'a.txt'</code><code>,</code>

<code>                </code><code>'md5'</code><code>: </code><code>'123sxd123x123'</code><code>,</code>

<code>                </code><code>'total_size'</code><code>: </code><code>len</code><code>(stdout) </code><code>+</code> <code>len</code><code>(stderr)</code>

<code>            </code><code>}</code>

<code>            </code><code>headers_json </code><code>=</code> <code>json.dumps(headers)                </code><code>#把headers转为json格式</code>

<code>            </code><code>headers_bytes </code><code>=</code> <code>headers_json.encode(</code><code>'utf-8'</code><code>)     </code><code>#前面的json结果得到字节形式</code>

<code>            </code><code>conn.send(struct.pack(</code><code>'i'</code><code>,</code><code>len</code><code>(headers_bytes)))    </code><code>#先发报头的长度</code>

<code>            </code><code>conn.send(headers_bytes)                          </code><code>#发送报头</code>

<code>            </code><code>conn.send(stdout)                                 </code><code>#发送真实数据,正确的stdout,错误的stderr</code>

<code>client.connect((</code><code>'127.0.0.1'</code><code>,</code><code>8093</code><code>))</code>

<code>    </code><code>client.send(cmd.encode(</code><code>'utf-8'</code><code>))</code>

<code>    </code><code>headers_size</code><code>=</code><code>struct.unpack(</code><code>'i'</code><code>,client.recv(</code><code>4</code><code>))[</code><code>0</code><code>]</code>

<code>    </code><code>headers_bytes</code><code>=</code><code>client.recv(headers_size)</code>

<code>    </code><code>headers_json</code><code>=</code><code>headers_bytes.decode(</code><code>'utf-8'</code><code>)</code>

<code>    </code><code>headers_dic</code><code>=</code><code>json.loads(headers_json)</code>

<code>    </code><code>print</code><code>(</code><code>'========&gt;'</code><code>,headers_dic)</code>

<code>    </code><code>total_size</code><code>=</code><code>headers_dic[</code><code>'total_size'</code><code>]</code>

<code>    </code><code>recv_size</code><code>=</code><code>0</code>

9、文件下载

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

<code>#服务端</code>

<code>import</code> <code>os</code>

<code>SHARE_DIR</code><code>=</code><code>r</code><code>'F:\SHARE'</code>          <code>#目标文件路径</code>

<code>class</code> <code>FtpServer:</code>

<code>    </code><code>def</code> <code>__init__(</code><code>self</code><code>,host,port):</code>

<code>        </code><code>self</code><code>.host</code><code>=</code><code>host</code>

<code>        </code><code>self</code><code>.port</code><code>=</code><code>port</code>

<code>        </code><code>self</code><code>.server</code><code>=</code><code>socket.socket(socket.AF_INET,socket.SOCK_STREAM)</code>

<code>        </code><code>self</code><code>.server.bind((</code><code>self</code><code>.host,</code><code>self</code><code>.port))</code>

<code>        </code><code>self</code><code>.server.listen(</code><code>5</code><code>)</code>

<code>    </code><code>def</code> <code>serve_forever(</code><code>self</code><code>):</code>

<code>        </code><code>print</code><code>(</code><code>'server starting...'</code><code>)</code>

<code>        </code><code>while</code> <code>True</code><code>:</code>

<code>            </code><code>self</code><code>.conn,</code><code>self</code><code>.client_addr</code><code>=</code><code>self</code><code>.server.accept()</code>

<code>            </code><code>print</code><code>(</code><code>self</code><code>.client_addr)</code>

<code>            </code><code>while</code> <code>True</code><code>:</code>

<code>                </code><code>try</code><code>:</code>

<code>                    </code><code>data</code><code>=</code><code>self</code><code>.conn.recv(</code><code>1024</code><code>)  </code><code>#params_json.encode('utf-8')</code>

<code>                    </code><code>if</code> <code>not</code> <code>data:</code><code>break</code>

<code>                    </code><code>params</code><code>=</code><code>json.loads(data.decode(</code><code>'utf-8'</code><code>)) </code><code>#params=['get','a.txt']</code>

<code>                    </code><code>cmd</code><code>=</code><code>params[</code><code>0</code><code>]</code>

<code>                    </code><code>if</code> <code>hasattr</code><code>(</code><code>self</code><code>,cmd):</code>

<code>                        </code><code>func</code><code>=</code><code>getattr</code><code>(</code><code>self</code><code>,cmd)</code>

<code>                        </code><code>func(params)</code>

<code>                    </code><code>else</code><code>:</code>

<code>                        </code><code>print</code><code>(</code><code>'\033[45mcmd not exists\033[0m'</code><code>)</code>

<code>                </code><code>except</code> <code>ConnectionResetError:</code>

<code>                    </code><code>break</code>

<code>            </code><code>self</code><code>.conn.close()</code>

<code>        </code><code>self</code><code>.server.close()</code>

<code>    </code><code>def</code> <code>get(</code><code>self</code><code>,params): </code><code>#params=['get','a.txt']</code>

<code>        </code><code>filename</code><code>=</code><code>params[</code><code>1</code><code>] </code><code>#filename='a.txt'</code>

<code>        </code><code>filepath</code><code>=</code><code>os.path.join(SHARE_DIR,filename)</code>

<code>        </code><code>if</code> <code>os.path.exists(filepath):</code>

<code>            </code><code>headers </code><code>=</code> <code>{                                           </code><code>#制作报头</code>

<code>                </code><code>'filename'</code><code>: filename,</code>

<code>                </code><code>'filesize'</code><code>: os.path.getsize(filepath)</code>

<code>            </code><code>headers_json </code><code>=</code> <code>json.dumps(headers)</code>

<code>            </code><code>headers_bytes </code><code>=</code> <code>headers_json.encode(</code><code>'utf-8'</code><code>)</code>

<code>            </code><code>self</code><code>.conn.send(struct.pack(</code><code>'i'</code><code>,</code><code>len</code><code>(headers_bytes)))  </code><code>#先发报头的长度</code>

<code>            </code><code>self</code><code>.conn.send(headers_bytes)                        </code><code>#发送报头</code>

<code>            </code><code>with </code><code>open</code><code>(filepath,</code><code>'rb'</code><code>) as f:                     </code><code>#发送真实的数据</code>

<code>                </code><code>for</code> <code>line </code><code>in</code> <code>f:</code>

<code>                    </code><code>self</code><code>.conn.send(line)</code>

<code>    </code><code>def</code> <code>put(</code><code>self</code><code>):</code>

<code>        </code><code>pass</code>

<code>if</code> <code>__name__ </code><code>=</code><code>=</code> <code>'__main__'</code><code>:</code>

<code>    </code><code>server</code><code>=</code><code>FtpServer(</code><code>'127.0.0.1'</code><code>,</code><code>8081</code><code>)</code>

<code>    </code><code>server.serve_forever()</code>

<code>##客户端</code>

<code>DOWNLOAD_DIR</code><code>=</code><code>r</code><code>'F:\DOWNLOAD'</code>    <code>#下载路径</code>

<code>class</code> <code>FtpClient:</code>

<code>        </code><code>self</code><code>.client</code><code>=</code><code>socket.socket(socket.AF_INET,socket.SOCK_STREAM)</code>

<code>        </code><code>self</code><code>.client.connect((</code><code>self</code><code>.host,</code><code>self</code><code>.port))</code>

<code>    </code><code>def</code> <code>interactive(</code><code>self</code><code>):</code>

<code>            </code><code>data</code><code>=</code><code>input</code><code>(</code><code>'&gt;&gt;: '</code><code>).strip() </code><code>#get a.txt</code>

<code>            </code><code>if</code> <code>not</code> <code>data:</code><code>continue</code>

<code>            </code><code>params</code><code>=</code><code>data.split() </code><code>#parmas=['get','a.txt']</code>

<code>            </code><code>cmd</code><code>=</code><code>params[</code><code>0</code><code>] </code><code>#cmd='get'</code>

<code>            </code><code>if</code> <code>hasattr</code><code>(</code><code>self</code><code>,cmd):</code>

<code>                </code><code>func</code><code>=</code><code>getattr</code><code>(</code><code>self</code><code>,cmd)</code>

<code>                </code><code>func(params) </code><code>#func(['get','a.txt'])</code>

<code>    </code><code>def</code> <code>get(</code><code>self</code><code>,params):</code>

<code>        </code><code>params_json</code><code>=</code><code>json.dumps(params)</code>

<code>        </code><code>self</code><code>.client.send(params_json.encode(</code><code>'utf-8'</code><code>))</code>

<code>        </code><code>headers_size </code><code>=</code> <code>struct.unpack(</code><code>'i'</code><code>, </code><code>self</code><code>.client.recv(</code><code>4</code><code>))[</code><code>0</code><code>]   </code><code>#接收报头长度</code>

<code>        </code><code>headers_bytes </code><code>=</code> <code>self</code><code>.client.recv(headers_size)         </code><code>#接收报头</code>

<code>        </code><code>headers_json </code><code>=</code> <code>headers_bytes.decode(</code><code>'utf-8'</code><code>)</code>

<code>        </code><code>headers_dic </code><code>=</code> <code>json.loads(headers_json)</code>

<code>        </code><code># print('========&gt;', headers_dic)</code>

<code>        </code><code>filename </code><code>=</code> <code>headers_dic[</code><code>'filename'</code><code>]</code>

<code>        </code><code>filesize </code><code>=</code> <code>headers_dic[</code><code>'filesize'</code><code>]</code>

<code>        </code><code>filepath </code><code>=</code> <code>os.path.join(DOWNLOAD_DIR, filename)</code>

<code>        </code><code>with </code><code>open</code><code>(filepath, </code><code>'wb'</code><code>) as f:                   </code><code>#接收真实数据</code>

<code>            </code><code>recv_size </code><code>=</code> <code>0</code>

<code>            </code><code>while</code> <code>recv_size &lt; filesize:</code>

<code>                </code><code>line </code><code>=</code> <code>self</code><code>.client.recv(</code><code>1024</code><code>)</code>

<code>                </code><code>recv_size </code><code>+</code><code>=</code> <code>len</code><code>(line)</code>

<code>                </code><code>f.write(line)</code>

<code>            </code><code>print</code><code>(</code><code>'===&gt;下载成功'</code><code>)</code>

<code>    </code><code>client</code><code>=</code><code>FtpClient(</code><code>'127.0.0.1'</code><code>,</code><code>8081</code><code>)</code>

<code>    </code><code>client.interactive()</code>

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