一直不是很清楚服务器的定义,对于什么是服务器/客户端架构也只有一个模糊的感觉。最近开始学习,才明白一些什么服务器和客户端的关系。
所谓的服务器,就是提供服务的东西,它是一个硬件或者软件,可以向一个或者多个客户端提供所需要的服务。它存在的目的就是等待客户的请求,然后给客户服务,再接着等待请求。
而客户端,就来连上一个服务器,提出自己的请求,然后等待获得反馈。
比如说,打印机就是一个服务器的例子,与之相连的计算机就是客户端,通过网络连接打印机后,给它提出服务需求(打印)和传输数据(传输内容),然后打印机开始工作,或者返回造成服务失败的原因(比如缺纸或者没有墨)打印机是服务器端,它是一只等待请求的,它在一直工作,计算机这端是客户端,它不是一只工作的。
打印机是一个硬件服务器,也有一些软件服务器,比如说Web服务器,数据库服务器等等。
-----------------------------------------------------------------------------
那么它们又是怎样通过网络连接的呢?客户端/服务器之间的网络编程是如何做的?
首先,我们要创建一个通讯的断电,让服务器可以“监听”请求,好比公司的电话,客户通过电话设备向公司发行请求。
当然一个服务器在有电话之后还得将自己的电话给潜在的客户,才能得到响应,也就是说,必须得将网址发给客户才能有用。
对于客户端来说,同样也是创建一个通信端点,然后建立到与服务器的连接,客户就可以提出请求了。
-------------------------------------------------------------------------------------
关于通讯端点,就要介绍一个概念,叫套接字。
我们之前谈的“通讯端点”的概念,是一个关于通讯的抽象,而套接字就是一种有这样能力数据结构了。
就好比我们应用整型浮点型布尔型一样,套接字也是一种数据结构,我们通过它来访问网络。
套接字起源于上世纪七十年代,在加州大学伯克利分校版本上的Unix上创立,开始的时候被设计用于在同一台机器上的多个应用程序通讯,也就是进程间通讯,有两种类型,一种基于文件系统,一种基于网络。
基于文件系统被用于不同进程通讯是很有意义的,因为文件系统是不同的进程都可以访问的。而不同电脑间,基于网络的套接字就是必须的了。
我先只考虑基于网络的套接字,有两种地址家族,一种是AF_INET,另一种是AF_NETLINK。在大部分时候,我们讨论的都是有链接的AF_INET套接字。
套接字的地址是有两部分组成的,一个是主机,一个是端口。
类似于电话网络的区号和电话号码的含义。主机确定了你访问的机器,也就是一个IP地址,端口是你所访问的服务器软件所使用的端口号。一台机器上可以有很多个程序都在使用端口,合法的端口号范围是0~65535.小于1024的端口号是系统保留的端口号。
-------------------------------------------------------------------------------------
同时,还有一个基础知识是面向连接和面向无连接。
套接字有两种类型,一种是面向连接的,通信前先建立一条连接,然后顺序的、可靠的、不会重复的数据传输,不会有数据边界。实现这种连接的主要协议是传输控制协议(TCP),创建TCP套接字要指定套接字类型是SOCK_STREAM.
另一种是无连接套接字,也就是说传输数据前先不连接,这样数据传输的顺序、可靠性、不重复性就不可保证。这种传输的主要协议是用户数据报协议(UDP)。
----------------------------------------------------------------------------------------------
至此,网络的基础暂且放下,开始讲Python的网络编程。
Python提供了一个socket模块,来创建和使用套接字。
socket()模块函数
socket(socket_family, socket_type, protocol=0)
这个函数可以用于创建一个套接字对象,分别写入套接字家族名和套接字类型。
创建了这个套接字对象后,所有的交互都可以通过该对象的方法调用来进行。
具体的方法可以直接查Python的文档,我这里举例说明一下创建一个TCP服务器的过程:
from socket import * from time import ctime HOST = '' # 主机 PORT = 8002 # 端口号,可以随意选择 BUFSIZ = 1024 ADDR = (HOST, PORT) #主机端口号组成一个套接字地址 tcpSerSock = socket(AF_INET, SOCK_STREAM) #创建一个套接字对象,是AF_INET族的tcp套接字 tcpSerSock.bind(ADDR) #这个函数用于绑定地址到套接字 tcpSerSock.listen(5) # 服务器开始监听连接,参数表示最多允许同时有几个连接进来 while True: print 'waiting for connection...' tcpCliSock, addr = tcpSerSock.accept() #用于等待连接的到来 print '...connected from:',addr while True: data = tcpCliSock.recv(BUFSIZ) #用于接收从客户端发来的数据 参数代表一次最多接受的数据量,这里为1k if not data: break tcpCliSock.send('[%s] %s' % ( ctime(), data)) # 将时间戳作为内容发送给客户端 tcpCliSock.close() tcpSerSock.close()
运行时,可以看到服务器开始运行,等待一个客户端的连接,所以此刻我们需要用客户端去连接这个服务器。
再写一个简单的客户端的程序:
from socket import * HOST = 'localhost' #由于服务器开设在自己电脑上,所以主机是本地 PORT = 8002 #同一个连接端口 BUFSIZ = 1024 ADDR = (HOST, PORT) tcpCliSock = socket(AF_INET, SOCK_STREAM) #同样的TCP套接字 tcpCliSock.connect(ADDR) # 连接相应的地址,初始化TCP服务器的连接 while True: data = raw_input('>') if not data: break tcpCliSock.send(data) # 向服务器传输数据 data = tcpCliSock.recv(BUFSIZ) # 接受服务器端的数据 if not data: break print data tcpCliSock.close()
至此,你运行客户端的程序,就可以验证程序了。
当然,为了学习的乐趣,最好不要在本地连接,还是拿两台电脑来连接比较好,主要是客户端要知道服务器端的IP地址。
IP地址在网络属性里可以找,如图:
然后用同样的程序框架就可以实现两机间的通讯了,最后可以做到如下图的一个对讲机程序:
当然,要做到一个双方都可以自由发言的聊天工具,却又不是这么简单了。需要有新的尝试了。