Python之TCP编程的简单了解


前言

最近准备在单片机上用ATK-SIM900A的GSM模块,需要在ATK-SIM900A模块和服务器之间建立一个 TCP 连接,并实现数据的互相收发,所以简单了学习了一下python的TCP编程。

Socket是网络编程的一个抽象概念。通常我们用一个Socket表示“打开了一个网络链接”,而打开一个Socket需要知道目标计算机的IP地址和端口号,再指定协议类型即可。

伪代码:

ss = socket() #创建服务器套接字
ss.bind() #把地址绑定到套接字上
ss.listen() #监听连接(最大连接数)
info_loop: #服务器无限循环
cs = ss.accept() #接受客户端连接
comm_loop: #通信循环
cs.recv()/cs.send() #对话(接收/发送)
cs.close() #关闭客户端套接字
ss.close() #关闭服务器

所有的套接字都用socket.socket()函数来创建,服务器需要“坐在某个端口上“等待请求”所以它们必须要“绑定”到一个本地地址上,由于TCP是一个面向连接的通信系统,在TCP服务器开始工作之前,要先完成一些设置,TCP服务器必须“监听”连接,设置完成之后服务器就可以进入无限循环了。

一个简单的“单线程”服务器会调用accept()函数等待连接的到来,默认情况下accept()函数是阻塞的,即程序在连接到来之前会处于挂起状态,套接字也支持非阻塞模式。

一旦接收到一个连接,accept()函数就会返回一个单独的客户端套接字用于后续的通信。

客户端

大多数连接都是可靠的TCP连接。创建TCP连接时,主动发起连接的叫客户端,被动响应连接的叫服务器。

举个例子,当我们在浏览器中访问百度时,我们自己的计算机就是客户端,浏览器会主动向百度的服务器发起连接。如果一切顺利,百度的服务器接受了我们的连接,一个TCP连接就建立起来的,后面的通信就是发送网页内容了。

基本过程以下:

  • 第一步:创建一个socket
  • 第二步:建立连接
  • 第三步:发送数据
  • 第四步:读取从server发送过来的数据
  • 第五步:关闭连接
  • 第六步:对收到的数据进行处理

下面为python的TCP编程的client程序的一个小案例(访问百度首页):

#coding:utf-8
#TCP编程的client程序

#第一步:创建一个socket (AF_INET:IPv4, AF_INET6:IPv6) (SOCK_STREAM:面向流的TCP协议)
import socket
ss=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

#第二步:建立连接,參数是一个tuple
ss.connect(('www.baidu.com',80))    #80port是Web服务的标准port

#第三步:发送数据
ss.send(b'GET / HTTP/1.1\r\n Host:www.baidu.com\r\nConnection:close\n\r\n')

#第四步:接收数据
buffer=[]
while True:
    cs=ss.recv(1024)  #recv(max)方法。表示每次仅仅能读取max个字节
    if cs:
        buffer.append(cs)
    else:
        break
date=b''.join(buffer)

#第五步:关闭连接
ss.close()

#第六步:对接收到的数据进行处理
#因为接收到的数据包含http头和网页本身。因此将其分开:
header,html=date.split(b'\r\n\r\n',1)
print(header.decode('utf-8'))
with open('baidu.html','wb') as f:
    f.write(html)

现在,只需要在浏览器中打开这个baidu.html文件,就可以看到百度的首页啦。。。。。

服务器

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

所以,服务器会打开固定端口(比如80)监听,每来一个客户端连接,就创建该Socket连接。由于服务器会有大量来自客户端的连接,所以,服务器要能够区分一个Socket连接是和哪个客户端绑定的。一个Socket依赖4项:服务器地址、服务器端口、客户端地址、客户端端口来唯一确定一个Socket。

但是服务器还需要同时响应多个客户端的请求,所以,每个连接都需要一个新的进程或者新的线程来处理,否则,服务器一次就只能服务一个客户端了。

基本步骤如下:

  • 第一步:创建一个socket
  • 第二步:绑定监听的地址和port,方法bind()仅仅接收一个tuple
  • 第三步:调用listen()方法開始监听port,传入的參数指定等待连接的最大数量
  • 第四步:server程序通过一个永久循环来接收来自client。accept()会等待并返回一个client的连接

我们来编写一个简单的服务器程序,它接收客户端连接,接收成功后给客户端一个成功的响应并打印客户端发送过来的内容!

服务器程序

此文件保存为 tcpServer.py

#python中的server端的程序,其用来测试的client程序为:tcpClient.py
#coding:utf-8

import socket
import threading
def tcplink(sock,addr):
    print('新连接来自 %s:%s'%addr)
    sock.send(b'had connected')
    while True:
        date=sock.recv(1024)
        if not date or date.decode('utf-8')=='exit':
            break 
        print(date.decode('utf-8')) 

    sock.close()
    print(' %s:%s 的连接关闭了。。。。'%addr)

#第一步:创建一个socket
ss=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

#第二步:绑定监听的地址和port,方法bind()仅仅接收一个tuple
ss.bind(('127.0.0.1',8888))

#第三步:调用listen()方法开始监听port。传入的參数指定等待连接的最大数量
ss.listen(10)
#第四步:server程序通过一个永久循环来接收来自client,accept()会等待并返回一个client的连接
while True:
    sock,addr=ss.accept()
    #创建一个新线程来处理TCP链接
    threading.Thread(target=tcplink,args=(sock,addr)).start()

客户端程序

此文件保存为 tcpClient.py

#为tcpServer端写一个测试的client程序
#coding:utf-8
import socket
#第一步:创建一个socket
sc=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#第二步:建立连接
sc.connect(('127.0.0.1',8888))
#第三步:发送数据
sc.send(b'hello World!')
#第三步:接收数据
date=sc.recv(1024)
print(date.decode('utf-8'))

测试

首先运行服务器端程序:

$ python3 tcpServer.py
|

光标说明此时没有客户端连接,不会有任何反应。。。

接着运行客户端程序,立马会得到服务器端的响应!

$ python3 tcpClient.py
had connected

而此时服务器端也会有新的反应!

新连接来自 127.0.0.1:59521...
hello World!
 127.0.0.1:59521 的连接关闭了。。。。
|

说明我们的测试成功了!!!!

总结

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

同一个端口,被一个Socket绑定了以后,就不能被别的Socket绑定了。

讲了这么多,终于可以测试 ATK-SIM900A的GSM模块啦。大家祝我成功!!!