Python学习—19 网络编程

TCP编程

Client

创建一个基于TCP连接的Socket:

  1. # coding: utf-8
  2. import socket
  3. # 创建一个TCP连接:
  4. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  5. # 建立连接:
  6. s.connect(('52fhy.com', 80))
  7. # 发送HTTP请求
  8. s.send(b'GET / HTTP/1.1\r\nHost: 52fhy.com\r\nConnection: close\r\n\r\n')
  9. # 读取响应
  10. buffer = []
  11. while True:
  12. d = s.recv(1024)
  13. if d:
  14. buffer.append(d)
  15. else:
  16. break
  17. data = b''.join(buffer)
  18. # 关闭连接
  19. s.close()
  20. # 解析响应
  21. header, body = data.split(b'\r\n\r\n', 1)
  22. print(header.decode('utf-8'))
  23. with open('52fhy.html', 'wb') as f:
  24. f.write(body)

输出:

  1. HTTP/1.1 200 OK
  2. Server: nginx/1.4.4
  3. Date: Sat, 11 Feb 2017 08:16:43 GMT
  4. Content-Type: text/html
  5. Content-Length: 8368
  6. Last-Modified: Mon, 19 Sep 2016 07:04:02 GMT
  7. Connection: close
  8. Vary: Accept-Encoding
  9. ETag: "57df8de2-20b0"
  10. Accept-Ranges: bytes

代码说明:
1、创建socket连接的时候使用AF_INET指定使用IPv4协议,如果要用更先进的IPv6,就指定为AF_INET6SOCK_STREAM指定使用面向流的TCP协议。
2、建立连接的connect()接受一个tuple,包含地址和端口号。
3、发送请求是模拟浏览器发送一个Request,实际浏览器请求时包含更多项:

  1. GET / HTTP/1.1
  2. Host: 52fhy.com
  3. Connection: keep-alive
  4. Cache-Control: max-age=0
  5. Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
  6. Upgrade-Insecure-Requests: 1
  7. User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36
  8. Accept-Encoding: gzip, deflate, sdch
  9. Accept-Language: zh-CN,zh;q=0.8,en;q=0.6

换行使用\r\n\r\n\r\n则表示该段内容的结束,用于分隔请求的header和请求的body。
4、读取远程服务器响应则使用recv()每次接受1024byte内容。注意接收的是字节内容,不是字符串,所以拼接以b开头。
5、读取响应完毕,关闭socket连接。
6、通过\r\n\r\n可以区分响应头和返回的body内容(即网页),这里将网页内容保存到了文件里。

Server

上节例子说明了如何编写客户端,这节讲如何创建一个基于TCP的Server端。

和客户端编程相比,服务器编程就要复杂一些。

服务器进程首先要绑定一个端口并监听来自其他客户端的连接。如果某个客户端连接过来了,服务器就与该客户端建立Socket连接,随后的通信就靠这个Socket连接了。

  1. # coding: utf-8
  2. import socket, threading
  3. # 创建一个TCP连接
  4. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  5. # 绑定IP和端口
  6. s.bind(('127.0.0.1', 9999))
  7. # 开始监听客户端连接
  8. # 传入的参数指定等待连接的最大数量
  9. s.listen(3)
  10. # 输出提示语
  11. print('Server is running on %s:%s' % ('127.0.0.1', 9999))
  12. print('Waiting for connection...')
  13. def tcp_link(sock, addr):
  14. print('Accept new connection from %s:%s...' % addr) # 参数addr是一个tuple
  15. sock.send(b'Welcome!') # 发送问候语给客户端
  16. while True:
  17. r = sock.recv(1024)
  18. if not r or r.decode('utf-8') == 'exit': # 结束连接指令
  19. break
  20. # 输出客户端发来的信息,注意元组拼接
  21. msg = addr + (r.decode('utf-8'),)
  22. print('%s:%s : %s' % msg)
  23. # 回复客户端
  24. sock.send( ('you say: %s' % r.decode('utf-8') ).encode('utf-8') )
  25. sock.close()
  26. print('Client %s:%s is closed.' % addr)
  27. while True:
  28. # 接受一个新连接:
  29. sock, addr = s.accept()
  30. print(addr) # ('127.0.0.1', 62090)
  31. # 创建新线程来处理TCP连接:
  32. # 每个连接都必须创建新线程(或进程)来处理,否则,单线程在处理连接的过程中,无法接受其他客户端的连接
  33. t = threading.Thread(target=tcp_link, args=(sock, addr))
  34. t.start()

还需要个客户端发送消息:

  1. # coding: utf-8
  2. import socket
  3. # 创建一个TCP连接
  4. s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  5. # 连接服务端
  6. s.connect(('127.0.0.1', 9999))
  7. # 接收来自服务端的信息
  8. print(s.recv(1024).decode('utf-8'))
  9. # 发送数据给服务端
  10. for x in [b'Python', b'PHP']:
  11. s.send(x)
  12. print(s.recv(1024).decode('utf-8'))
  13. # 发送断开连接命令
  14. s.send(b'exit')
  15. s.close()

先运行服务端:

  1. erver is running on 127.0.0.1:9999
  2. Waiting for connection...

这时候运行一个客户端:

  1. Welcome!
  2. you say: Python
  3. you say: PHP

服务端输出:

  1. ('127.0.0.1', 62428)
  2. Accept new connection from 127.0.0.1:62428...
  3. 127.0.0.1:62428 : Python
  4. 127.0.0.1:62428 : PHP
  5. Client 127.0.0.1:62428 is closed.

编写代码需要注意:
1、服务端和客户端互相发送和接收到的是字节Bytes,需要使用encode()或者decode()转码;
2、服务端使用accept()接收到的是一个元组,例如:('127.0.0.1', 62090);
3、服务端必须使用多线程以处理来自多个客户端的连接;
4、服务端同一个端口,被一个Socket绑定了以后,就不能被别的Socket绑定了。

用TCP协议进行Socket编程在Python中十分简单。对于客户端,要主动连接服务器的IP和指定端口;对于服务器,要首先监听指定端口,然后,对每一个新的连接,创建一个线程或进程来处理。通常,服务器程序会无限运行下去。

UDP编程

TCP建立的是可靠的连接,而使用UDP,无需建立连接,只要知道对方的IP和端口,就可以发送数据。

UDP是一个非连接的协议,传输数据之前源端和终端不建立连接。UDP使用尽最大努力交付,即不保证可靠交付。

使用UDP虽然传输数据不可靠,但比TCP要快。

user_udp_server.py:

  1. # coding: utf-8
  2. import socket
  3. # 建立UDP连接:SOCK_DGRAM表示UDP
  4. s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  5. # 绑定地址和端口,之后无需监听
  6. s.bind(('127.0.0.1', 9999))
  7. print('UDP server is running on %s:%s' % ('127.0.0.1', 9999))
  8. while True:
  9. # recvfrom()返回客户端发过来的数据及地址端口信息
  10. data, addr = s.recvfrom(1024)
  11. msg = addr + (data.decode('utf-8'),)
  12. print('Received from %s:%s : %s' % msg)
  13. # 使用sendto()发送消息,注意第二个参数是addr
  14. s.sendto(('you say : %s ' % data.decode('utf-8')).encode('utf-8') , addr)

user_udp_client.py

  1. # coding: utf-8
  2. import socket
  3. # 建立UDP连接:SOCK_DGRAM表示UDP
  4. s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  5. # 服务端地址
  6. serv_addr = ('127.0.0.1', 9999)
  7. #无需使用connect()连接
  8. # 发送数据给服务端
  9. for x in [b'Python', b'PHP']:
  10. s.sendto(x, serv_addr)
  11. print(s.recv(1024).decode('utf-8'))
  12. # s.close()

先运行服务端:

  1. UDP server is running on 127.0.0.1:9999

再运行客户端:

  1. you say : Python
  2. you say : PHP

服务端输出:

  1. Received from 127.0.0.1:57827 : Python
  2. Received from 127.0.0.1:57827 : PHP

与TCP不同的是:
1、服务端和客户端建立连接时选择UDP:SOCK_DGRAM
2、服务端无需进行监听listen()
3、服务端使用recvfrom()返回客户端发过来的数据及地址端口信息,而不是recv()或者accept()
4、服务端和客户端使用sendto()发送数据,第二个参数必填,是addr元组;
5、客户端创建连接后无需使用connect()连接服务端;

这里服务端我们没有使用多线程,因为比较简单。需要注意的是客户端发送数据如果加入一句s.close(),本次连接关闭,服务端也会退出。

UDP的使用与TCP类似,但是不需要建立连接。此外,服务器绑定UDP端口和TCP端口互不冲突,也就是说,UDP的9999端口与TCP的9999端口可以各自绑定。

作者: 飞鸿影
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
出处:https://www.cnblogs.com/52fhy/p/6389634.html