时间:2023-05-29 18:03:38

关键词:网络通信;Tcp/IP;Socket
中图分类号:TP393 文献标识码:A 文章编号:1009-3044(2013)35-8116-02
1 C/S中TCP/IP与Socket
1.1 TCP/IP协议的优点
每种网络协议都有自己的优点,但是只有TCP/IP允许与Internet完全的连接。TCP/IP是在60年代由麻省理工学院开发的,即便网络遭到了大部分破坏,TCP/IP仍然能够维持有效的通信。在所有的OS中都集成了TCP/IP协议,TCP/IP是实现网际互联的基础,同时TCP/IP协议也具备了可扩展性和可靠性的需求。在现今的网络环境下,能有效地保证点对点信息的传输和安全是网络应用的需要。
1.2 Socket的作用
Socket通常称作"套接字",应用程序通常通过"套接字"向网络发出请求或应答网络请求,Socket是面向C/S模型而设计的,针对客户和服务器程序提供不同的socket系统调用。客户随机申请一个socket (相当于在电话系统中一个想打电话的人可以在任何一台入网电话上拨号呼叫),系统为任何有联网需求及信息需求的客户分配一个socket号;服务器拥有全局socket号 ,客户端socket号相当于电话系统中的各个公共电话,而服务器端相当于总机电话。
1.3 Socket实现网络通信的基本原理
TCP/IP系统中的端口号是一个16位的数字,它的范围是0~65535。客户和服务器必须事先约定所使用的端口。在C/S开发模式中,网络中的各个节点都有独有的IP地址用来表示各节点的身份,服务器预留端口作为链接的接口,而Socket通过在它们之间两两建立链路的方式,来实现网络中点对点的通信。
Socket分为客户端Socket对象及ServerSocket类对象,它们类库位于包中。ServerSocket用于服务器端,Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会产生一个Socket实例,通过这个实例,完成所需的会话。对于一个网络连接来说,套接字是平等的,并没有差别,不因为在服务器端或在客户端而产生不同级别。不管是Socket还是ServerSocket它们的工作都是通过Java语言提供的SocketImpl接口及其方法来完成网络编程所需的网络通信功能。 这套API使Java程序员不用考虑复杂的网络协议以及底层的数据传输方式,而直接用面向对象的思想来实现网络传输。
2 建立C/S基于TCP/IP Socket通信模型
2.1 客户端Socket通信过程
Socket是网络上运行的两个程序间双向通信的一端,它既可以接受请求,也可以发送请求,利用它可以较为方便地编写网络上数据的传递。在Java中,利用Socket类的方法,就可以实现两台计算机之间的通信。客户端Socket通信整个过程主要有4个阶段:(1)请求阶段;(2)建立连接阶段;(3)通信阶段;(4)关闭阶段。
2.2 TCP Socket C/S模型及工作流程
TCP/IP本身是一个工业标准,在C/S模式中,主要通过三次握手,实现通信:
1):主机A发送位码为syn=1,随机产生seq number=1234567的数据包到服务器,主机B由SYN=1知道,A要求建立联机;
2):主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1),syn=1,ack=1,随机产生seq=7654321的包;
3):主机A收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,主机A会再发送ack number=(主机B的seq+1),ack=1,主机B收到后确认seq值与ack=1则连接建立成功;
图1 C/S程序工作流程图
3 程序实现
3.1 C/S模型下基本程序框架
3.1.1 客户端Socket的实现
客户端通过Socket连接服务器,主要过程经过4个阶段,具体代码如下:
Socket PersonalConnect=new Socket(“服务器IP”,PORT);
……;//socket、bufferedReader及PrintStream对象作为线程成员
PersonalConnect.close();
3.1.2 服务器端ServerSocket的实现
ServerSocket myTcpServer=new ServerSocket(PORT);//设定服务器端口
while(true){//实现多客户链接
Socket c_s=myTcpServer.accept();
……;//socket、bufferedReader及PrintStream对象作为线程成员
c_s.close();}
对于TCP C/S模式中两端的Java类中,主要有三个类成员它们分别是Socket对象、BufferedReader对象、PrintStream对象,而服务器端还有个ServerSocket对象。其中Socket及ServerSocket对象完成连接两端的请求绑定套接的过程,而输入输出流在套接中进行通信。如果一端要表达消息发送结束,则可以关闭其输出流,但并不关闭套接字对象,这就是“半关闭”的作用。
3.2 通过线程管理C/S中的通信
3.2.1 客户端ClientThread的实现
ClientThread.java的主要代码:
public class ClientThread implements Runnable{
……;
public ClientThread(Socket s) throws IOException{
this.personalConnect=s;
……;}
public void run() {……}
}
3.2.2服务器端ServerThread的实现
Tcp_Server.java文件的主要代码:
public static ArrayList socketList = new ArrayList();
//用来保存客户端Socket对象
ServerSocket myTcpServer=new ServerSocket(“服务器端口“);
Socket c_s = myTcpServer.accept();
socketList.add(c_s);//响应客户端Socket请求,并保存Socket对象进入socketList
new Thread(new ServerThread(c_s)).start(); ServerThread.java文件的主要代码:
public class ServerThread implements Runnable
{……;//socket、bufferedReader及PrintStream对象作为线程属性
public ServerThread(Socket s) throws IOException {
this.c_s = s; }}
利用线程在一个List结构中实现多个不同的Socket对象的管理即每一个客户端通过线程实现套接字对象的管理与通信。Accept()方法用于产生”阻塞”,直到接受到一个连接,并且返回一个客户端的Socket对象实例。”阻塞”是一个术语,它使程序运行暂时”停留”在这个地方,直到一个会话产生,然后程序继续。
为了更好地实现对于客户端对象的管理,实现对于其Socket与其消息的管理,我们也可以通过HaspMap对象,来实现单一客户K与其多条消息V的关系映射。实现“Kclient+Vserver“的形式传送消息即“用户名:消息类容”的形式输入,服务器端以HaspMap解析读入的消息对象,辨识消息的发送者与其消息之间的映射关系,实现点对点的消息传输。
4 结束语
本文主要介绍的内容仅适合于TCP/IP网络协议。Java Socket 可以实现TCP协议在两台计算机之间建立可靠连接。连接安全可靠,数据不会丢失,Java平台还提供了更安全的SSLSocket类,SSLSocket通信是对SOCKET通信的拓展。在Socket基础上添加了一层安全性保护,提供了更高的安全性,包括身份验证、数据加密以及完整性验证。其中身份验证用于数字证书的发放和应用。数据加密可以通过密钥防止消息传递过程中被别人监听而造成的损失。所以Socket编程在网络通信中得到了广泛的应用。
参考文献:
关键词:无线通信;负荷分担;Socket通信
中图分类号:TP393 文献标识码:A 文章编号:1009-3044(2013)28-6278-06
通信技术与计算机结合,已成为集无线、有线传输、数字程控交换和各类新型终端为一体的高效能综合通信手段。
在通信网中,除了传递具体业务信息外,还在通信设备之间传递一些控制信号,而信令网就是传输这些控制信号的网络。信令网的安全措施中,广泛地使用了把信令业务分配给各信令链的负荷分担技术。通常,在通信网中负荷分担使用以下两种方式:同一信令链路组内各信令链路间的负荷分担,用于两个信令点(包括综合型信令转接点)采用直联工作方式的信令链路之间;不同信令链路组间的信令链路的负荷分担,用于一个信令点连接两个信令转接点和信令转接点间不同信令链路组之间采用准直联工作方式。目前信令链路的费用较低,为了提高信令链路的可靠性,应尽可能不采用不同链路组间的信令链路负荷分担方式。
本文设计了一套考虑负荷分担的通信系统仿真,用来模拟无线通信时的负荷分担情况。
1 Socket通信
1.1套接字(Socket)基本概念
套接字是进行程序间通信的一种方法,同时是通信的基石。套接字是支持TCP/IP协议的网络通信的基本操作单元,可以将套接字看作不同主机之间的进程来进行双向通信的端点,它构成了单个主机内及整个网络间的编程界面。套接字存在于通信域中,通信域是为了处理一般线程通过套接字通信而引进的一种抽象的概念。套接字通常和同一个域中的套接字进行数据交换。各种进程使用这个相同的域互相之间用Internet协议簇来进行通信。
1.2 Socket通信原理
Socket在计算机中提供了一个通信端口,可以通过这个端口与任何一个具有Socket接口的计算机通信。应用程序在网络上传输、接收的信息都通过这个Socket接口来实现。在应用开发中就像使用文件句柄一样,可以对Socket句柄进行读、写操作。同时,Socket可以看作网络通信的一个端点,网络通信包括两台主机或两个进程,通过网络传递数据。网络对话的每一端称为一个端点。当使用Socket接口对网络通信编程时,Socket是网络通信过程中端点的抽象表示。为了通过Socket接口进行网络通信,程序在网络对话的每一端都需要一个Socket。
本文采用流式Socket(SOCK_STREAM)通信方式:在这种方式下,两个通讯的应用程序之间先要建立一种虚拟的连接,提供可靠的、面向连接的通信流,从而保证数据传输的正确性和有序性。
2 负荷分担通信系统仿真平台的设计
2.1 系统总体设计
系统整体包括总控端、负荷端和测试端三大部分。如图1所示,该系统用来模拟实现总控端的负荷分配和负荷端的数据处理。为了分担总控节点的处理负担,系统中适当增加一些负荷节点来分担总控节点的处理任务,总控节点除了记录当前接入用户总数、负荷节点的处理用户数等一些基本操作外,其完全可以将所有权利下放,起到一个合理分配任务的作用。负荷节点由总控节点进行支配,与总控节点充分协作,完成所需功能。而用户通过测试端进行用户接入、释放等自定义测试,也可以通过测试端进行测试用例的整体测试。重点就是要测试总控节点是否合理分配了任务,以及总控节点和负荷节点的协调性是否合理,一致(包括总控节点和负荷节点数据是否同步)。
系统中传送的消息结构体有两类,具体如下所示:
1) typedef struct users
{
char bussy ;//用于标示监听端发送到客户端的数据开始
int userID;//保存用户ID
int PINodeID;//保存负荷节点号
int PINodeP;//用于保存节点端口号
char cmd;//传送的命令
int PINodeUsers;//节点上的用户数目
} USER, *pUSER;
users结构体是主要消息载体,用于用户与总控节点,总控节点与负荷节点的消息传送等操作。
2) typedef struct PINode_inf
{
char flag;//消息开始标标识
int user_num;//节点上当前用户总数
int userID[MAX_ID];//节点上用户信息,节点上每一个用户的ID号
}PINode, *pPINode;
PINode_inf结构体用于负荷节点将自己的信息发送给总控节点。
2.2 层次结构
该软件平台采用分层模块化结构设计,如图2所示系统层次图,该仿真平台系统共分为42个节点,四个层次。其中层次1为三个独立的进程,分别建立三个独立工程。其他节点可以作为独立函数,进行模块化程序设计。通过相应的标号可以看出各个节点的从属关系,模拟无线通信时的负荷分担情况,其中subp节点为公共调用节点,API节点为接口函数节点,层次图中都做了特殊注明。
2.2.1 总控端
总控端主要用于建立网络通信连接,判断测试端请求命令,处理测试端请求命令,合理将任务分配给负荷节点,保证和负荷端以及测试端的通信。具体如下:
1)建立网络通信连接:发起socket连接,相当于服务器,测试端和负荷节点相当于用户端。
2)判断测试端请求命令:用户接入,用户释放,取用户数,取负荷ID。
3)处理测试端请求命令:用户接入,用户释放,取用户数,取负荷ID。
4)用户接入,合理分配给某一个负荷节点,给负荷节点发送请求接入命令。
5)用户释放,查找用户分配的负荷节点,给负荷节点发送请求释放命令。
6)取用户数,查找负荷节点当前处理用户数,直接返回给测试端。
7)取负荷ID,直接发送用户ID给负荷端,等待负荷端返回负荷节点ID值。
如图3所示总控节点流程图,总控端先定义变量用于套接字初始化和通讯使用,建立套接字,同时绑定端口;之后处于监听状态并等待用户连接,准备接收数据,当收到数据后,判断命令根据不同命令字进行节点信息的获取并做相应处理;最后将处理结果发回用户,当总控段端不再处于监听状态时,关闭套接字,结束进程。
2.2.2 负荷端
负荷端主要用于实现判断总控端请求命令,处理总控端请求命令,记录负荷节点状态。具体如下:
1)判断总控端请求命令:用户接入,用户释放,取用户数,取负荷ID。
2)用户接入,记录用户ID,显示当前状态。
3)用户释放,删除用户ID,显示当前状态。
4)取用户数,返回用户数,显示当前状态。
5)取负荷ID,所有负荷节点都参与查找自己的用户,看是否匹配,有匹配则返回给总控端。
如图4所示负荷节点流程图,负荷端先定义变量用于套接字初始化和通讯使用,初始化显示信息、建立套接字,同时绑定端口;之后处于监听状态并等待总控节点连接,等待接收数据,收到数据后,判断接收数据中命令字符做相应处理(如:介入用户、释放用户、发送节点信息回总控节点等);最后刷新显示,并且继续等待总控节点的连接,如果失去连接则关闭套接字,结束进程。
2.2.3 测试端
测试端主要功能是提供如下表1四个API函数,用于实现用户接入、用户释放、取用户当前接入负荷节点ID号、取某负荷节点当前处理用户数。并通过单步调试和测试用例联调等方式进行系统测试,同时提供用户的使用说明。
3 通信协议
本设计采用了自行规定的协议,如图5通信协议,测试端的用户请求接入总控端,总控端检验是否第一次接入,并发送指令给负荷端询问状态,如果负荷端准备完毕,用户接入,如果准备未完成,则返回状态字。用户请求接入后,测试端继续发送用户名,总控端记录重要信息,合理分配命令给负荷端,负荷端接到命令立即处理,处理完毕后返回状态字,这时总控端可以继续通信和处理命令,生成相应的子线程,处理失败则返回状态字。测试端用户请求通信,总控端询问负荷端是否可以通信,可以通信则等待用户通信内容,通信失败则返回状态字。确定可以通信后,这时测试端开始发送通信内容,总控端接收到内容后进行处理,并分配内容给负荷端进行处理。内容传送完毕,测试端开始请求用户释放,总控端校验记录中是否存在当前用户,并询问负荷端状态,负荷端准备完毕,用户可以释放,关闭子线程,没准备好则返回状态字。
4 测试结果
本设计在windows操作系统下,应用VC6.0进行开发,根据被测系统的特点采用键盘输入进行相应测试,输入不同命令调用不同函数进行系统测试,如图6所示测试结果,输入小写字母c、 回车,根据提示输入用户ID号、回车,可以接入用户,如果用户ID已经存在将接入失败并提示,同时也可以直接输入c 空格、用户ID 按回车键。系统将会根据分配原则随机将用户接入相应负荷节点。与接入用户命令同样的方法输入小写r可以释放用户。如果用户不存在将提示。同理输入小写g可以获取用户所在的负荷节点号,如果输入的用户不存在将终止测试程序。输入大写G并输入负荷节点号可以获取到该节点上的用户数目,节点号不存在则将终止测试程序。如果输入小写q或大写Q将结束测试程序。
5 结论
本文以socket技术为载体,设计了一套考虑负荷分担的通信系统,实现无线通信时的负荷分担情况。仿真实验结果能够达到预期的目标,运行时基本无错误,对于连续用户接入和信息处理等响应及时,同时该系统的可靠性、可维护性和可测试性比较良好。对于无线通信网中负荷分担的使用具有借鉴意义。
参考文献:
[1] 王峰.无线通信系统可靠性技术研究[J].技术应用,2011,9,20.
[2] 张红. NO.7信令网的负荷分担研究[D].华中科技大学学位论文,2005,10.
[3] 王远洋,周渊平,郭焕丽. Linux下基于socket多线程并发通信的实现[J].微计算机信息,2009,25,5-3.
[4] 周丽娟.基于 UDP 协议的 Socket 网络编程[J].电脑知识与技术,2008,12:1867-1868.
前 言
Java语言是一种使用频率比较高的语言,它有着比较强的移植性和安全性,在软件开发中得到了广泛的应用,尤其是在网络通信的程序中,主要原因是Java语言编制软件与操作系统不关联,同时Java平台中能支撑网络API函数,Java的API网络接口能支撑网络编程机制,如URL和Socket。借助Java平台编制软件,从而建立起网络通信客户端,进而实现了客户实时聊天和资源共享。
一、Java中网络API
在Java的网络API基础中,想要实现网络通信,Socket是一个最佳选择。网络通信时,当客户需要某项信息时,将需求指令输入到Socket里,Socket会自动搜索关键字,从而找到客户想要的信息,最后将答案输送到客户端,这些信息的来源是程序员输入到Socket中的,从而实现简单的网络通信。Socket的本质意义是“套嵌字”,实际上是一个网络端口,当Java编制的程序处于开放网络环境下能够完成简单的对话,这一原理是通过Socket在网络中发出请求或者应答指令,并将请求与最佳的应答进行组合,从而实现网络通信。Socket在通信协议上与计算机网络协议差别不大,都是参照TCP/IP的协议标准,故而Socket在真正意义上的通信过程中,它的客户端以及IP地址是唯一的。
1.Socket类
Socket类是连接客户端与服务端之间的桥梁,在服务器上能够收集与服务端相关的请求指令。首先建立一个Socket对象,其代码是:Socketsocket=newSocketServerName,编制这一代码是为了获取服务器的IP地址。再获取IP地址连接之后,通信程序会进行调用。同时,采取Lose()方法可以关闭建立的通道。
2.ServerSocket类
ServerSocket是起到监控服务端网络的作用,能够在客户端做出应答指令时反馈给服务端的请求方。首先建立一个ServerSocket对象,其代码是:ServerSocketserver=newServerSocket,编制这一代码是为了监控服务网络中的端口实时情况,从而发现网络中的请求找出最佳应答予以匹配。
3.Java工具
(1)JDk中包括Java编译器、Java解释器以及Appletviewer。
(2)VisualJ++是将可视化界面设计、交互式调试、代码编辑以及联机帮助信息介绍从而迅速掌握网络开发环境中的使用向导等功能,同时还具备Active以及COM先进技术的优势。
二、Java中网络通信机制
Java中网络通信机制实现的原理是在Socket的通信方式基础上进行升级完成通信的,但Socket是在C/S方式的通信基础上完成通信的,意味着客户/服务器方式。Java中网络通信机制在进行通信活动时,主要是通过Socket来完成的,Socket实现通信连接的流程都是一样的,主要有以下几个步骤:
1.建立一个Socket对象,同时还要做好监控Socket服务端口的准备。
2.从服务网络中的请求指令获取客户网络中的IP地址以及端口号,从而完成Socket与客户端的通信连接。
3.服务器的监控端要对网络中的通信回话进行监控,当收到客户端传输过来的请求指令时,借助对方的端口号以及IP地址实现通信连接,在通信连接之后,服务器的监控端口呈现关闭状态。
4.Socket在建立对象时要同时创建输出与输入流。
5.Socket是参照TCP/IP网络协议完成客户端的请求指令或应答指令的,同时将对应的数据输送到客户端处。
6.一旦客户端完成通信连接,客户端的通信流以及Socket都是关闭状态。
三、客户端/服务器通信的实现
1.Application与Applet的通信两端是借助Socket通信机制实现通信连接的
(1)客户端程序的实现
首先,打开Socket,建立一个套嵌字,即建立一个网络端口;其次,为Socket建立一个输出流以及输入流;再次,通过网络协议中从Socket处发出的请求或者应答做出指令;最后,清除Socket的输出流以及输入流。
(2)服务端程序的实现
首先,新建一个,其中包括服务器类型的套嵌字以及普通类型的套嵌字,针对服务器类型的套嵌字是在特定的服务端口专门为客户端口的请求指令Socket工作的;其次,再采取ServerSocket类中的Accept()方法实现服务器类型的套嵌字呈现监控状态,此外,将监控的实时情况反馈给普通类型的套嵌字。同时,为该套嵌字建立一个输出流以及输入流;再次,根据输出流以及输入流中的读入以及写入的字节流,做出相应的指令,最终将指令结果传输给客户端;当客户端以及服务器工作完毕后,所有的Socket以及ServerSocket对象都将关闭,例如,服务器类型的套嵌字、普通类型的套嵌字、输出流以及输入流。
由于Java中网络通信机制实现的原理是在Socket的通信方式基础上进行升级完成通信的,而Socket通信机制具有灵活多变的特点。因此,Java应用程序能够无限制的访问网络上的对象,如同浏览本地文件一般。
2.Applet之间的通信
Application与Applet实现通信原理是采用AppletContext类的getApplet()方法实现通信的。在程序中加入Appletoneapplet=getAppletContext().getApplet(“first”);便可引用name为first的Applet中的方法了。
在Application与Applet实现网络通信这一过程中,由于服务端通信的Applet同时包含接受信息的方法与发送信息的方法,因此所有客户端的Applet都要使用通信Applet里的方法,故而客户端中的Applet需要与负责通信的Applet进行通信连接。
四、结束语
综上所述,通过Java平台编制一个软件,基于Java网络API程序上,利用SOCKET网络通信机制,建立一个客户端,实现简单的网络通信。本文就Java实现网络通信的原理和通信设计流程做了简要分析,总结出Java具有较强的理论依据,并且通过数据分析可以看出,Java能够在网络通信中较好的实现资源共享,并且程序相对简单,易于使用,为日后研究Java语言夯实了牢固的基础。
关键词:套接口,网络编程,TCP/IP协议
1 引言
网络应用程序是通过网络使用通信协议实现进程间的通信,TCP/IP就是网络上常用的协议之一,在进行网络应用程序设计时,TCP/IP协议的核心内容被封装在操作系统中。网络应用程序要使用TCP/IP协议来实现自己的功能,只能通过由系统提供给用户的TCP/IP协议编程接口来实现[1]。因此,可以说设计网络应用程序就是利用网络编程接口(API)进行程序设计的过程。在Windows环境下的网络应用程序编程接口叫Windows Sockets,即套接口。
2 套接口
2.1 套接口的概念
WindowsSockets API 依靠套接口Socket进行通信。套接口可看成是两个网络应用程序进行通信时,各自通信连接中的一个端点。通信时,其中的一个网路应用程序将要传输的一段信息写入它所在主机的Socket中,该Socket通过网络接口卡(NIC)的传输介质将这段信息发送到另一台主机的Socket中,使这段信息能传送到其它程序中,如图1所示。[1]
当主机A上的网络应用程序A要发送数据时,通过调用数据发送函数首先将要发送的一段信息写入Socket中,Socket中的内容通过主机A的网络管理软件由主机A的网络接口卡发送到主机B,主机B的网络接口卡接收到这段信息后,再传给主机B的网络管理软件,网络管理软件将这段信息保存在主机B的Socket中,然后程序B才能在Socket中读取并使用这段信息。由此看来,Socket的本质是通信过程中所要使用的一些缓冲区及一些相关的数据结构。
2.2 套接口的分类
为了满足不同的通信程序对通信质量和性能的要求,一般的网络系统提供了三种不同类型的套接口,以供用户在设计网络应用程序时根据不同的要求来选择。免费论文。这三种套接口分别是:
(1)流式套接口(SOCK_STREAM),它提供了一种可靠的、面向连接的双向数据传输服务,实现数据无差错、无重复地发送。流失套接口内设流量控制,被传输的数据看作是无记录边界的字节流。在TCP/IP协议族中,使用TCP协议来实现字节流的传输,当用户想要发送大批量的数据或者对数据的传输有较高的要求时,使用流式套接口。
(2)数据报套接口(SOCK_DGRAM),它提供了一种无连接、不可靠的双向数据传输服务。免费论文。数据包以独立的包形式被发送,并保留了记录边界,不提供可靠性保证。数据在传输过程中可能会丢失或重复,并且不能保证在接收端数据按发送顺序接收。免费论文。在TCP/IP协议族中,使用UDP协议来实现数据报套接口。在同一台计算机上或负载较轻的LAN上,因为出现差错的可能性较小,所以可以使用数据报套接口进行数据传输,这样通信的质量可以得到保证,并且通信的效率较高。
(3)原始套接口(SOCK_RAW),该套接口允许对较低层协议(如IP或ICMP)进行直接访问,常用于检验新的网络协议实现,也可用于测试新配置或安装的网络设备。
3 Socket编程基础
3.1 Socket工作模式
Socket以客户/服务器模式工作。Socket服务器程序通常事先已经启动,并在一个众所周知的端口监听对服务的请求。Socket客户应用程序需要某种服务时,便向提供服务的Socket服务器发出请求,服务器接收到请求后,就响应客户提出的请求[2]。
3.2 基本套接字的系统调用
应用程序常用的函数列举如下,
WSAStarup():检查系统中是否有WindowsSockets的实现库
Socket():创建一个Socket
Bind():为创建的Socket指定通信对象
Connect():请求连接
Listen():等待连接状态
Accept():接收连接请求
Send()/Recv():发送接收数据(TCP)
Sendto()/Recvfrom():发送接收数据(UDP)
CloseSocket():关闭指定的Socket
WSACleanup():释放为应用程序或DLL分配的任何资源
3.3用于无连接协议(如UDP)的Socket系统调用流程框图
3.4 用于面向连接协议(如TCP)的Socket系统调用流程框图
4 编程实例
本实例使用了Visual C++6.0下的控制台程序,该程序中的通信协议使用的是面向连接的TCP协议(SOCK_STREAM)[3]。服务器端的IP地址使用系统指定的IP地址,端口号在程序中指定为6000。
4.1 服务器端的实现
建立一个空的基于控制台的应用程序 TCPserver,引入头文件:WINSOCK2.H,在Project—>settings—>ink—>object/libarymodules 下引入:ws2_32.lib库
实现代码如下,
#include <WINSOCK2.H>#include <stdio.h>
int main(){WORD wVersionRequested;WSADATA wsaData;int err;wVersionRequested=MAKEWORD(1,1);err=WSAStartup(wVersionRequested,&wsaData);if (err!=0){return 0;}if (LOBYTE(wsaData.wVersion)!=1||HIBYTE(wsaData.wVersion)!=1){WSACleanup();return 0;}
//检查系统中是否有Windows Sockets的实现库,如果没有则释放为应用程序或DLL分配的任何资源
SOCKETsockSrv=socket(AF_INET,SOCK_STREAM,0);
//创建套接口SOCKADDR_IN addrSrv;addrSrv.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
//htonl()函数把一个四字节主机字节顺序的数转换为网络字节顺序的数addrSrv.sin_family=AF_INET;addrSrv.sin_port=htons(6000);
//htons()函数把一个双字节主机字节顺序的数转换为网络字节顺序数
bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR));
//把套接口与指定的主机ip地址和端口号联系起来listen(sockSrv,5);
//进入监听状态,最多同时可以接受5个连接请求SOCKADDR_IN addrClient;int len=sizeof(SOCKADDR);
while (TRUE){SOCKET sockTemp=accept(sockSrv,(SOCKADDR*)&addrClient,&len);
//创建一个临时套接口,与其中一个客户端建立连接
char sendbuff[1024];sprintf(sendbuff,'Welcome to zhangjie');
//将字符串格式化到buff中去send(sockTemp,sendbuff,strlen(sendbuff)+1,0);
//向客户端发送数据
char recvBuff[1024];recv(sockTemp,recvBuff,strlen(recvBuff)+1,0);
//接收客户端发送的数据printf('%s ',recvBuff);
closesocket(sockTemp);
//关闭套接口
}return 0;}
4.2 客户端的实现
建立一个空的基于控制台的应用程序TCPClient,引入头文件:WINSOCK2.H,在Project—>settings—>ink—>object/libarymodules 下引入:ws2_32.lib库
实现代码如下,
#include <WINSOCK2.H>#include <stdio.h>
int main(){WORD wVersionRequested;WSADATA wsaData;int err;wVersionRequested=MAKEWORD(1,1);err=WSAStartup(wVersionRequested,&wsaData);if (err!=0){return 0;}if (LOBYTE(wsaData.wVersion)!=1||HIBYTE(wsaData.wVersion)!=1){WSACleanup();return 0;}
//检查系统中是否有Windows Sockets的实现库,如果没有则释放为应用程序或DLL分配的任何资源
SOCKETsockClient=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN addrClient;addrClient.sin_addr.S_un.S_addr=inet_addr('127.0.0.1');
//点分十进制转化成ULong类型addrClient.sin_family=AF_INET;addrClient.sin_port=htons(6000);
//转化成网络字节序connect(sockClient,(SOCKADDR *)&addrClient,sizeof(SOCKADDR));
//请求与服务器建立连接char recvBuff[1024];
recv(sockClient,recvBuff,1024,0);printf('%s ',recvBuff);
send(sockClient,'I am zhangjie',strlen('I am zhangjie ')+1,0);
closesocket(sockClient);WSACleanup();
//通信结束后释放资源return 0;}
5 小结
本文介绍了套接口的基本概念和实现套接口编程的基础,并给出了一个在VC平台下实现的基于TCP的客户/服务器通信实例,体现了套接口编程的灵活性。
6 参考文献
[1]任泰明.TCP/IP协议与网络编程[M].第1版.西安:西安电子科技大学出版社.2004;94-139
[2]邹月,陈建兵.Socket的网络编程研究与实现[J].电脑编程技巧与维护.2008.10-12
[3]谭桂华,魏亮.Visual C#高级编程范例[M].第1版.北京:清华大学出版社.2004;202-235
关键词:Socket TCP协议 服务器 客户端 线程
一、引言
随着信息技术的高速发展,网上交谈已成为人们学习和工作的重要手段,那么如何设计合理的、人性化的网上交谈工具就成为当务之急。使用Visual c#.NET中的Socket编程技术,以及多线程的知识可快速、方便地设计理想的网上交谈工具。一个Socket通信无论功能多么齐全、程序多么复杂,其基本结构均应包括以下步骤:创建Socket对象,建立连接;打开连接到Socket的输入/输出流;对Socket进行读写操作;关闭Socket套接字,关闭流。
二、TCP协议通信的流程
TCP协议是面向连接的协议,它的实现需要数据发送方和数据接收方建立数据通信连接,它的具体流程如下:
1.服务器端首先创建服务器套接字
2.服务器套接字监听一个端口,等待客户端的请求
3.客户端创建一个客户端套接字
4.客户端向服务器发送连接请求
5.服务器确认与客户端的连接
6.客户端和服务器利用建立的连接进行通信
7.通信完毕后,客户端和服务器关闭各自的连接
三、Socket编程简介
1.利用Socket建立服务器程序
要用Socket建立一个TCP服务器程序,一般需要以下几个步骤:
(1)创建一个服务器套接字,用IP地址和端口初始化服务器
(2)监听服务器端口
(3)确认与客户端的连接
(4)处理客户端的请求并回应客户端
(5)断开客户端的连接,释放客户端的连接
(6)关闭服务器,释放服务器连接
2.利用Socket建立客户端程序
要用Socket建立一个TCP客户端程序,一般需要以下几个步骤:
(1)创建客户端套接字
(2)连接服务器
(3)得到与服务器通信的流通道
(4)向服务器发送数据
(5)接收从服务器发回的数据
(6)断开连接
四、设计方案
1.服务器端的设计
服务器端管理着交谈任务,它维持着一张当前在线用户的列表,转发用户发送来的信息,主要功能如下:
监听本机IP地址中的一个指定的端口。
当有客户端向该端口发出请求时,服务器程序立即建立一个与该客户端的连接并启动一个新的线程来处理该客户端的所有请求。
根据客户端发送来的各种不同的请求,执行相应的操作,并将处理结果返回给该客户端。服务器能识别4种请求命令:CONN(建立新的连接)、CHAT(聊天)、PRIV(私聊)和EXIT(离开),服务器接收ASCII字符信息,用“|”分隔信息的各个部分,一条信息包含一条命令,一个或多个信息参数。
2.客户端的设计
客户端应用程序包含用户登录窗口和用户交谈的主窗口,它允许用户登录到服务器,可以向服务器发送信息,同时可以接收从服务器返回的信息,设计的主要功能如下:
向远程服务器发送连接请求。
得到服务器程序的确认后,建立与服务器的连接,并获得与服务器交互的流通道(NetworkStream)。
通过网络流通道与服务器端的程序进行数据通信。向服务器发送服务器能够识别的以上4种命令请求,同时也接收服务器发回的命令。客户端能够识别的命令有JOIN(通知当前在线用户有新的用户进入聊天室)、LIST(更新当前在线用户)和QUIT(关闭客户端程序)。客户端程序接收ASCII字符信息,用“|”分隔信息的各个部分,一条信息包含一条命令,一个或多个信息参数。 五、解决方案
1.服务器端的实现
服务器端是一个Windows窗体应用程序,命名为ChatServer。当运行服务器程序时,单击服务器窗体上的“启动”按钮后,便启动了服务器进程。为了使用Socket对象和Thread对象,在代码文件中加入名字空间System.Net、System.Net..Socket、System..Threading的引用。
在服务器端使用了多线程,每个用户通过一个单独的线程进行连接,当服务器开始运行时,它就启动一个线程等待客户连接(在方法StartListen()中实现)。当接收到一个请求时,服务器立即启动一个新的线程来处理和该客户端的信息交互(在方法ServiceClient()中实现)。自定义了一个Client类,它用于保存每个当前在线用户的用户名和与服务器连接的Socket对象。当Socket连接一旦建立,就马上将其保存在一个Client对象中,以便让每个用户有自己的Socket,以后可以对不同用户的Socket对象进行操作,实现与客户端的数据交换。
在服务器窗口中,单击“启动”按钮,进入btnStart_Click处理程序。在该处理程序中,创建了一个服务器套接字并且监听本机IP地址中的一个指定的端口,同时启动一个线程等待用户连接(在方法StartListen()中实现)。StartListen()方法是在新的进程中进行的操作,它主要用于当接收到一个客户端请求时,确认与客户端的连接,并且立即启动一个新的线程来处理和该客户端的信息交互(在方法ServiceClient()中实现)。ServiceClient()方法用于和客户端进行数据通信,包括接收客户端的请求,根据不同的请求命令,执行相应的操作,并将处理结果返回到客户端,此方法完成了服务器的主要的工作。SendToClient()方法实现了向客户端发送命令请求的功能,它利用不同用户保存的Socket对象,向对应的用户发送命令请求。GetUserList()方法实现了获取当前在线用户列表的功能,它通过对clients数组的遍历,获取当前在线用户的用户名,用字符串发回。
2.客户端的实现
客户端是一个Windows窗体应用程序,命名为ChatClient。当运行客户端程序时,首先进入登录界面,输入用户名,单击“登录”按钮后,进入交谈主窗口(命名为Login),在主窗口中可以向服务器发送信息,同时可以接收从服务器返回的信息。
一、实现目标
本聊天系统的设计采用了Client/Server模型,服务器端的一个服务地址用来监听对服务的请求,当客户端向该地址提出连接请求时,服务器端会对它的请求做出相应的反应。在本设计中,主要是实现服务器端跟客户端的简单字符交换,当实现连接后,客户端就能向服务器端发送和接收字符。
二、系统设计
1.模型及分析。本系统设计是基于TCP的。TCP是面向连接的可靠的传输协议,利用TCP协议进行通信,首先要经过三步握手,以建立通信双方的连接。一旦连接建立好,就可以进行通信。TCP提供了数据的确认和数据的重传机制,保证了发送的数据一定能够到达通信的对方。
2.C/S模型流程。在C/S模型的网络中,服务器是网络的核心,客户机是网络的基础,客户机依靠服务器获得所需要的网络资源,服务器为客户机提供网络所必须的资源。
3.系统设计及分析。在TCP/IP的网络应用中,通信的两个进程相互作用的主要模式是C/S模式,在Internet上的聊天系统是服务器提供服务端的连接响应,使用者通过客户端程序登录到服务器后,即可与登录在同一服务器上的用户交谈,这是一个面向连接的通信过程。程序要在TCP/IP环境下实现服务器端和客户端的两部分程序。在VC中进行WINSOCK的API编程开发的时候,需要在项目中使用下面的三个文件,否则会出现编译错误:(1)WINSOCK.H:这是WINSOCK API的头文件,需要包含在项目中。(2)WSOCK32.LIB:WINSOCK API连接库文件。在使用中,一定要把它作为项目的非缺省的连接库包含到项目文件中去。(3)WINSOCK.DLL:WINSOCK的动态连接库,位于WINDOWS的安装目录下。
(1)服务器端socket的操作。第一,初始化阶段调用WSAStartup()。首先定义一个WORD类型的变量,wVersionRequested,用来保存Winsock库的版本号,接着调用MAKEWORD宏创建一个包含了请求版本号的WORD值,之后调用WSAStartup函数加载套接字库。接下来判断wsaData.wVersion的低字节和高字节是否都等于1,如果不是我们所请求的版本,调用WSACleanup函数,终止对Winsock库的使用并返回。第二,创建套接字(Socket)。初始化WinSock的动态连接库后,需要在服务器端定义一个SOCKET类型的变量,用来接收socket函数返回的套接字。对socket函数来说,第一个参数只能是AF_INET;基于TCP协议的网络程序,需要创建的是流式套接字,将socket函数的第二个参数设置为SOCK_STREAM;将其第三个参数指定为0,该函数可根据地址格式和套接字类别自动选择一个合适的协议。第三,将套接字绑定到一个本地的地址和端口(bind)。为服务器端定义的该监听的Socket指定一个本地的地址及端口,这样客户端才知道要连接哪一个地址的哪个端口,为此要调用bind()函数。在绑定之前,定义一个SOCKET_IN类型的变量,然后对该地址结构变量的成员进行赋值。第四,将套接字设为监听模式(listen),准备接收客户请求。当服务器端的Socket对象绑定完成之后,服务器端必须建立一个监听的队列来接收客户端的连接请求。Listen()函数使服务器端的Socket 进入监听状态,第一个参数是将要设置的套接字,第二个参数是可以建立的最大连接数(目前最大值限制为 5,最小值为1)。第五,等待客户请求到来,接受连接请求,返回一个新的对应于此次连接的套接字(accept)。调用accept函数等待并接受客户的连接请求。此时,也需要定义一个地址结构体SOCKETADDR_IN的变量,用来接收客户端的地址信息。作为服务器端,它需要不断的等到客户端的连接请求,所以程序进入一个死循环,让服务器端程序能够不断运行。第六,与客户端进行通信(send/recv)。可以利用recv函数接收客户端发送的数据,调用send函数向客户端发送数据。第七,关闭套接字。服务器或客户机的任一端启动,调用closesocket()就可关闭通信连接,而要关闭Server端监听状态的socket,同样也是利用此函数。另外,与程序启动时调用WSAStartup()相对应,程式结束前,需要调用WSACleanup()来通知Winsock Dll释放Socket所占用的资源。
(2)客户端Socket的操作。第一,创建客户端的Socket。客户端应用程序首先调用WSAStartup()函数来与Winsock的动态连接库建立关系,然后调用socket()来建立一个TCP socket。socket函数第一个参数只能是AF_INET,socket函数的第二个参数设置位SOCK_STREAM,其第三个参数指定为0。第二,向服务器提出连接申请。对客户端来说,它不需要绑定,可以直接连接服务器端。调用connect()函数来提出与服务器端建立连接的申请,首先定义一个地址结构体变量,并对其成员进行赋值,设定服务器端的IP地址和端口。本程序中服务器端和客户端都是本地的,所以可以使用一个特殊的IP地址:127.0.0.1,这是本地的回路地址。第三,和服务器端进行通信。当建立连接后,就可以调用send函数向服务器端发送数据了,之后调用recv 函数接收服务器发送的数据。第四,关闭套接字。当完成通信后,调用closesocket函数关闭套接字,释放为此套接字分配的资源。最后调用WSACleanup函数,终止对套接字库的使用。
参考文献
[1]孙鑫,余安萍.VC++深入祥解[M].北京:电子工业出版社,2006:223
[2]谢希仁.计算机网络[M].大连:大连理工大学出版社,2004:79~80
引言
Linux是一种很受欢迎的操作系统,与UNIX系统兼容,开放源代码。它原本被设计为桌面系统,现在广泛应用于嵌入式设备。uCLinux正是在这种氛围下产生的。在uCLinux这个英文单词中,u表示Micro,是“小”的意思;C表示Control,是“控制”的意思,所以uCLinux就是Micro-Control-Linux,字面上的理解就是“针对微控制领域而设计的Linux系统”。它也是针对无MMU(内存管理单元模块)的微处理器设计的操作系统。S3C4510B就是属于该类的微处理器。
Samsung公司的S3C4510B是基于以太网应用系统高性价比16/32位RISC微控制器,内含一个由ARM公司设计16/32位ARM7TDMI RISC处理器核。ARM7TDMI为低功耗、高性能的16/32核,最适合用于对价格及功耗敏感的应用场合。除了ARM7TDMI核以外,S3C4510B还有许多重要的片内外围功能模块,其中就有1个以太网控制器,用于S3C4510B系统与其它设备的网络通信工程。在S3C4510B的网络控制平台上移植了uCLinux操作系统,并在这个嵌入式平台上实现网络控制的各项功能。本文的叙述的网络通信工程就是其中最主要的功能。
1 基于S3C4510B以太网电路的设计思路与实现
作为一款优秀的网络控制器,基于S3C4510B的系统若没有以太网接口,其应用价值就会大打折扣,因此,就整个系统而言,以太网接口电路应是必不可少的,但同时也是相对较复杂的。从硬件的角度看,以太网接口电路主要由MAC控制器和物理层接口(Physical Layer,PHY)两大部分构成。
S3C4510B内嵌一个以太网控制器,支持媒体独立接口(Media Independent Interface,MII)和带缓冲DMA接口(Buffered DMA Interface,BDI),可在半双工或全双工模式下提供情报0M/100Mbps的以太网接入。在半双工模式下,控制器支持CSMA/CD协议,在全双工模式下支持IEEE802.3MAC控制层协议。因此,S3C4510B内部实际上已包含了以太网MAC控制,但并未提供物理层接口,故需外接一片物理层芯片,以提供以太网的接入通道。
常用的单口10M/100Mbps高速以太网物理层接口器件主要有RTL8201、DM9161等,均提供MII接口和传统7线制网络接口,可方便地与S3C4510B接口。以太网物理层接口器件主要功能一般包括:物理编码子层、物理媒体附件、双绞线物理媒体子层、10BASE-TX编码/解码器和双绞线媒体访问单元等。
在该设计中,使用DP9161作为以太网的物理层接口。DM9161是一款低功耗、高性能的CMOS芯片,支持10M和100M的以太网传输,它起编码、译码输入和输出数据的作用。它与S3C4510B的引脚连线如图图1所示。
由于S3C4510B片内已民用有带MII接口的MAC控制器,而DM9161也提供了MII接口,各种信号的定义也很明确,因此DM9161与S3C4510B的连接时序衔接,可以达到很好的网络信号传递的目的。图2为DM9161在本系统中的实际应用电路(图中右下方的1、2、3以及14、15、16分别与网络隔离变压器相应引脚相连)。
S3C4510B的MAC控制器可通过MDC/MDIO管理接口控制多达斡尔1个DM9161,每个DM9161应有不同的PHY地址(可从00001B~11111B)。当系统复位时,DM9161锁存引脚9、10、12、13、15的初始状态作为与S3C4510B管理接口通信工程的PHY地址;但该地址不能设为00000B,否则DM9161进入掉电模式。
信号的发送和接收端应通过网络隔离变压器和RJ45接口接入传输媒体,实际应用电路如图书室所示。
图2
2 Linux下的网络编程协议分析
Linux下的TCP/IP网络协议栈的各层之间是通过一系列互相连接层的软件来实现Internet地址族的,结构层次如图4所示。
其中BSD socket层由专门用来处理BSD socket的通用套接字管理软件来处理,它由INET socket层来支持。INET socket为基于IP的协议TCP和UDP管理传输端点。UDP(用户数据报协议)是一个无连接协议,而TCP(传输控制协议)是一个可靠的端对端协议。传输UDP包的时候,Linux不知道也不关心它们是否安全到达了目的地。TCP则不同。在TCP连接的两端都需要加上一个编号,以保证传输的数据被正确接收。在IP层,实现了Internet协议代码,这些代码要给传输的数据加上一个IP头,并且知道如何把传入的IP包送给TCP或者UDP协议。在IP层以下,就是网络设备来支持所有的Linux网络工作,如PLIP、SLIP和以太网。
3 uClinux环境下的socket编程
网络的socket数据b传输是一种特殊的I/O,socket也是一种文件描述符,也具有一个类似文件的函数调用socket()。该函数返回一个整型的socket描述符,随后的连接建立、数据传输等操作都是通过该socket函数实现的。常用的socket类型有两种:流式socket和数据报式socket。两者的区别在于:前者对应于TCP服务,后者对应于UDP服务。
3.1 uCLinux中socket编程中用到的函数
(1) socket函数
为了执行I/O,一个进程必须做的第一件事情就是调用socket函数,指定期望的通信协议类型(使用IPv4的TCP、使用IPv6的UDP、Unix域字节流协议等),其函数结构如下:int socket(int family,int type,int protocol);
/*返回:非负描述字—成功,-1—出错*/
代码中的family指明协议族。套接口的类型type是某个常值。一般来说,函数socket的参数protocol主设置为0,socket函数成功时返回一个小的非负整数值。为了得到这个数值,我们指定协议族(IPv4IP、v6或Unix)和套接口类型(字节流、数据报或原始套接口)。
(2)connect函数
TCP客户用connect函数来建立一个与TCP服务器的连接。
Int connect(int sockfd,const struct sockaddr* servaddr,socklen_t addrlen);/*返回:0—成功,-1—出错*/
Sockfd由socket函数返回数值,第二、第三个参数分别是一个批晌套接口地址结构的指针和该结构的大小。套接口叶址结构必须含有服务器的IP地址和端口号。
(3)bind函数
函数bind给套接口分配一个本地协议地址。对于网际协议,协议地址是非颠倒2位IPv4地址16位的TCP或UDP端口号的组合。
Int bind(int sockfd,const struct sockaddr* myaddr,socklen_t addrlen);/*返回:0—成功,-1—出错*/
第二个参数量个指向特定于协议地址结构的指针,第三个参数是该地址结构的长度。对于TCP,调用函数bind可以指定一个端口,指定一个IP地址。可以两者都指定,也可以一个也不指定。
(4)listen函数
函数listen仅被除数TCP服务器调用。它做两件事件事情,当函数socket创建一个套接口时,被假设为一个主动套接口。也就是说,它是一个将调用connect发起连接的客户套接口,函数listen将未连接的套接口转换成被动套接口,指示内核应接受指向此套接口的连接请求。根据TCP状态转换调用函数listen导致套接口从CLOSED状态转换到LISEN状态。函数的第二个参数规定了内核为此套接口排队的最大连接个数。
Int listen(int sockfd,int backlog);
/*返回:0—成功,-1—出错*/
一般来说,此函数应在调用函数socket和bind之后,调用函数accept之前调用。
(5)accept函数
accept函数由TCP服务器调用,从已完成连接队列头返回下一个已完成连接。若已完成连接队列为空,则进程睡眠。(假定套接口噗缺省的阻塞方式)
int accept(int sockfd,struct sockaddr*cliaddr,socklen_t*addrlen);/*返回非负数值—OK,-1—出错*/
参数cliaddr和addrlen用来返回连接对方进程(客户)的协议地址。Addrlen是结果参数,调用前,将由*addrlen所指示的整数值置为由cliaddr所旨的套接口地址结构的长度,返回时,此整数值即为由内核存在此套接口地址结构内的准确字节数。
3.2 uClinux中网络通信编程的实现
在uCLinux中进行socket编程,一般按照图书资料所示流程编写网络应用程序。
除了熟悉前文提出的函数外,还应知道两个重要的数据结构。因为在计算机中,数据存储有两种字节优先顺序:高位字节优先和低位字节优先。在互联网上,数据是以高位字节优先顺序传输的,所以对于在内部以低位字节优先方式存储的数据,需要进行转换才能在互联网上传输。
*struct sockaddr:用来保存socket信息
struct sockaddr{unsigned short sa_family;/*地址族,AF_xxx*/
char sa_data[14]; /*14字节的协议地址*/};
*struct sockaddr_in;和来进行数据类型的转换
struct sockaddr_in{
short int sin_family; /*地址族*/
unsigned short int sin_port; /*端口号*/
sruct in_addr sin_addr; /*IP地址*/
unsigned cha sin_zero[8]; /*填充0,以保持与struct sockaddr同样大小*/};
至此,可经编出uCLinux的网络通信工程程序。在此给出部分uCLinux下实现网络通信源代码及其Makefile文件的编写实例。
main()函数中部分代码如下:
int sockfd;
unsigned int uiip;
char szsendbuf[1024];
char head[8];
int*phead=head+4,nsize=1024,allsize=0;
struct sockaddr_in servaddr;
sockfd=socket(AF_INET,SOCK_STREAM,0);/*创建socket*/
bzero(&servaddr,sizeof(struct sockaddr_in));
servaddr.sin_family=AF_INET;
servaddr.sin_port=8888;//htons(8888); /*指定通信端口*/将命令行输入的字符串IP转换为connect函数可识别的整数uiip。本来在Linux上开发时可以使用C库函数inet_pton(),但在uCLinux的库中不支持该函数,因此只好自己实现该函数的功能。
aiptoi()如下所示:
aiptoi(argv[1],&uiip);
servaddr.sin_addr.s_addr=uiip; /*指定连接的对端IP*/
connect(sockfd,(struct sockaddr)&servaddr,sizeof(struct sockaddr));
/*连接对端接收代码*/
fp=fopen("kongzhi.htm","r"); /*打开控制页面*/
while(nsize==1024)
{bzero(szsendbuf,1024); /*每次从文件中读取巧024个字节发送出去,若读出少于1024字节结束*/
nsize=phead=fread(szsendbuf,1,1024,fp);/*从文件中读取并填入发送BUFFER中*/
write(sockfd,head,8);/*发送协议头*/
nsize=write(sockfd,szsendbuf,nsize);/*发送*/}
fclose(fp);
uCLinux中的Makefile需做的修改如下:
CC=gcc
COFF2FLAT=/uclinux/coff2flt-0.3/coff2flt
CFLAGS=-I/uclinux/uC-libc-pic/include
LDFLAGS=/uclinux/uC-libc-pic/libc.a
ethernet:Ethernet.o
$(CC)-o $@.coff ethernet.c $(CFLAGS)$(LDFLAGS)
$(COFF2FLAT)-o Ethernet ethernet.coff
cp Ethernet /Ethernet
clean:
rm -f Ethernet Ethernet.o
需要注意的是:①uCLinux中不带有pthread库,在编写网络程序要切记;②在uCLinux环境下,处理器(硬件)和内核黄素(软件)均不提供内存管理机制,所以程序的地址空间等同于内存的物理地址空间。在程序中可直接对I/O地址进行操作,而不需要申请和释放I/O空间,但需要用户自己来检查所操作的I/O地址的占用情况。
关键词:SocketTCP协议服务器客户端线程
一、引言
随着信息技术的高速发展,网上交谈已成为人们学习和工作的重要手段,那么如何设计合理的、人性化的网上交谈工具就成为当务之急。使用Visualc#.NET中的Socket编程技术,以及多线程的知识可快速、方便地设计理想的网上交谈工具。一个Socket通信无论功能多么齐全、程序多么复杂,其基本结构均应包括以下步骤:创建Socket对象,建立连接;打开连接到Socket的输入/输出流;对Socket进行读写操作;关闭Socket套接字,关闭流。
二、TCP协议通信的流程
TCP协议是面向连接的协议,它的实现需要数据发送方和数据接收方建立数据通信连接,它的具体流程如下:
1.服务器端首先创建服务器套接字
2.服务器套接字监听一个端口,等待客户端的请求
3.客户端创建一个客户端套接字
4.客户端向服务器发送连接请求
5.服务器确认与客户端的连接
6.客户端和服务器利用建立的连接进行通信
7.通信完毕后,客户端和服务器关闭各自的连接
三、Socket编程简介
1.利用Socket建立服务器程序
要用Socket建立一个TCP服务器程序,一般需要以下几个步骤:
(1)创建一个服务器套接字,用IP地址和端口初始化服务器
(2)监听服务器端口
(3)确认与客户端的连接
(4)处理客户端的请求并回应客户端
(5)断开客户端的连接,释放客户端的连接
(6)关闭服务器,释放服务器连接
2.利用Socket建立客户端程序
要用Socket建立一个TCP客户端程序,一般需要以下几个步骤:
(1)创建客户端套接字
(2)连接服务器
(3)得到与服务器通信的流通道
(4)向服务器发送数据
(5)接收从服务器发回的数据
(6)断开连接
四、设计方案
1.服务器端的设计
服务器端管理着交谈任务,它维持着一张当前在线用户的列表,转发用户发送来的信息,主要功能如下:
监听本机IP地址中的一个指定的端口。
当有客户端向该端口发出请求时,服务器程序立即建立一个与该客户端的连接并启动一个新的线程来处理该客户端的所有请求。
根据客户端发送来的各种不同的请求,执行相应的操作,并将处理结果返回给该客户端。服务器能识别4种请求命令:CONN(建立新的连接)、CHAT(聊天)、PRIV(私聊)和EXIT(离开),服务器接收ASCII字符信息,用“|”分隔信息的各个部分,一条信息包含一条命令,一个或多个信息参数。
2.客户端的设计
客户端应用程序包含用户登录窗口和用户交谈的主窗口,它允许用户登录到服务器,可以向服务器发送信息,同时可以接收从服务器返回的信息,设计的主要功能如下:
向远程服务器发送连接请求。
得到服务器程序的确认后,建立与服务器的连接,并获得与服务器交互的流通道(NetworkStream)。
通过网络流通道与服务器端的程序进行数据通信。向服务器发送服务器能够识别的以上4种命令请求,同时也接收服务器发回的命令。客户端能够识别的命令有JOIN(通知当前在线用户有新的用户进入聊天室)、LIST(更新当前在线用户)和QUIT(关闭客户端程序)。客户端程序接收ASCII字符信息,用“|”分隔信息的各个部分,一条信息包含一条命令,一个或多个信息参数。五、解决方案
1.服务器端的实现
服务器端是一个Windows窗体应用程序,命名为ChatServer。当运行服务器程序时,单击服务器窗体上的“启动”按钮后,便启动了服务器进程。为了使用Socket对象和Thread对象,在代码文件中加入名字空间、..Socket、System..Threading的引用。
在服务器端使用了多线程,每个用户通过一个单独的线程进行连接,当服务器开始运行时,它就启动一个线程等待客户连接(在方法StartListen()中实现)。当接收到一个请求时,服务器立即启动一个新的线程来处理和该客户端的信息交互(在方法ServiceClient()中实现)。自定义了一个Client类,它用于保存每个当前在线用户的用户名和与服务器连接的Socket对象。当Socket连接一旦建立,就马上将其保存在一个Client对象中,以便让每个用户有自己的Socket,以后可以对不同用户的Socket对象进行操作,实现与客户端的数据交换。
在服务器窗口中,单击“启动”按钮,进入btnStart_Click处理程序。在该处理程序中,创建了一个服务器套接字并且监听本机IP地址中的一个指定的端口,同时启动一个线程等待用户连接(在方法StartListen()中实现)。StartListen()方法是在新的进程中进行的操作,它主要用于当接收到一个客户端请求时,确认与客户端的连接,并且立即启动一个新的线程来处理和该客户端的信息交互(在方法ServiceClient()中实现)。ServiceClient()方法用于和客户端进行数据通信,包括接收客户端的请求,根据不同的请求命令,执行相应的操作,并将处理结果返回到客户端,此方法完成了服务器的主要的工作。SendToClient()方法实现了向客户端发送命令请求的功能,它利用不同用户保存的Socket对象,向对应的用户发送命令请求。GetUserList()方法实现了获取当前在线用户列表的功能,它通过对clients数组的遍历,获取当前在线用户的用户名,用字符串发回。
2.客户端的实现
客户端是一个Windows窗体应用程序,命名为ChatClient。当运行客户端程序时,首先进入登录界面,输入用户名,单击“登录”按钮后,进入交谈主窗口(命名为Login),在主窗口中可以向服务器发送信息,同时可以接收从服务器返回的信息。
(1)Login.cs文件的实现
Login.cs文件是登录窗体的代码文件,为了使用Socket对象,在代码文件中加入名字空间、..Socket的引用,它主要实现了创建客户端套接字同时连接到服务器指定端口,把用户名和创建的客户端套接字传递给ChatClient窗体。
(2)ChatClient.cs文件的实现
ChatClient.cs文件是客户端窗体(ChatClient)的代码文件,在该文件中,首先获得与服务器通信的流通道,在用户登录后,向服务器发送CONN命令以此说明有新的用户进入交谈室,服务器将返回所有的当前在线用户的呢称,选择不同的人,就可以与他们交谈了,如果选中“悄悄话”复选框,则具有私人交谈的功能。
当加载ChatClient窗体时,便会进入ChatClientForm_Load处理程序。在该处理程序中,首先显示用户登录窗口。如果登录成功,那么获取与服务器的连接并得到与服务器数据交互的流通道,向服务器发送CONN请求命令,同时启动一个新的线程用于响应从服务器发回的信息(在方法ServerResponse()中实现)。ServerResponse()方法用于和服务器进行数据通信,主要是接收从服务器发回的信息,根据不同的命令,执行相应的操作。当需要进行“私人交谈”时,便进入priCheckBox_CheckedChanged处理程序。在该处理程序中主要对privatemode的布尔属性进行设置。当需要发送信息时,便会进入btnSend_Click处理程序。在该处理程序中,如果privatemode的布尔属性值为false(不是私人交谈),将CHAT命令发送给服务器;否则(为私人交谈),将PRIV命令发送给服务器。当用户需要离开时,便进入了btnExit_Click处理程序。在该处理程序中,将EXIT命令发送给服务器,停止交谈。
六、结束语
使用Socket编程的关键技术可以方便地实现网上交谈工具。利用Socket可以创建服务器程序和客户端程序。服务器监听本机IP地址中的一个指定的端口,建立与客户端的连接并启动一个新的线程来处理客户端的所有请求,执行相应的操作,并将处理结果返回给该客户端。客户端向远程服务器发送连接请求,得到服务器程序的确认后,建立与服务器的连接,并获得与服务器交互的流通道并通过网络流通道与服务器进行数据通信。我们可以进一步进行功能完善,进而编译成组件应用到自己的应用程序中,开发更完美的通信软件。
参考文献:
[1]谭桂华魏亮等:VisualC#高级编程范例.北京:清华大学出版社,2004年5月
关键词:嵌入式;Socket;串口通信;网络通信
本文的背景是研究通过对摄像头的远程控制从而对运动图像加以采集和处理。该系统的音视频编解码部分,是基于Windows系统用VC进行的开发,实现对图像视频的编解码处理,摄像头的远程控制系统主要是通过Linux系统设计。统通过串口和网络进行传输和控制。系统采用C/S模型,PC机作为client端,根据采集到的数据进行编解码后,发送出对摄像头的控制命令,通过网络传输到ARM9平台上,ARM平台作为server端在接收到client端发送的命令后,通过串口发送命令来控制摄像头。
1 串口通信
1.1 Linux下串口通信基本原理
Linux系统将设备和文件的操作都作为对文件的处理,所以对设备的操作,内核会返回一个文件描述符,需要将其作为参数传给相对应的函数。Linux中所有的设备文件都放在“/dev”目录下,通过“ttyS+编号”命名相应的串口资源,所以对应路径是“/devttyS*”。因而可以相对文件读写那样访问一个串口设备。
1.2 Linux下的串口设置
首先要对串口进行设置,在termios.h完成对波特率等的设置。
struct termios
{tcflag t c iflag;
tcflag t c oflag;
tcflag t c cflag;
tcflag t c lflag;
cc t c cc [NCCS];
};
open()函数:int fd = open ("/dev/ttyS *", O RDWR|O NOCTTY|O NDELA Y);
然后调用read()和write()函数读写端口,返回实际读写的字节数,如果有错误发生则返回-1。
读取串口结束后,首先恢复串口原始属性,然后才可以关闭串口并退出程序。
1.3 串口控制应用程序
下面给出本系统中一个通过串口控制摄像头的函数的一部分结构:
void sendMsg(int pan,int tilt)
{struct timeval timeout;
static char *driver = "/dev/ttyS1";
timeout.tv_sec = 0;
timeout.tv_usec = 0;
formCmd(buf,pan,tilt);
fd = uart_open(driver,B9600);
write(fd,buf,16);
close(fd);
}
2 网络通信
2.1 Socket基本原理介绍
Socket是一种通信机制,客户/服务器系统既可以在本地单机上运行,也可以在网络中运行。
首先需要了解socket中的一些重要函数:
1) 创建Socket:int socket( intdomain, inttype, intprotocol);
2) 配置Socket:bind函数指定本地信息int bind( intsocket, const structsockaddr* address, size_taddress_len);
3) 使用listen( )和accept( )函数获取远端信息:
Int listen(int socket, int backlog);
Int accept(int socket, struct sockaddr* address, size_t* address_len);
4) 通信处理:send( ),write( ),recv( ),read( )。
5) 通信结束:使用closesocket( )。
2.2 Windows和Linux下socket应用程序
客户端程序:创建一个未命名的套接字,然后把它连接到服务器套接字server_socket上,向服务器写一个字符,再读回经服务器处理后的一个字符。
服务器端程序:首先创建一个服务器套接字,绑定到一个名字,然后创建一个监听队列,接收来自客户程序的连接。
下面我们分别在Windows和Linux下进行socket编程,实现二者之间的通信,具体函数实现的主要部分如下:
Windows下作为client端:
{
SocketClient=socket(AF_INET,SOCK_STREAM,0);
addrSrv.sin_addr.S_un.S_addr=inet_addr("192.168.1.10");
while(1)
send(SocketClient,sendBuffer,16,0);
closesocket(SocketClient);
WSACleanup();
}
Linux下作为server端:
{
server_sockfd = socket(AF_INET, SOCK_STREAM, 0);
server_address.sin_addr.s_addr = inet_addr("192.168.1.10");
bind(server_sockfd, (struct sockaddr *)&server_address, server_len);
listen(server_sockfd, 5);
client_sockfd = accept(server_sockfd, (struct sockaddr *)&client_address, &client_len);
while(1)
read(client_sockfd, str,16);
close(client_sockfd);
}
3 结束语
本文设计了基于嵌入式平台上可移动摄像头远程控制,对串口通信和网络通信的原理和机制加以阐述,对串口驱动和网络驱动的编写提出了具体的方法,并且在实际的操作应用中给出了具体函数的编写。本文所提供的处理方法已在实际的项目中的得到应用,对于其他的通过串口通信和网络通信控制的设备,具有参考意义,今后我们还将展开进一步的相关研究。
参考文献:
[1] 刘智国,张海春.基于S3C2410的嵌入式串口通信设计[J].微计算机信息(嵌入式与SOC),2009,25(4-2).
关键词:网络控制器;通信协议;Rabbit2000单片机;套接字;串口通信
中图分类号:TN91 文献标识码:B
文章编号:1004373X(2008)0105403オ
Communication Technology of Jia-xiao-tong Network Controller
WEI Zi,YANG Qingjiang,ZHANG Guanglu
(Heilongjiang Institute of Science and Technology,Harbin,150027,China)
オ
Abstract:For the function demand of the network controller in Jia-xiao-tong system,the paper proposes a communication technology of combining serial-port with network.Firstly,it describes the network controller′s hardware platform and the application-layer protocol based on TCP/IP protocol stack.Then,it mainly discusses the communication network technology and the communication serial technology.The polling scheme is applied to improve data transfer effectiveness and reduce data collision.Finally,it explains and analyzes the communication technology′s feasibility and stability through filed test data.
Keywords:network controller;communication protocol;Rabbit2000 single chip;socket;serial communication
オ
1 引 言
随着TCP/IP等网络通信的出现,将串口通信与网络通信相结合的趋势越来越明显,这是保护使用者既往投资和整体利益的一种有效办法。本文所讨论的通信技术就是将串口与网络通信相结合并在实际应用中取得良好效果的一种通信解决方案。
“家校通”是利用现代信息技术实现家庭与学校实时沟通的教育网络平台,其组成部分为:家校互联卡、读卡器、无线网关、网络控制器、短信发射接收机。网络控制器的主要功能是通过无线网关从读卡器获取信息,并与互联网服务平台进行网络通信,是家校通系统的核心。
2 网络控制器的硬件平台
根据网络控制器的功能要求,考虑整体的性能、价格方面,CPU采用Rabbit2000单片机,他是Z-World公司特别为中小型控制器而设计的高性能8位微处理器。编译环境为Dynamic C,提供Socket级TCP/IP编程。网络控制器的硬件结构如图1所示。
3 应用层通信协议
3.1 网络控制器与读卡器的串口通信协议
网络控制器与读卡器的通信主要通过异步串行RS 485接口,基于通用串行通信RS 485数据传输单元(字节格式)有不带校验位的10 b和带奇偶校验位的11 b两种数据格式。为了提高数据传送的效率和可靠性,采用无奇偶校验位的10 b数据格式,并由BBC校验生成校验码与数据一起发送。
开始标志读卡器地址信息长度命令和参数校验
2 B1 B2 B14 B1 B
(3)命令字:
A1:从读卡器读取一条记录。
A2:控制器正确接收记录信息后,返回更新记录的读地址命令,使地址指针指向下一条记录。
A4:设置校正读卡器时间。
A6:测试控制器与读卡是否正常通信。
3.2 网络控制器与服务平台的网络通信协议
网络协议通常分不同的层次进行开发,每一层分别负责不同的通信功能。TCP/IP通常是一个4层协议,包括数据链路层、网络层(含IP协议)、传输层(含TCP协议)和应用层。本网络协议就是基于TCP/IP协议之上的应用层协议,采用请求应答的通信模式。考虑到协议的可靠性和实现的方便性,数据采用ASCII码表示。
(1)报文组成
4 通信技术
控制器建立socket连接后,向服务器发送连接请求和数据传输,并实时判断连接的有效性,在有效连接的状态下,实现控制器、服务器、读卡器三者的正常通讯,程序流程如图2所示。
4.1 网络通信技术的实现
socket初始化: 首先要对RTL8019AS的控制寄存器进行初始化配置,Dynamic C已将这部分配置封装成函数放入REALTEK.LIB库中,应用时只需调用相关函数。之后再调用函数库dcrtcp.lib中的几个简单函数可完成socket的初始化,以下是程序的主体框架。
#define TCPCONFIG1
#memmapxmem
#usedcrtcp.lib
main()
{
sock[CD#*2]init( );
while(ifpending(IF[CD#*2]DEFAULT)==IP[CD#*2]COMING[CD#*2]UP)
tcp[CD#*2]tick(NULL);
tcp[CD#*2]open(&socket,0,destIP,sockport,null);
…… ……
}
在建立TCP的连接中,Dynamic C有两种方法打开TCP socket:一种是被动的方式,调用函数tcp[CD#*2]listen(),等待客户端进行连接;一种是主动的方式,调用函数tcp[CD#*2]open(),根据相应的参数(IP地址和端口号)主动连接到服务器端。本通信技术采用的就是主动方式(作为客户端)。
Socket数据的发送与接收:数据发送调用sock[CD#*2]write(&socket,str[CD#*2]send,str[CD#*2]send[CD#*2]len)。在读取socket缓冲区数时,先判断缓冲区中是否有数据,有数据再进行读操用,同时有超时设置。
start[CD#*2]time=SEC[CD#*2]TIMER;
/*SEC[CD#*2]TIMER为秒级系统时间*/
end[CD#*2]time=start[CD#*2]time;
while((end[CD#*2]time
!recv[CD#*2]count)/*READ[CD#*2]TIME为设定的等待时间*/
{
str[CD#*2]recvbuf[CD#*2]len=sock[CD#*2]bytesready(&socket);
/*判断socket读缓冲区是否有数据*/
if(str[CD#*2]recvbuf[CD#*2]len>0)
recv[CD#*2]count=sock[CD#*2]read(&socket,str[CD#*2]recvbuf,
str[CD#*2]recvbuf[CD#*2]len); /*读socket*/
}
if(revc[CD#*2]count)
{…… ……}
/*拆分接收到的命令字,读取有效信息*/
else
break;
4.2 串口通信技术的实现
主要包含3个步骤:命令字的组合,设置串口D,通过设置Rabbit2000的PE3来控制数据的发送与接收。以A6命令为例,程序主体框架如下:
command[0]=0xA6;/*A6命令*/
create[CD#*2]command(addr,command,pack);
/*组合命令字*/
serDopen(9600);/*9600为波特率*/
send[CD#*2]command(command,returnlen);/*发送数据*/
read[CD#*2]command(readbuf,sizeof(readbuf),10);
/*接收数据*/
void send[CD#*2]command(char *pack,int len)
{
int i;
WrPortI(PEDR,& PEDRShadow,0x08);
WrPortI(PEB3R,NULL,0);
for(i=0;i
serDputc(pack[i]);
}
int read[CD#*2]command(char *buf,int len,int time)
{
int i[CD#*2]return;
WrPortI(PEB3R,NULL,1
i[CD#*2]return=serDread(buf,len,time);
return i[CD#*2]return;
}
5 数据采集与分析
网络控制器联网正常运行时,向服务器发送的一包测试记录数据为:
对数据包进行分析:0244为数据包的总长度,01为协议版本号,02为命令字,000003为序列号,21000102*****为设备号,09为数据包中的刷卡记录总数,0080D122为测试卡号,07070414152715为刷卡时间(顺序为年周月日时分秒),03为状态。
采集从服务器返回的命令:001501020000031,其中
列号,1为正确接收。返回命令格式符合协议要求,内容正确。
网络控制器向读卡器发送的命令(以A6为例): aa ff 01 00 03 a6 41 41 f1,其中aa ff表示本命令为控制器发给读卡器,01为读卡器地址,00 03为数据长度,a6为命令字,41 41为测试数据,f1为校验位。
采集从读卡器返回的命令:bb ff 01 00 02 41 41 47 d1 22 07 07 04 14 15 42 17 1 6b 00 00 00,其中bb ff表示本命令为读卡器发给控制器,01为读卡器地址,00 02为数据长度,41 41为测试数据,47为校验位,之后的为无效数据。
6 结 语
基于Rabbit2000实现串口与网络结合的通信技术可以实时、准确地对数据进行处理、发送与接收。由采集到的数据表明数据收发的正确性,证明了本通信技术的可行性与可靠性。本通信技术已在家校通系统中取得了良好的通信效果,并可应用于其他数据传输系统中,具有广泛的发展前景。
参 考 文 献
[1]Z-World Inc.Rabbit2000 Microprocessor Designer′s Hand-book,2003.
[2]Z-WorldInc.Dynamic C TCP/IP User′s Manual,2003.
[3]林丽,朱宏.基于Rabbit2000的网络通信技术[J].福建电脑,2005(4):45-46.
关键词:实时视频 网络传输 DSPACK Indy
中图分类号:TN919.8 文献标识码:A 文章编号:1007-9416(2016)05-0000-00
1 引言
随着商业银行业务的飞速发展和管理模式的革新变化,为了更好的提高授权工作效率,提升风险防控能力,优化人力资源配置,解决网点转型过程中的一些突出问题,增强客户的精细化管理水平,必须加快业务转型,建立“技术先进、控制严密、因地制宜、稳健高效”的新型系统及管理模式的创新机制。在网点转型、渠道拓展、系统重构的研发和实际应用过程中,现场实时视频是业务办理及审核的一种重要监督方式和审核手段,本文主要探讨实时监控视频的采集和网络传输技术,并介绍基于DSPACK和Indy的快速实现方法。
2 技术分析
2.1 视频采集技术
2.1.1 DirectShow
DirectShow作为目前Windows平台上主流的媒体处理框架,提供了高质量的多媒体流采集和回放功能,大大简化了媒体回放、格式转换和采集工作。运用DirectShow,我们可以很方便地从支持WDM驱动模型的采集卡上捕获数据,并且进行相应的后期处理乃至存储到文件中。
2.1.2 DSPACK
DSPACK是一组封装了MS Direct Show和DirectX技术的组件,用于多媒体应用系统的开发。它不仅功能强大而且完全免费。利用基于DirectShow的DSPACK开发包,不仅可以绕开COM组件编程,而且可以大幅提高开发效率,快速实现目标。
2.2 网络协议分析和选择
2.2.1 Socket
Socket接口是TCP/IP网络的API,常用的 Socket类型有两种:流式Socket(SOCK_STREAM)和数据报式Socket(SOCK_DGRAM)。流式是一种面向连接的 Socket,针对于面向连接的TCP服务应用;数据报式Socket是一种无连接的Socket,对应于无连接的UDP服务应用。Socket实际在计算机中提供了一个通信端口,可以通过这个端口与任何一个具有Socket接口的计算机通信。应用程序在网络上传输,接收的信息都通过这个Socket接口来实现。在应用开发中就像使用文件句柄一样,可以对Socket句柄进行读、写操作。
2.2.2 Indy组件特点分析
Internet Direct(Indy)是一组开放源代码的Internet组件,涵盖了几乎所有流行的Internet协议。使用Indy组件可以开发基于各种协议的TCP客户和服务器应用程序,并处理相关的编码和安全问题。
(1)Indy是阻塞式(Blocking)。通常的Winsock编程取数据或者向Socket写入数据都是异步,而Indy使用了阻塞式Socket调用方式。阻塞式访问更像是文件存取。当你读取数据,或是写入数据时,读取和写入函数将一直等到相应的操作完成后才返回。阻塞式Socket优点,例如编程简单、容易向Unix移植、很好地利用线程技术。(2)Indy使用了线程技术。阻塞式Socket通常都采用线程技术。从最底层开始,Indy的设计都是线程化的。因此用Indy创建服务器和客户程序跟在Unix下十分相似,并且Delphi的快速开发环境和Indy对WinSock的良好封装使得应用程序创建更加容易。(3)Indy服务器模型。一个典型的Unix服务器有一个或多个监听进程,它们不停地监听进入的客户连接请求。对于每一个需要服务的客户,都fork一个新进程来处理该客户的所有事务。Indy服务器工作原理同Unix服务器十分类似,只是Windows不像Unix那样支持fork,而是支持线程,因此Indy服务器为每一个客户连接分配一个线程。Indy服务器组件创建一个同应用程序主线程分离的监听线程来监听客户连接请求,对于接受的每一个客户,均创建一个新的线程来为该客户提供服务,所有与这一客户相关的事务都由该线程来处理。
3 主要实现过程
3.1 服务端的实现方法
首先是视频设备枚举和初始化,需要检测系统当前连接的设备,并把设备枚举出来供程序选择使用。其次是视频的预览,需要根据枚举到的设备列表,选择相应摄像头设备,进行初始化和预览.程序的菜单选择事件。最后是帧采集和帧发送,DSPACK的SampleGrabber对象负责图像帧的中转和处理,我们可以在SampleGrabber的Buffer事件中得到SampleGrabber当前正在处理的帧,并对当前帧进行处理。本文的处理主要是把当前帧通过UDPServer发送到客户端。处理逻辑如下如图1所示。
(1)检测帧回调标志状态,如果不在回调状态,则退出本次循环。(2)检测是否正在发送,如果正在发送,则退出本次循环。(3)检测连续未响应计数器,如果计数器低于阀值,则退出本次循环。(4)获取当前帧,然后转化和压缩,然后递增未响应计数器。(5)通过UDP发送,记录日志,循环结束。
3.2 客户端的实现
客户端主要负责视频的请求和展示,处理逻辑如下如图2所示:
(1)获取对方IP地址和端口参数,然后呼叫对方并发送视频请求命令。(2)UDPServer会检测到报文接收事件,然后判断报文内容是否为内置命令集,如果是操作命令集,则按命令处理并退出循环。如果不是,则需要尝试处理并显示接收帧。(3)检查处理错误,如果没有错误则发送确认命令给客户端。如果存在错误则尝试丢弃数据包并汇报检测报文接收事件状态,等待接收和解析下一包数据。
如果视频查看人员需要保存某帧图像数据,可以通过抓拍功能来实现。本文中抓拍图像保存后和远程授权系统主进程有一个进程间通讯,通过消息通知授权端进程已抓拍图片,授权程序可以到约定的路径下获取抓拍的图片并进行处理和存档。
4 结语
本文介绍了基于远程授权中网络实时视频采集和传输需要的技术,并给出了DSPACK和Indy的快速实现方法,相关成果已在金融授权、双录等系统中投入使用。
参考文献
[1] 李维.Inside VCL(深入核心――VCL架构剖析)[M].电子工业出版社,2004.
关键词:视频传输;网络视频;嵌入式系统
中图分类号:TP37文献标识码:A文章编号:1009-3044(2010)22-6321-03
The Design and Realization of Embedded Network Video Transmission System
HU Peng1,2
(1.Department of Computer Science, Wuhan University, Wuhan 430074, China; 2.Department of Computer Technology and Application, Wuhan Institute of Technology, Wuhan 430074, China)
Abstract:This paper puts forward design solution of an embedded Network Video Transmission System. The system is designed with the core being S3C2410 and the operating system Linux, This paper introduced the design methods of Network Video Transmission System, and placed its importance on the hardware design and software design of Network Video Transmission System.
Key words: video transmission; network video; embedded system
随着多媒体技术、网络技术的快速发展,远程视频采集传输相关应用已经成为安防、人防等行业的重要应用,而如何更好的获得监控现场的图象数据一直是个热点问题,传统的采用CCD摄像机获取现场视频信息的方法易于实现,但成本较高,相较之下采用ARM+Linux嵌入式系统实现图象的采集与传输具有无可比拟的优势。
1 方案设计
该方案以实时抓拍高清晰图片为主要手段,辅以现场环境数据采集,将远程无人值守或观测人员无法到达的现场情况以高清晰图文信息、准确的现场状态数据的方式实时传送至监控中心并呈现给监测人员,从而实现了实时图片状态信息数据的采集、通信、分析、处理和应用的一体化,为系统监控人员实时观测、分析及处理现场状况提供了快速的、便捷的、高效率的、全新的解决方案。
该方案可应用于各种不同需求的场合。对于仅对高清图文信息有特别需求的用户,可以携带移动终端进行随时随地的图文信息实时采集,即时传送;又可以通过固定前端定时自动拍摄、发送现场图片;还可以远程操控固定前端拍照,实时获取前方高清晰图片。
对于既需要获取现场图片又要求得到现场环境监测数据的用户,在以上述方式获取高清现场图片的同时,还可以通过终端带扩张的485通信接口实时采集多路现场监测数据。
2 系统构成与基本原理
该系统采用USB摄像头采集现场图像数据,然后将采集到的图象通过USB接口传输到基于ARM架构的32位嵌入式处理器,在SDRAM中以MPEG4的方式进行图像压缩处理,然后,按照UDP包的格式打包,通过RJ45接口或GPRS/CDMA模块向监控中心发送,同时监控中心也可以通过网络控制视频采集设备的工作。该系统为实现远程实时监控系统,对监控数据的管理以及视频网络传输等提供了解决方案,系统构成框架图如图1所示。
1) 嵌入式处理器:嵌入式处理器是整个系统的控制管理中心,系统选用S3C2410作为嵌入式处理器,S3C2410 是基于 ARM920T 内核,主频 200M 。
2)存储器设计:采用1片64M Nand flash作为启动ROM和系统程序保存ROM;采用1片16M×32bit共64M SDRAM用于设置程序堆栈和存放各种数据变量等。
3)USB芯片:采用Cypress公司的SL811HS芯片作为USB接口芯片,1个USB host,1个USB device。
4)USB摄像头:采用OmniVision公司的OV511,Linux内核支持该摄像头芯片,USB接口支持1.1/2.0。
5)LCD:本文选用SL3204LCD显示屏,其是一个32x4的LCD驱动器,可软件程控使其适用于多样化的LCD应用线路。
6)GPRS模块:选用西门子MC55嵌入式GPRS模块,该模块内置TCP/IP协议,支持双频GSM宽带900MHz/1800MHz,可通过SMS短消息服务或GPRS来发送或接收资料。
7)以太网芯片:选用RealTek的RTL8019AS芯片,采用QFP-100封装,是一种全双工即插即用的以太网控制器,它在一块芯片上集成了RTL8019内核和一个16KB的SDRAM存储器。
8)CDMA模块:采用华为CDMA模块EM200,是新一代CDMA模块,CM320的替代产品,主芯片采用高通的6020芯片,内嵌TCP/IP协议。
3 软件设计
3.1 视频采集
1) USB 摄像头驱动
视频采集是系统的基本功能,在本系统中,采用USB 摄像头采集视频数据,在Linux中USB摄像头被作为一种字符设备来处理,用/dev/video0来表示,在Linux内核中包含了Ov5ll摄像头驱动程序,在搭建开发环境过程中,在编译操作系统内核时,需要加载OV5ll摄像头驱动。加载驱动的方法有两种:动态加载与静态加载,本文用静态加载方法,具体步骤如下:
① 进行内核裁减,在ARM Linux的kernel目录下 make menuconfig。
② 加载video4linux模块,选择 Multimedia device下的Video for linux,为视频采集设备提供编程接口。
③ 加载ov5ll接口芯片的USB数字摄像头驱动,在 usb support录下选择support for USB和 USBcamera ov511 suppor
④ Make dep为编译生成相关的依赖文件,make zlmage最终的编译命令。
2) V4L编程
v4L (video for Linux)是Linux中关于视频设备的内核驱动程序,v4L处于应用程序和视频设备驱动之间,它屏蔽了视频设备驱动程序的差异,向应用程序提供了统一的编程接口。Linux下用V4L视频程序设计时常用的数据结构有:
① Video_capability:视频设备基本信息数据结构,包括设备名称、设备类型、设备数目、信号源信息、最大分辨率、最小分辨率、信道数等;
② Video_ picture:图象属性数据结构,包括如亮度、灰度、对比度、深度等;
③ Video_channel:信号源属性数据结构,包括信号源编号、名字、制式、调谐器个数等;
④ Video_ Window:采集窗口数据结构,包括窗口的位置、大小等;
⑤ Video_mbuf:映射帧信息数据结构,包括帧数、帧大小、每帧基址偏移等。
⑥ Video_mmap:用于内存映射;
⑦ Video_buffer:最底层对缓冲器的描述。
3) 图像采集程序开发
v4L下图象采集程序的流程:首先打开USB摄像头,读取摄像头的设备信息,更改设备当前的设置(如图像格式),接着,开始进行图像采集,最后将采集到的图像通过LCD显示。
① 使用fd=open(“/dev/v4l/video0”,O_RDWR)函数打开USB摄像头设备,其中,/dev/v4l/video0是打开的设备名,O_RDWR是打开方式(读写方式)。
② 使用ioctl(fd,VIDIOCGCAP,video_cability *)函数获取video设备源的信息,其中,fd是打开的文件描述符,VIDIOCGCAP是控制读写设备通道以获取摄像头信息,video_cability是函数成功后从内核空间拷贝到用户程序空间的摄像头信息,
③ 使用ioctl(fd,VIDIOCGPICT,video_picture *)函数获取缓冲区中图像的信息,其中,fd打开的文件描述符,VIDIOCGPICT是操作命令,video_picture是函数成功后返回的图像信息。
④ 使用ioctl(fd,VIDIOCSPCT,video_picture *)函数设置缓冲区中图像的格式,其中,fd是打开的文件描述符,VIDIOCSPCT是控制读写设备通道以设置图像信息,video_picture是要设置的图像格式信息。
⑤ 使用ioctl(fd,VIDIOCGPICT,video_mbuf *)函数获得摄像头存储缓冲区帧信息, 之后修改viode_mmap中的设置重新设置图像帧的垂直及水平分辨率、彩色显示格式, 接着把摄像头对应的设备文件映射到内存区。
⑥ 连续采集数据
while(1)
{
v4lsync(&device, frame);//等待
framenum = (framenum +l)%2;//下一帧的frame
v4lcapture(&device, framenum);//获取下一帧
buffer = (char*)v4lgetaddress(&device);
}
⑦ LCD显示。用fb=open(”/dev/fb/0”,O_RDWR)直接将图像数据写入FrameBuffer中。
3.2 视频压缩
在网络视频传输系统中,大量的视频需要通过网络进行传输,这就要求在数据传输之前进行编码压缩以减少数据量。新一代基于对象的编码标准MPEG-4以高压缩率、高质量、低传输率成为目前网络多媒体传输的主要格式和标准,针对MPEG一4的编解码器有很多,主要有XVID,Divx等,本设计中采用开源的XVID软件作为MPEG-4视频压缩的核心算法,版本为xvidcorel.1.2。将XVID应用程序移植到ARM平台的步骤如下:
① 执行#tar -zxvf xvidcore-1.1.2-beta2.tar.gz 解压xvidcorel.1.2软件的源代码,并删除优化代码。
② 执行配置命令/configtire-disable-assembly-host-arm-Iinux生成makefile文件;
③ 通过#make, #make install命令编译程序源代码;
④ 将库文件libxvidcore. so.*拷贝到交叉编译器工作目录的lib子目录下,将libxvidcore.so.*等文件拷贝到目标系统的/usr/lib目录下。
⑤ 在程序中设置相应的参数,调用Xvid编码器提供的接口函数:xvid_ global(),xvid encore(),FrameCodeP(),FrameCodeI()等编写应用程序,然后通过交叉编译,最后将生成的可行文件拷贝至目标板ARM调试执行。
3.3 视频传输模块
Socket是一种网络接口,可以被看成是网络通信的一个端点,采用“打开一读一写-关闭”的方式。Linux支持多种套接字,Socket提供下列三种类型套接字:流式套接字(SOCK --STREAM),数据报式套接字(SOCK--DGRAM),原始式套接字(SOCK--RAW),本系统中采用的是流式套接字,主要使用以下几个函数:
1) 服务进程和客户进程在通信前必须创建各自的套接字,然后才能用相应的套接字进行发送、接收操作,实现数据的传输。socket() 函数创建套接字其原型如下:SOCKET socket (int af, int type, int protocol);
2) 当调用socket() 函数创建了一个套接字后,服务器必须把套接字与自己的地址(包括IP地址与端口号)显式地建立联系,以便客户端向该IP地址和端口号的服务进程请求服务,这个过程是通过调用绑定函数bind() 来实现的。bind() 函数原型如下:int bind (SOCKET s, const struct sockaddr FAR * name, int namelen);
3) 服务器设置监听工作方式后,通过调用 accept() 函数使套接字等待接受客户连接,accept() 函数的原型为:SOCKET accept(SOCKET s, struct sockaddr* addr, int* addrlen);
4) 客户通过 connect() 函数可以和服务器建立一个端到端的连接。connect() 函数原型为:int connect(SOCKET s, const struct sockaddr FAR* name, int namelen);
5) 将服务器上的套接字设置为监听方式工作,使用API函数listen():int listen(SOCKET s, int backlog);
6) 对于面向连接的应用程序来说,一旦客户机与服务器实现连接,则可以进行数据传输。send()、recv() 函数是在已建连接上进行数据收发的函数。 send() 函数的原型为: int send(SOCKET s, const char FAR * buf, int len, int flags);recv() 函数的原型为:int recv(SOCKET s, char FAR * buf, int len, int flags);
7) 一旦网络通信任务完成,就必须释放套接字占用的所有资源。通常调用closesocket() 函数即可以达到目的。closesocket() 函数的原型为:int closesocket(SOCKET s );
8) 网络套接字应用程序结束前,要使用下面这个函数释放 winsock DLL: int WSACleanup(void);
9) 当 Winsock 函数调用发生错误时,可以通过如下函数获得错误代码:
int WSAGetLastError(void);
在服务器端的主程序中,首先通过套接口的Socket ()函数创建Socket描述符,通过bind()函数将该描述符与本地地址绑定,再通过listen()函数实现监听,accept()函数实现主程序阻塞,一直等到有客户端发出连接请求,通过三次握手建立双方的连接。
在客户端程序中,通过建立一个MPEGDownLoad类来与服务器建立连接,下载图像信息,在该类中定义一个socket类型变量通过 new Socket (host,port)为其赋值,然后通过Socket.getlnputstream()和Socket.getoutputstream()得到输入输出流,调用各自的read()方法和 println()方法来实现数据的接收和发送。
4 优势与技术突破
与传统的视频采集方案相较,该方案采用高性能的ARM嵌入式微处理器S3C2410,结合大容量的FLASH芯片存储,运行Linux嵌入式操作系统,便于系统移植以及系统应用升级,整个系统具有通用性、安装方便、稳定、可靠、成本低等优点,特别适合实时性要求不很强的工业控制、远程监控系统等诸多领域,具有很好的应用前景。
嵌入式操作系统在系统中有着至关重要的作用,该方案采用Linux操作系统,Linux源代码开放、系统管理性能好、系统稳定性好。采用Linux操作系统,提高了数据传输速度,降低了系统的建造费用。
参考文献:
[1] 熊茂华,谢建华,熊昕.嵌入式Linux C语言应用程序设计与实践[M].北京:清华大学出版社,2010.
[2] 俞辉.ARM嵌入式Linux系统设计与开发[M].北京:机械工业出版社,2010.