前言:一篇好文章的诞生,需要你不断地搜集资料、整理思路,本站小编为你收集了丰富的socket通信主题范文,仅供参考,欢迎阅读并收藏。
关键词:网络通信;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通讯;数据交互;delphi
USing Socket Communication Technology to Realize Data Security Interaction between
the Systems of Huaian Accumulation Fund Center and Registration Trading Center
Wei Juan 1 Zhao Chun-mei 2
【 Abstract 】 With the development of the electronic administrative affairs, different application systems cause certain difficulties while being developed in the aspects of development environment, system structure and data base, etc. This paper adopts socket communication mode to realize data interaction between the systems of Huaian Accumulation Fund Center and Registration Trading Center. The log processing and image processing specially added also provides a good reference to the interaction of other systems.
【 Keywords 】 socket communication; data interaction; delphi
1 系统间的交互方式
加强淮安市公积金中与淮安市房屋交易登记中心数据共享,为社会公众及自身提供一体化的高效、优质、廉洁的管理和服务的过程实现住房公积金贷款“一窗办结”、“一次告知,集中收件”的受理原则,“一次审核、全程通用” 的审查原则。淮安市公积金中心与淮安市登记中心系统要实现系统数据的共享,但系统架构、数据库不一样,给数据交互带来一定难度。
常用的数据交互技术有Socket和WebSevice。
(1)Socket交互,首先建立Socket数据交互包,即两个系统间建立传递数据的数据包,接受到数据包的一端根据制定的规则完成交互。该方式优点是点对点之间的数据交互安全性高。
(2)WebSevice交互以WebSevice 的方式实现服务器间的交互,系统间互为客户端向服务器端发送请求实现交互。该方式的优点是实时交互效率高;缺点是由于服务器端作为客户端,有可能出现安全问题。公积金中心采用C/S架构、Oracle数据库,登记中心是C/S架构\SQL Server数据库,双方业务系统均在内网,且对安全性都有很高的要求,不适合直接互联,双方均租用电信专线实现网络互联、防火墙等设备保障系统安全。所有采用了Socket方式实现数据交互。
Socket是建立在传输层协议上的一种套接字规范,它定义两台计算机间进行通信的规范,套接字屏蔽了底层通信软件和具体操作系统的差异,使得任何两台安装了TCP协议软件和实现了套接字规范的计算机之间的通信成为可能。Socket通常用来实现客户方和服务方的连接。客户程序可以向Socket写请求,服务器将处理此请求,然后通过Socket将结果返回给用户。
2 制定数据交换规范,统一数据交换格式
由于公积金系统和登记中心系统的是在不同的数据库开发的软件,我们首先要做的事情是,将数据的数据映射成为统一的数据格式,再由统一的数据格式映射成为本系统的数据格式。
对于数据交互格式我们制定了两种交互方式。
(1)对数据统一编码,对每个交互内容确定数据项名称、类型、长度、位置、备注。发送的信息组合为一个长字符串。接受方通过对字符串的解码,提取相应的信息,这种模式比较适合交互内容较少的信息。
(2)内容较多时,可以通过XML进行组织数据。
3 基于Delphi的Socket交互的设计
在Delphi中,对Windows Socket进行了有效的封装,使得可以很方便地实现通信。在Delphi环境下实现Socket技术通讯,服务器的流程为,首先建立服务器端的的Socket,当检测到来自客户端的连接请求时,向客户端发送收到连接请求的信息,并建立与客户端之间的连接。当完成通信后,服务器断开与客户端的Socket连接。客户端的步骤如下。建立客户端的Socket,确定要连接的服务器的主机名和端口。发送连接请求到服务器,并等待服务器的回馈信息。连接成功后,与服务器进行数据的交互。数据处理完毕后,关闭自身的Socket连接,如图1所示。
3.1 客户端软件开发
客户端软件主要实现的功能是向与服务器端建立Socket连接,发送消息,并接受返回信息,结束后断开Socket连接。主要通过ClientSocket实现。
一、Java多线程简介
将多线程机制蕴含在语言中,是Java的一个重要特征。所谓线程,是指程序中的一个执行流。在一个进程中,可以有多个线程。这些线程在操作系统的调度下并发执行,使得每个线程都好像在独占整个系统资源。而有了多线程这个特性,JAVA可以支持多个程序并发执行。利用Java的多线程编程接口,开发人员可以方便地写出支持多线程的应用程序,有效地减少并发并行程序设计的困难,提供程序执行效率。
1.线程的创建
有两种方法可以创建线程。第一种方法是通过继承类Thread来创建线程类。子类重载其mn0方法。实现方法如下:
classThieadNameextendsThread{
publicvoidrun(){//run是整个线程类代码的
入口
…//需要以线程方式运行的代码
}}
第二种方法是建立一个具有Runnable接口的类。由于Java不支持多继承性,如果需要类似线程方式运行且继承其他的类,就必须实现Runnable接口。Runnable接口只有一个方法run()。在类中实现此接口的方法如下:
classThieadNameextendsAppletimplementsRunnable{
publicvoidrun(){
…//需要以线程方式运行的代码
}}
2.线程的调用
如果采用第一种方法,创建的线程类的调用格式如下:ThreadNametest=newThreadName();//test是线程类
ThreadName的一个实例test.start();
start()是线程类的成员函数,用于启动该线程,该线程将自动调用run()方法。
如果采用第二种方法,创建的线程类的调用格式如下:
ThreadNametest=newThreadName();
Threadth=newThiead(test);/组过Thread创建个新的线程
th.start()
3.线程的同步及通信
系统中存在多个线程时,就需要保证线程的同步及相互通信,以期协调工作,避免发生死锁。Java提供了二个标准的Object类方法wait(),notify()和notifyAll(),以及二个Thread类方法sleep()suspend()和resume(),用于中断或唤醒线程的执行。当线程调用sleep(),wait()或suspend()方法之后线程就会由可运行状态进入阻塞状态(blocked),一旦线程睡眠时间到或者是其他线程调用了notify()或resume()方法后,此线程才会由阻塞状态进入可运行状态中,然而一个线程是否最终占有CPU,取决于系统的调度策略。Java1.0在solaris版本中实现的“绿色线程”的调用策略是让一个线程持续处于执行状态直到有一个更高优先级的线程将之打断,而Windows95和WindowsNT是给每一个处于可执行状态的线程分配一个时间片,当时间片用完时系统会调用另一个线程投入运行。
二、网间网的Socket通讯机制
TCP/IP技术的核心部分是传输层(TCP和UDP协议)、网络层(P协议)和物理层(面向各种物理硬件技术),能实现这三层协议的内核可称之为tcp/ip网络操作系统。tcp/ip协议技术中的中下层协议向外提供的只是原始的编程界面,而不是直接的用户服务,用户服务要靠核外的应用程序实现。应用程序和tcp/ip核心协议关系如图2所示。
即网间网应用程序能够直接操作的是TCP/P核心协议提供的编程界面。由于网络中资源、运算能力和信息的差异,同时又由于网间网通信完全是异步的,因此,在基于TCP/P协议的网间网中,最主要的进程间相互作用模型是客户/服务器(client/server)模型。客户和服务器分别是两个应用程序(进程),客户向服务器发出服务请求,服务器作出响应。
网间网进程通信的关键是要解决进程的标识和多传输协议的标识问题以及进程间相互作用的模式。在网间网中,全局惟一的标识一个进程需要一个三兀组表示,即用半相关(half-association)来描述一个Socket:
{协议,本地地址,本地端口号}
而一个完整的网间网进程通信需要由两个进程组成(两个端进程),因此一个完整的网间网进程通信必须用一个五元组表示,即用相关(association)来描述一■个完整的Socket:{协议,本地地址,本地端口号,远地地址,远地端口号}其中,一个确定的网间网进程通信只能使用同一个内核的高层协议,不可能通信的一端用TCP而另一端用UDP协议。故而,两个协议相同的半相关才能组成一个合适的相关,两个三元组组合起来是一个五兀组而非六兀组。每一个Socket有一个本地惟一的Socket号,由操作系统分配。一个本地Socket号,完整地描述了本地进程以及与之通信的远地进程,因此Socket的语义具有网络一致性,准确地描述了网络进程。因此,Socket的关键是建立客户和服务器之间的相关。
Socket编程界面最早由4BSDUNIX系统提出,其主要目的是解决网间网进程通信(IPC)问题。故而,Socket系统调用与UNIX的文件访问有许多类似之处,是对UNIX输入输出的扩充。Socket是面向客户/服务器模型设计的,针对客户和服务器程序提供了不同的Socket系统调用。服务器拥有全局公认的半相关Socket,这就保证了任何客户都可以在网络的任何地方随机向它发出联接请求和信息请求。Socket的数据信息是原始字节流的形式,通信双方要在此基础上进行约定的数据格式化和解释等处理(即相同的协议),然后才能进行进一步的具体应用操作,这也是实现某种协议的过程。基于Socket的这种通信机制,在网关(gateway)的作用下可实现TCP/协议和其他低级协议,如现场总线协议CAN(ControllerAreaNetwork)协议的转换,进而扩展Internet应用领域,将实时控制功能带入网络。Socket通信机制提供了两种通讯方式:有联接和无联接方式,分别面向不同的应用需求。使用有联接方式时,通信链路提供了可靠的,全双工的字节流服务。在该方式下,通信双方必须创建一个联接过程并建立一条通讯链路,以后的网络通信操作完全在这一对进程之间进行,通信完毕关闭此联接过程。使用无联接方式时其系统开销比有联接方式小,但通信链路提供了不可靠的数据报服务,不能保证信源所传输的数据一定能够到达信宿。在该方式下,通信双方不必创建一个联接过程和建立一条通讯链路,网络通信操作在不同的主机和进程之间转发进行。
面向客户/服务器方式的Socket通信机制模型如图3所示。
三、Java多线程在网络编程中的应用
1.Socket客户端程序设计我们通过一个简单的应用程序实例来说明其通信程序的工作过程和编程特点。
?X'
importjava.io.;
X
importjava.net.;publicclassJabberServer{publicstaticfinalintPORT=8080;publicstaticvoidmain(String[]args)throwsIOException
{ServerSockets=newServerSocket(PORT)System.out.println(“Started:”s)try
{Socketsocket=s.accept();try
{System.out.println(“Connectionaccepted:”+socket)
DatalnputStreamsi=newDataInputStream(socket.getInputStream());
PrintSteamso=newPrintStream(socket.getOutputStream());while(true){Stringstr=in.readLine();if(str.equal(“END”))break;
System.out.println(“Echoing:”+str);System.out.println(str);
}finally{
System.out.println(“closing"?”);}}}
此应用程序的功能是从输入流中获取从服务器方发来的数据,并显示在屏幕上。Socket类是覆盖在一个与平台有关的实现之上的,只是它把具体的系统细节从java程序中屏蔽了。因而在程序开始部分引入了java.net包中的所有的Socket类。基于此,Java程序即可实现与平台的无关性。网络通信的目的主要是对网络资源的访问和操作。在建立了新的Socket对象实体s后,利用outputStream(),getTnputStream()方法建立输出、输入流。这样,访问的网络资源的过程就变成了处理流对象的过程,即以数据流中的方法读写应用程序端。数据流(Stream)可以理解成数据的通信途径,在建立好应用程序和资源方的通信通道后,远方的数据就可以自动传输过来。在程序的结尾用close()方法关闭输入、输出流和Socket,这样将释放所占用的系统资源。
Java语言的Socket通信机制和UNIX系统的输入输出操作(open-read-write-close)相类似。其客户端基本操作程序的编写概括起来包括以下四步:①打开Socket,即创建一个Socket对象实体:②创建与此Socket联接的输入输出流;③根据服务器的协议向此Socket写数据或从Socket读数据:④关闭输入、输出流和Socket。
2.Socket服务器端程序设计基于Java语言的Socket服务器端基本操作过程和客户端过程相对应,其程序的编写也包括以下五步:①打开SeneSocket,即创建一个ServerSocket对象实体在指定端口为客户端请求的Socket服务;②使用ServerSocket类的accept()方法接收来自客户端的联接请求;③使用新建的Socket对象创建输入、输出流对象;④通过对流对象的操作完成客户端的处理请求,并将结果返回给客户端;⑤当客户端和服务器工作结束时,关闭输入、输出流,用ServerSocket类的close()方法关闭Sacket。
服务器通常分为并发服务器和重复服务器,并发服务器只接收客户请求,不处理请求,客户请求由它的fork之进程处理和响应;而重复服务器接收、处理并响应客户请求,即工作时只能同时和一个客户程序联接。下面给出使用Java的多线程实现并发服务器通信程序。具体地实现并发服务器的思路是:在服务器的程序中首先创建单个ServerSocke^并调用accept()来等候一个新连接,然后用accept()返回的Socket新建一个线程,它只为连接的特定的客户提供服务。接着再调用accept(),等候下一个亲新的连接请求。
(1)服务器端主线程程序的实现
?..+x.importjava.io.;
x
importjava.net.;publicclassMultiJabberServer{publicstaticfinalintPORT=8080;publicstaticvoidmain(String[]args)throwsIOException
{ServeiSockets=newServerSocket(PORT);System.out.println(“ServerStarted”)try
{while(true)
{Socketsocket=s.accept()by
{newMyserverHandler(socket)
}catch(IOExceptione){socket.close()
}
}
}finally{
si.close()so.close();s.close();
}
}
(2)服务器端子线程程序的实现publicclassMyserverHandlerextendsThread{privateSocketsocket;
privateBufferReaderin;privatePrintWriterout;
publicMyserverHandler(Sockets)throwsIOException
{socket=s;
in=newBufferReader(newInputStreamReader(socket.getlnputStream()));
out=newPrintWriter(newBufferedWriter(newOutStreamWriter(socket.getOutputStream()))true);
start();
publicvoidiun(){
by{
while(true){
Stringstc=in.readLine()if(str.equals(“END”)break;
System.out.println(“Echoing:+str);out.pnintln(str)
}
System.out.Println(“closing"?,,);
}finally{
try{
socket.close();
}catch(IOException。){}
}
1
四、结语
关键词:Linux;嵌入式;网络编程
中图分类号:TP311 文献标识码:A 文章编号:1009-3044(2009)15-3953-02
Applications of Network Programming in Embedded Linux Systems
JIANG Ai-zhen
(Channel 561 in SARFT,NanChang 330046,China)
Abstract: Linux supports a wide range of types of socket addresses and the abstract cluster as a single socket interface, the introduction of the abstract is for the convenience of Internet application programming, as well as web applications to provide data communications between the convenience. In this paper, starting from the basic concepts of socket, introduced the basic model of network programming and the Linux kernel support for the socket.
Key words: Linux; Embedded; Network programming
1 socket概述
在Linux中的网络编程是通过socket套接字接口来进行的,这一抽象的引入是为了方便联网应用程序的编程,在UNIX的BSD版本第一次使用了这种接口,因此他也叫做BSD套接字。应用程序可以使用这种统一接口收发网络上的数据,网络的socket数据传输是一种特殊的I/O口,socket也是一种文件描述符。套接字的设计符合Linux的习惯,在理想情况下,应将所有可读写访问的对象映射成文件,这样就可以用普通的文件读写操作来处理这些对象了,使通信中的收发可以很容易映射成读写操作。在传输协议的上下文中,由这类读写操作的对象就是通信关系的两端,他们表示成了套接字。
BSD套接字是一个通用接口,它支持不同的网络结构,同时也是一个内部进程间通信机制。当一个主机上同时有多个应用程序在运行,他们使用tcp和udp协议进行通信,则传输层协议收到数据后将根据端口和套接口区分数据是传给哪个应用程序。端口是标识传输层与应用程序的数据接口,每个端口有一个16位的标识符。套接口是IP地址与端口号的组合,用来标识全网范围内的唯一一个端口,在tep和udP协议中用来标识一个连接,网络应用程序之间通过套接口来实现通信。套接字是套接口描述字的简称,是整型数字,它与文件描述符共用一段数值空间O_65535。应用程序中使用套接字来调用套接口,套接字可认为是指向套接口的指针,就像文件描述符是指向文件的指针一样。一个套接字描述了一个链接的一个端口,一个socket端点可以用socket地址来描述,socket地址结构由正地址,端口和使用协议组成(TCPorUDP),因此两个互联的进程都要有一个描述他们之间连结的套接字。我们也可以把套接字看作为是一种特殊的管道,只是这种管道对于包含的数据量没有限制。套接字存在于特定的通信域(即地址族)中,只有隶属于同一地址族的套接字才能建立对话。Linux支持的协议族有AF_INET(IPv4协议)、AF_INET6(IPv6协议)和AF_UNIX(Unix域协议)。
Linux支持多种套接字类型,每种套接字类型对应于创建套接字的应用程序所希望的通信服务类型。同一协议簇可能提供多种服务类型,比如TCP/IP协议族提供的虚电路和数据报就是两种不同的通信服务类型。TCP/IP中常用的socket类型共有三种,一种是流式socket(SOCK_STREAM),另一种是数据报式socket(SOCK_DGRAM),还有一种是原始socket(SOCK_RAW)。流式socket是一种面向连结的socket,对应于面向连接的TCP服务应用。数据报式socket是一种无连接的socket,对应于无连接的UDP服务。原始套接字接口容许对较低层协议如IP、ICMP直接访问,常用于检验新的协议实现或访问现有服务中的新设备。
2 网络编程基本模式
2.1 客户机/服务器模式
网络编程的基本模式是Client/Serve:模式,该模式的建立基于以下两点:
1) 非对等作用;2)通信完全是异步的客户机/服务器模式在操作过程中采取的是主动请示方式,首先服务器方要先启动,并根据请示提供相应服务。Server端首先调用socket创建一个一定类型socket,然后通过bind函数将这个socket绑定到一个client知道的端口上,接着server调用Listen函数设置倾听队列的长度,为了接收来自client端的请求做准备,然后server调用accept,开始在所绑定的端口倾听来自client端的连接请求。如果socket被设置成阻塞方式, accept调用将被阻塞,进程被挂起,直到server收到来自client的请求后,accept才返回。Client端通过socket调用创建一个一定类型的socket(应当和server的socket类型相同)。然后调用connect函数向server所在的主机发出连接请求,连接时,需要指定server所在主机的IP地址和server倾听的端口号,连接的报文包含了client端的初始的序号SYN a和MSS=1460信息(最大数据段的大小)。正在倾听来自client的连接请求的server收到client的连接请求后,server从accept调用中返回(通常socket是阻塞方式工作的)。server将会向client端发送server端的初始序号SYN b和对client端的SYN a的确认ACK=a+l,还有本端的最大数据MSS当client端接收到server端的回应时,将发出对server请求的ACK=b+1。然后client从connect中返回,返回值是一个打开的socket的描述符,这个描述符和文件的描述符类似,程序可以像使用文件的描述符一样使用它。稍后,在server端收到client端对其请求的回应时,server将从accept调用返回,返回值也是一个socket的描述符。
2.2 面向连接协议的字节流套接字编程
字节流socket采用的是传输控制协议TCP。TCP提供面向连接的流传输,面向连接对可靠性的保证首先是它在进行数据传输前,必须在信源端和信宿端建立连接。在面向链接传输的每一个报文都需要接收端确认,未确认的报文被认为是出错报文。字节流套接字的服务器进程和客户进程在通信前必须先建立连接,建立连接和通信的步骤如下:
1) 服务进程首先调用Socket()创建一个字节流套接字,并调用bind()将服务器地址捆扎在该套接字上,接着调用listen()监听连接请求,随后调用accept()做好与客户进程建立连接的准备,无连接请求时,服务进程被阻塞;
2) 客户进程调用Socket()创建字节流套接字,然后调用connect()向服务进程发出连接请求;
3) 当连接请求到来后,服务进程被唤醒,生成一个新的字节流套接字,并用新套接字同客户进程的套接字建立连接,而服务进程最早生成的套接字则继续用于监听网络上的服务请求;
4) 服务进程和客户进程通过调用read()和write()交换数据;
5) 服务进程和客户进程通过调用close()撤消套接字并中断连接;当选择SOCK STREAM(字节流)类型的时,sock()系统调用中的参数protocol(协议)总会选中TCP,而UDP则一直用作SOCK DGRAM类型的传输协议。
2.3 非连接协议的数据报套接字编程
数据报式socket采用的是用户数据报协议UDP,它是建立在IP协议之上的,提供无连接数据报传输,主要应用在高可靠性、低延迟的局域网上,它的优点是高效率低开销,不用建立连接和撤销连接,缺点是不可靠,报文丢失后需重发。数据套接字的服务进程客户进程通信前不必建立连接,UDP则一直用作SOCKpGRAM类型的传输协议,通信的步骤如下:
1) 服务进程首先调用Socket()创建一个数据套接字,并调用bind将服务器地址捆扎在该套接字上,然后调用recvfrom()等待客户进程发来的请求;
2) 客户进程在调用SocketQ创建一个数据报套接字后,调用bindU将客户机地址捆扎在此套接字上,接着调用sendto()向服务进程发送请求,然后调用recvfrom()等待服务进程返回该请求的处理结果;
3) 服务进程在执行客户进程所请求的任务后,调用sendto()将处理结果返回给客户进程;
4) 服务进程和客户进程通过调用close()撤消套接字;
3 Linux内核对socket的支持
确切地说,Linux内核只提供了一个与套接字有关的系统调用,应用程序的所有套接字调用都会映射到这个系统调用上。在Linux内核中的net/socket.c中定义这个函数sys_socketcall(int call,unsigned long *args)。 include/asm/unistd.h中会指派一个数字,该数字会和arch/i386/kernel/entry.s中的系统调用一起添加到表格中。通过调用中。all参数可以说明所指向的那个套接字函数,在include/linux/net.h中定义了可接受的参数SYS_SOCKET, SYS_IND,SYS_CONNECT, SYS_LISTEN等,在用户空间的函数库中,带有特定参数的sys_socketcall调用会映射成某个独立函数,在内核中若要选中希望调用的那个函数,需要在sys_socketcall函数中用到一条:witch命令如下所示,而在此之前首先要使用copy_from_user()命令将sys_ socketcall()的函数复制到一个向量中,即ensign long a中。
if copy_from user(a,args,nargs(call))
return _EFAULT;
a0=a[0];
al=a[1];
switch(call)
{
case
SYS_SOCKET:
err=sys_socket(a0,al,a[2]);
break;
SYS_BIND:
err=sys_bind(a0,al,a[2]);
break;
SYS_CONNECT:
err=sys_connect(a0,al,a[2]);
break;
为了支持BSD套接字,一个重要的数据结构就是struct socket,它的定义位于
include/linux/net.h中,其定义如下:
struct socket
{
socket statestate;
unsigned long flags;
struct proto_ops *ops;
structmode *inode;
structfasync struct *fasync list;
structfile*file;
structsock*sk;
wait queue head t wait;
shorttype;
unsigned char passcred;};
与早期的内核相比,socket结构己经稍有简化。state中存储的是套接字状态可以取值如下(include/linux/net.h):SS_FREE(不忙)、SSes UNCONNECTED(未连通)、SS_ONNECTING(目前正在连接)、SS_ONNECTED(已连通)、SS_ISCONNECTING(目前正在断开连接)。flags用以同步访问,ops指针指向了连通协议(如tcp或udp)在初始化之后的协议运作。就像Linux中的每个文件都有一个mode一样,每个BSD套接字也分派了一个mode o file中存储了一个指向该文件结构的指针,这个结构连接了套接字,因此它可以用与指向套接字。如果有进程等待着这个套接字上的事件,也可以通过fasync_list找出该进程。通过sk指针可以使用一个匹配的sock结构。不过,这个sock结构是由BSD套接字之下、特定于协议的套接字初始化的,并且连通到这个指针。字段负责根据用户空间中的同名套接字调用存储第二个参数,在Linux内核include/asm/socket.h中定义了可接受的参数。
从上面分析可以看出任何时候通过一个socket来读写数据时,都是在使用一个系统调用(system_call)这个调用(例如read或write)跨越了用户空间应用程序与内核的边界。另外,在进入内核之前,您的调用会通过C库来进入内核中的一个通用函数system_call()。从system_call()中,这个调用会进入文件系统层,内核会在这儿确定正在处理的是哪种类型的设备。最后,调用会进入socket层,数据就是在这里进行读取或进行排队从而通过socket进行传输的。
4 总结
每种网络协议都提供网络应用开发接口,TCP/IP协议的应用开发接口的事实标准是socket套接口,开发socket的目的是隐藏网络底层的复杂结构和协议,使编程人员能够简单抽象的对网络进行操作。socket面向客户机/服务器模型,针对客户机/服务器程序提供不同的socket的系统调用函数,客户端随机申请一个socket,操作系统为之分配一个随机socket号;服务器端拥有全局公认的socket号,任何客户都可以向他发送连接请求和信息请求。进程通信以前,双方必须各自创建一个端口,否则是没有办法在通信前建立联系的,而socket提供了这种进程间通信的端口。从网络编程的套接字的分析来看,选择TCP套接字和选择UDP套接字编程,在传输数据时有着速度、效率和稳定性的差别。TCP编程拥有了可靠的数据连接,UDP不具有。但是在速度方面,UDP编程确优于TCP编程,特别是对于传输短消息。基于这两种通信方式优缺点的考虑,在后续编写IDU控制应用软件时,将UDP套接字用于硬件终端对外广播本地IP地址,使局域网内客户端软件识别某台终端设备,获取其MAC地址等硬件信息。将TCP套接字用于在客户端传输用户数据,对硬件终端上的硬件设备进行初始化设置。
参考文献:
[1] Warren W Gay. Linux Socket Programming by Example. Que(R), April 2000.
[2] Jonathan Corbet,Alessandro Rubini,Greg Kroah-Hartman. Linux Device Drivers 3rd Edition. Reilly Media Inc,2005.
[3] 孙琼,嵌入式Linux应用程序开发详解[M],北京:人民邮电出版社,2006.
关键词:Windows Socket;TCP/IP;阻塞;非阻塞;异步选择机制
中图分类号:TP393文献标识码:A文章编号:1009-3044(2008)09-11598-02
The Implementation of LAN Communication Based on Asynchronous Selection Mechanism
YANG Xiao-yan, BAI Ya-xiu
(Ankang University, Ankang 725000, China)
Abstract: Internet is increasingly universal in our country, customer's needs to network application also increase continuously. For the network condition's fast changing, to network application procedure development personnel, it is very important to develop the highly effective windows network application procedure. The article introduces Windows Socket, Asynchronous Selection Mechanism briefly and the implementation of LAN communication based on the mechanism according to the basic principle of the network correspondence with VC in dail.The method has characteristics of real time and high efficiency, and may be widely applied in C/S structure softwares.
Key words: Windows Socket; TCP/IP; Blocking; Non-blocking; Asynchronous Selection Mechanism
1 引言
Internet在我国日益普及,用户对网络应用的需求也不断增长,提高网络程序的效率就显得相当重要。网络由一系列协议组成,TCP/IP协议是当今异种机互联的工业标准,它支持不同厂家、不同操作系统的计算机之间的通信。TCP/IP协议族的分层结构中的传输层为相互通信的主机提供了端到端的通信能力。其中,TCP协议向应用层提供可靠的数据连接,它保证进程间数据传输的正确、有序和不重复。UDP协议仅仅为应用层提供数据报的分组发送服务,数据传输的可靠性只能通过应用层来保证。TCP和UDP的主要差别在于可靠性,TCP高度可用,需要大量功能开销,而UDP是简单、高效。由于是在局域网中实现通信,为了达到简单、实时、高效的目的,在介绍Windwins Socket,异步选择机制,网络通信原理等相关知识的基础上,根据基于数据报套接字(UDP协议)的编程步骤,详细探讨了在VC中基于异步选择机制的局域网通信的实现。
2 Windows Socket及异步选择机制
2.1 Windows Socket
套接字(Socket)是建立在传输层协议(主要是TCP和UDP)上的一种套接字规范,最初是由美国加州Berkley大学提出,它定义了两台计算机间进行通信的规范,套接字屏蔽了底层通信软件和具体操作系统的差异,使得任何两台安装了TCP协议软件和实现了套接字规范的计算机之间的通信成为可能。Windows Scoket是UNIX操作系统下的Bakeley Socket应用程序开发接口在Windows环境下的实现。Windows Socket规范主要有WinSock1.1和WinSock2两个版本,它们保持了和Bekeley Socket函数的兼容性,并做出了重要扩充。这些扩充主要是增加了一些异步请求函数和对网络事件的异步选择机制,使之更适合Windows平台消息驱动的特性。
2.2 异步选择机制
Windows Socket在两种模式下执行I/O操作,阻塞和非阻塞。在阻塞模式下,在I/O操作完成前,执行操作的Winsock函数会一直等待下去,不会立即返回程序(将控制权交还给程序)。Windows Sockets为了支持Windows消息驱动机制,使应用程序开发者能够方便的处理网络通信,它对网络事件采用了基于消息的异步存取策略。Windows Sockets的异步选择函数WSAAsyncSelect()提供了消息机制的网络事件选择,当使用它登记的网络事件发生时,Windows应用程序相应的窗口函数将收到一个消息,消息中指示了发生的网络事件,以及与事件相关的一些信息。这个函数自动设置套接字为非阻塞模式。
Int WSAAsyncSelect(SOCKET s, HWND hWnd, unsigned int wMsg)
S,标识了请求事件通知的套接字描述符。
hWnd,标识当一个网络事件发生时,接收消息的窗口的句柄
wMsg,网络事件发生时,窗口接收到的消息(这里指的是一个自定义的消息。)
lEvent,指定应用感兴趣的网络事件。
3 网络通信的基本原理
网络通信实质上是网络中的不同主机进程之间的相互通信(可以把同机进程通信看作其中的特例)问题。与单纯的同机通信相比,网络通信需要解决以下三个问题。第一,标识网络中的进程。在同一主机上,不同进程可以用进程标识符来标识,而在网络通信中则是利用端口号来标识。端口是TCP和UDP与应用程序打交道的访问点,是TCP/IP协议软件的一部份。TCP/IP协议规定了一些标准保留端口,主要提供给服务器进程使用,用户进程可以申请使用非保留端口,其端口的标识符在本机种具有唯一性。因此可以利用端口号作为网络中进程本身的标识。第二,多重协议的识别。网络中的两个进程必须使用协议来相互通信,而网络协议有多种,这就要求进程能够在众多的协议中作出选择。原因在于不同的协议地址格式不同、工作方式不同(比如面向连接与无连接),协议端口分配是相互独立的。综合以上两点,在网络中全局地址标识一个本地进程需要一个三元组:协议,本地地址,本地端口号。而一个完整的网络通信实例是由通信两端的进程组成,因此需要一个五元组来标识:协议,本地地址,本地端口号,异地地址,异地端口号。这里的本地地址、异地地址是用来标识计算机的,一般是指计算机的IP地址。第三,进程之间相互作用模式,即应用程序相互作用的模式。在网络中两个应用程序间主要的作用模式是客户机/服务器模式,在这种模式中客户应用程序向服务器程序请求服务,这种方式隐含了在建立客户机/服务器间通信时的非对称性。表1显示了基于数据报套接字的客户机/服务器编程模型。
4 基于异步选择机制的局域网通信的实现
4.1 实现思想
根据数据报套接字的客户机/服务器编程模型,采用Windows Scoket的异步选择机制,将服务器端和客户端在同一个程序中实现。需要通信的计算机只要运行同样的程序,通过输入对方的IP地址,或主机名就可以实现相互通信。
4.2 在VC中的具体实现
4.2.1 创建基于对话框的MFC EXE工程
在应用程序类的初始化函数:InitInstance()种调用如下语句加载套接字库进行版本协商。(采用Winsock2版本)
WSAStartup(wVersionRequested,&wsaData);
if(LOBYTE(wsaData.wVersion)!=2||HIBYTE(wsaData.wVersion)!=2)
{WSACleanup();return FALSE;}
在预编译头文件中包含头文件winsock2.h,并链接库文件ws2_32.lib.
4.2.2 服务器端的实现
m_socket=WSASocket(AF_INET,SOCK_DGRAM,0,NULL,0,0);//创建数据报套接字
SOCKADDR_IN addrSock;//地址结构体的定义
addrSock.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
addrSock.sin_family=AF_INET;
addrSock.sin_port=htons(6000);//端口号为6000
//将套接字绑定到一个本地地址和端口上
bind(m_socket,(SOCKADDR*)&addrSock,sizeof(SOCKADDR));
//采用异步选择机制注册网络读取事件
WSAAsyncSelect(m_socket,m_hWnd,UM_SOCK,FD_READ);
#define UM_SOCK WM_USER+1 //自定义消息UM_SOCK
afx_msg void OnSock(WPARAM,LPARAM);//消息响应函数原型声明
ON_MESSAGE(UM_SOCK,OnSock)//消息映射
//接收数据(消息响应函数的实现)
OnSock(WPARAM wParam,LPARAM lParam)
{ switch(LOWORD(lParam))//判断是否是网络读取事件发生了。
{case FD_READ:
if(SOCKET_ERROR==WSARecvFrom(m_socket,&wsabuf,1,&dwRead, &dwFlag,(SOCKADDR*)&addrFrom,&len,NULL,NULL))
{MessageBox("接收数据失败!"); return;}
str.Format("%s说:%s",inet_ntoa(addrFrom.sin_addr),wsabuf.buf);
str+="\r\n";
GetDlgItemText(IDC_EDIT_RECV,strTemp);
str+=strTemp;
SetDlgItemText(IDC_EDIT_RECV,str);
break;
}
}
4.2.3 客户端(实现数据的发送)
((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS1))->GetAddress(dwIP); //获取服务器端的IP地址
SOCKADDR_IN addrTo;// 接收方地址结构体定义
addrTo.sin_addr.S_un.S_addr=htonl(dwIP);
addrTo.sin_family=AF_INET;
addrTo.sin_port=htons(6000);
//将数据发给服务器
if(SOCKET_ERROR==WSASendTo(m_socket,&wsabuf,1,&dwSend,0,
(SOCKADDR*)&addrTo,sizeof(SOCKADDR),NULL,NULL))
{MessageBox("发送数据失败!");return; }
4.3.4 在应用类及对话框类的析构函数中分别终止套接字库,关闭套接字
WSACleanup();//终止对套接字库的使用
closesocket(m_socket);//关闭套接字,释放与套接字相关的资源。
4.3.5 程序运行效果如图1所示
<E:\2008学术交流\2008学术交流第一卷第九期\第1次供稿 54\2网络通讯及安全\yxy02.tif>
4.3.6 注意问题
编写网络通信程序需要特别注意的是:每一台机器内部对变量的字节存储顺序不同,而网络传输的数据是一定要统一顺序的。所以对内部字节表示顺序与网络字节顺序不同的机器,一定要对数据进行转换;在一个套接字上基于消息请求网络事件通知,可以同时请求多个网络事件,当接收到到消息时,需要根据发生的网络事件作相应的处理;网络的状况瞬息万变,在调用函数的时候,应对函数的返回值进行判断,以便找到出错原因。
5 结论
在Windows平台下程序的运行都是基于消息的,如果采用阻塞套接字,就会由于接收函数的调用而导致程序暂停运行,影响了程序运行的效率。采用异步选择机制,接收端和发送端在同一个程序中,并且采用数据报套接字实现了局域网通信,达到了简单、高效、实时的目的。在实现网络通信时,除了应注意的问题外,还应了解相关的网络协议以及程序在Windows平台下工作的原理,根据应用的具体需求,才能实现真正高性能的网络通信。
参考文献:
[1] 陈明.实用网络教程[M].北京:清华大学出版社,2006.1.
[2] 胡志坤,秦业,等.Visual C++通信工程实例精解[M].北京:机械工业出版社,2007.1
[3] 孙小刚,韩冬,等.面向软件工程的Visual C++网络程序开发[M].北京:清华大学出版社,2004,11.
关键词:JAVA;SWING;Socket;IM
中图分类号:TP319文献标识码:A文章编号文章编号:1672-7800(2013)012-0091-03
作者简介:徐华平(1977-),男,硕士,盐城师范学院讲师,研究方向为教育软件设计及教育信息化。
0引言
由于企业内管理、生产、销售等各个环节的信息流动与传递已成为企业正常生产与运转的重要条件,搭建一个基于企业内部网络的即时通信平台的重要性不言而喻。然而,通用的商业IM软件依赖于互联网接入技术,其信息安全性差。因此,有必要建立一种基于局域网的内部即时通信平台。
在各类网络客户端之间的通信机制的选择中,基于Socket机制无疑是成熟、可靠的选择。这种机制透明于各类局域网络类型,能够为企业提供一种优良、高效、快速的通信机制。基于上述优点,使得基于Socket机制的网络通信软件无需对企业现有的网络硬件设施进行任何变动,因而具有成本低廉的优点,能有效降低局域网通信负荷,提高局域网的使用效率,可以很好地解决企业内部局域网的各种通信需求。
1系统关键技术分析与选择
本文重点探讨即时通讯软件设计中实现系统通讯的关键技术。在应用系统中,客户端向服务端发送请求,等待服务器返回数据,再刷新客户端的数据,称之为通讯。在B/S或UE设备上实现即时通信的客户端,其同步方式是不可行的。此类客户端一方面处理能力弱,另一方面网络会引起延时,因此,在客户端之间以及客户端与服务器之间的同步协调难以做到,通常采用异步方式。在 C/S 网络编程中,数据的发送和接收通过Socket 套接口完成,套接字分为阻塞式和非阻塞式[1]。
通过对Java Net框架下GUI技术、通信协议等关键技术的分析,本通信器决定采用如下技术方案:
(1) Swing技术开发GUI桌面程序:采用Swing顶层容器、基本组件和事件处理等轻量级组件构建局域网通信系统的程序主要界面。
(2) UDP通信协议: 鉴于UDP协议无链接,不可靠传输,通信双方可不保持对方的状态,只需配置端口和IP地址即可通信,方便快捷,减少网络开销;考虑到UDP协议不可靠性,该通信器的通信协议中采用了确认与重传机制来保证数据传输的可靠性,采用了动态的超时重传定时器值提高了本协议的适应性和灵活性,还使用了在应用层对数据进行分片的方法来避免IP层分片的低效率[2]。
(3) 在本系统设计采用UDP通信协议时,基于UDP的Socket编程技术,中提供了两个类DatagramSoeket和DatagramPacket用来支持数据报通信。Datagramsocket用来在程序之间建立传送数据报的通信连接是数据报通信中的Socket。在数据报实现C/S通信程序时,无论在客户端还是服务器端,都要首先建立一个DatagramSoeket对象,用来表示数据报通信的端点,应用程序通过Socket接收或发送数据报。DatagramPaeket则用来表示一个数据报,它是传输数据的载体,封装了数据、数据长度、数据报地址等信息[3]。
(4)系统采用Derby作为数据库,Derby数据库是一个纯用Java实现的内存数据库,属于Apache的开源项目。因为是用Java实现的,所以可以在任何平台上运行。另外一个特点是体积小、免安装,只需要几个小Jar包就可以运行。
2系统总体目标与需求分析
2.1设计目标
(1)使用Swing组件实现图形化用户界面。
(2)使用Socket技术和UDP协议通过IP地址和PORT提供不可靠非连接通信。
(3) 实现基于线程池的多端口监听。
(4) 实现客户端间的文字、文件信息共享。
2.2功能需求分析
(1)用户管理。即时通讯系统拥有多个账户,允许多个用户注册。一个用户可以注册多个标识,注册所使用的帐号类型为字母数字的组合。注册新用户时必须填写符合要求的信息,注册后只有用户名与密码验证成功才能正确登录。
(2)分组管理。分组管理部分要能够实现分组的添加与删除,所添加的分组名称可以是中文也可以是字母数字的组合,通过对分组的有效管理便于更方便地管理好友。
(3)好友管理。用户可以查询所有用户,适当选择加为好友。一个用户可以添加多个用户为好友,同时一个用户也可以被多个用户添加为好友。用户可以删除好友,但是用户只可以将好友放在一个组中。
(4)即时通讯。即时通讯模块用户可以与在线的好友进行聊天,用户首先查看好友是否在线,如果在线即可进行即时通讯,并且用户可以查看与好友的所有聊天记录。
2.3数据库设计
根据数据逻辑结构设计的情况,本系统数据库的数据表共有好友数据表、用户表、分组表3个,和一般数据库应用系统基本类似,这里不再赘述。
3系统的设计与实现
(1)用户列表模块。
该模块实现本局域网内平台中当前登录用户的列表显示,并显示当前用户通过搜索或好友添加功能添加的用户。在每个用户节点上右击鼠标,会出现Popup功能菜单项,其中菜单项的添加好友和删除好友都会控制好友列表节点的增加和删除。关键代码部分如下:
……
add(p,BorderLayout.NORTH);
add(new JScrollPane(table),BorderLayout.CENTER);
setBounds(100,100,200,600);
setVisible(true);
validate();
thread.start();
模块说明:
本模块主要采用了Swing技术、多线程技术和循环技术。在系统进程启动时,利用多线程技术启动线程循环——自动检测数据库已有好友数据信息,然后使用Swing提供的JTree类对象生成一个分层显示数据的视图即用户列表。JTree类中的基本对象叫作结点,它表示在给定层次结构中的数据项。树以垂直方式显示数据,每行显示一个节点。每个树中只有一个根节点,其他节点从根节点引出。除根节点外,其他节点分为两类:一类是带子节点的分支节点;另一类是不带子节点的叶节点。树节点由Javax.swing.tree包中的接口TreeNode来定义的,该接口被DefaultMutableTreeNode类实现。为了创建一个树,使用DefaultMutableTreeNode类为树创建节点。
通过建立一个存放用户姓名的标签对象Lable,该标签显示的内容由用户在登录时在登陆界面输入的用户名信息来确定,另外创建一个标签来存放“在线列表”,然后实质上存放在线列表里面的内容则有一个表格组件来存放所对应的在线用户,通过UDP数据报广播来获取在线的用户的姓名以及IP地址,然后将数组的数据导入表格组件中,就可以形成在线用户列表。
(2)聊天功能模块。
用户通过单击在线用户列表中的节点选择某一用户头像,即可触发聊天对话框界面。聊天对话框的左上部分为显示聊天记录。左下方为聊天输入框。右侧可以显示对方的用户名和IP等信息。本次聊天内容在窗口关闭时,重新打开后仍会保存,一旦整个系统重启之后,上次的聊天内容才会清除。
关键代码如下:
……
con.add(new JScrollPane(inMessage),BorderLayout.CENTER);
con.add(p,BorderLayout.SOUTH);
Thread thread=new Thread(this);
……
模块说明:在用户聊天界面上分别创建一个既用于接收信息又用于发送信息的多行纯文本域对象JtextArea,它们来分别存放要发送的信息和聊天信息。outMessage用来存放所要发送的数据,而inMessage用来存放聊天的信息。
(3)用户搜索功能模块。
搜索在线用户的设计是基于广播数据报的,通过采用组播地址然后创建广播套接字,设置一个广播的范围,在这里设置的是一个本地的局域网的范围。设置了广播范围之后加入广播组,就可以广播数据报和接收广播数据报。
关键代码如下:
……
group=InetAddress.getByName("10.192.168.0");
socket=new MulticastSocket(port);
socket.setTimeToLive(255);
socket.joinGroup(group);
和多数通行通信软件一样,发送端在某一端口广播数据,接收端在指定的端口或端口范围内侦听并接收广播数据。
DatagramPacket packet=null;
byte data[]=total.getBytes();
packet=new DatagramPacket(data,data.length,group,port);
System.out.println(new String(data));
socket.send(packet);
……
这里的Packet是某一进程中待发送的数据报,定义一个数组用来存放,并且设置了长度length,还有端口号port,最后通过socket在本地的局域网上进行广播数据报。通过广播自己的IP地址,让所有在线的用户接收到自己的IP地址并且添加到对方的在线列表中,这样每一个用户的一个广播就使得列表可以更新。
(4)点对点资源共享功能模块。
如上所述,为了保证系统通信的即时性,选择的是使用UDP数据报的一个点对点的通信方式,实现了两个客户端间进程间的通信,这里使用DatagramPacket方法创建数据报对象:
DatagramPacket(b,b.length,address,8604);//发送端口是8604
DatagramSocket mail=new DatagramSocket();
通过DatagramPacket为存放指定数据的数据报,其中包括了指定的数据,数据接收方的信息,并且要明确发送的目的地址address,同时指定侦听该通信的主机端口号为8604。而在接收的时候:
byte b[]=new byte[5120];
mail.receive(pack);
构建一个长度为5 120字节的数组用于保存接收到的数据,同时设计了一个参数Pack,可以把收到的通信数据报传递给参数Pack。
4结语
本通信器界面友好, 虽然与大型复杂即时通讯软件相比在功能上还稍有欠缺, 但是其作为即时通讯的主体功能已经具备,尤其是其难易程度适中,综合运用了Java 面向对象的多种知识,是高等院校《网络编程》课程的一个很好的实例。
当然由于诸多条件的制约,以及系统设计的定位,该设计在文件传输多样化、网络适应性、通信机制、数据存储与共享方面仍然存在较多不足,主要有以下几个方面:
(1) 实现多样化文件传输,如:图片、文档、音频、视频等。
(2) 初期对系统所采用数据库的通用性认识不足,后期可以采用Mysql或XML等主流数据存储技术,使得系统具有更强的适应性。
(3) 改进系统通信协议,考虑穿透防火墙、异构网络实现多局域网之间的通信。
(4) 用户界面应借鉴主流IM软件,实现良好的用户体验。
(5) 如果作为一个高性能企业内部即时通信软件,应考虑到大用户、多并发的情况,应尽可能减轻服务器负荷,尽可能地将一些处理、判断放在客户端进行,节约服务器端开销。
参考文献参考文献:
[1]陈立浩. 基于B/S和C/S的即时通信系统[J].计算机工程, 2009(15).
关键词:winsock;TCP/IP协议;计费管理系统
Winsock是一个ActiveX控件,它为采用客户机/服务器通信机制的网络提供了编制接口,使客户机端和服务器端藉此实现连接和数据交换。它不是一种网络协议,而是一套开放的、支持多种协议的Windows下的网络编程接口。Socket实际在计算机中提供了一个通信端口,可以通过这个端口与任何一个具有Socket接口的计算机通信。应用程序在网络上传输,接收的信息都通过这个Socket接口来实现。
本文主要研究了Winsock的工作原理、编程方法,TCP/IP协议下计算机的工作原理,基于TCP/IP协议的网络编程,服务器端与客户机端程序的设计等,通过解决这些问题来实现Winsock网络按时收费系统的设计。
一、系统设计方案的研究
我们所设计的网络计费系统分为三部分,一是在win32平台上的线程对数据包的截获,并且进行初步的整理,生成日志文件,作为前台;而后台则是在windows net server上的数据库管理,直接取得第一部分截取程序生成的日志文件和通过ftp获取在Linux服务器上其它服务生成的日志文件,对其分析,并至于以BDE驱动的数据库文件中。三是计费信息用户查询子系统,使得用户能在线通过权限查询到自己各个时间段内的计费信息和费用情况,有利于减轻网络管理员的工作负担。
Socket有3种主要类型:流式套接口,数据报套接口和原始套接口。面向连接服务器处理的请求往往比较复杂,原理是:服务器端不断监听客户端的请求,当客户端向服务器端发出连接请求并被服务器端检测到以后,服务器会接收客户端的请求,并建立连接。本文在方案选择上采用了在网络编程中最常用的一种模型--客户机/服务器模型。选取了基于TCP/IP的客户机/服务器模型和面向连接的流式套接字。
二、系统的软件设计
(一)服务器的设计
①在初始化阶段调用WSAStartup(),此函数在应用程序中初始化Windows Sockets DLL ,只有此函数调用成功后,应用程序才可以再调用其他Windows Sockets DLL中的API函数。②建立Socket,初始化WinSock的动态连接库后,需要在服务器端建立一个监听的Socket,为此可以调用Socket()函数用来建立这个监听的Socket,并定义此Socket所使用的通信协议。③绑定端口,为服务器端定义的这个监听的Socket指定一个地址及端口(Port),这样客户端才知道待会要连接哪一个地址的哪个端口,为此我们要调用bind()函数,该函数调用成功返回0,否则返回SOCKET_ERROR。④监听,当服务器端的Socket对象绑定完成之后,服务器端必须建立一个监听的队列来接收客户端的连接请求。listen()函数使服务器端的Socket 进入监听状态,并设定可以建立的最大连接数。⑤服务器端接受客户端的连接请求,当Client提出连接请求时,Server端hwnd视窗会收到Winsock Stack送来我们自定义的一个消息,这时,我们可以分析lParam,然后调用相关的函数来处理此事件。⑥结束 socket 连接,这一过程可以由服务器或客户机的任一端启动,只要调用closesocket()就可以,而要关闭Server端监听状态的socket,同样也是利用此函数。
(二)客户机的设计
①建立客户端的Socket,客户端应用程序首先也是调用WSAStartup() 函数来与Winsock的动态连接库建立关系,然后同样调用socket() 来建立一个TCP或UDP socket(相同协定的 sockets 才能相通,TCP 对 TCP,UDP 对 UDP)。与服务器端的socket 不同的是,客户端的socket 可以调用 bind() 函数,由自己来指定IP地址及port号码;但是也可以不调用 bind(),而由 Winsock来自动设定IP地址及port号码。②提出连接申请,客户端的Socket使用connect()函数来提出与服务器端的Socket建立连接的申请,函数调用成功返回0,否则返回SOCKET_ERROR。
三、基于TCP/IP协议的网络编程
TCP/IP协议实际上就是在物理网上的一组完整的网络协议。其中TCP是提供传输层服务,而IP则是提供网络层服务。
TCP/IP协议的核心部分是传输层协议(TCP、UDP),网络层协议(IP)和物理接口层,这三层通常是在操作系统内核中实现,因此用户一般不涉及。编程时,编程界面有两种形式:一是由内核心直接提供的系统调用;二是使用以库函数方式提供的各种函数。前者为核内实现,后者为核外实现。用户服务要通过核外的应用程序才能实现,所以要使用套接字(socket)来实现。
四、系统测试包括测试仪器和软件调试测试仪器
Agilent网络测试仪表和AutoRunner自动化软件测试工具。软件调试:由于本系统要实现的功能比较多,所以在编写程序和调试时出现了许多问题,不过经过多次反复的调试后还是能把题目所要实现的功能都能实现了。
五、结束语
基于Winsock的编程,可以比较容易的实现按时收费系统的设计。利用Winsock编程的系统界面友好、操作简便、功能全面。这也就说明Winsock编程的优点,它将会成为软件开发的重要工具!
参考文献:
【关键词】I/O NIO BIO SOCKET通信 Java 多线程
1 引言
传统BIO技术在Socket通信中,系统需要为每一个链接建立一个线程去处理其请求,随着客户端的并发量不断增加后,会导致线程数量的增加严重影响系统的性能。由于并发量的增加有可能导致服务器宕机,严重影响到用户在使用过程中的良好体验。为解决传统BIO的不足,Java 中提供了新的API----NIO和NIO2来解决由于BIO技术带来的系统瓶颈问题。在NIO中系统不再为每一个用户请求注册一个线程,而是通过通道将每一个链接都注册到多路复用器上,通过多路复用器对注册在其上的链接进行轮询检查,发现有链接请求才会开启线程对其进行处理。NIO只在有连接请求时selector才会不断轮询检查通道IO操作是否完成,与NIO技术不同的是AIO技术是异非步阻塞的。AIO中不再需要多路复用器,而是由异步非阻塞通道直接操作read和write方法。在客户端读写请求发出后不再等待服务器的响应,而是处理完成后由操作系统来通知应用程序。AIO与NIO这两种技术都极大地改变了传统I/O流的不足。
2 Socket基本通信原理介绍
Socket是网络通信中的其中一方,用来接收网络通信中双方其中一方的请求,方便的对双方的数据进行传输。Socket通信分有连接的和无连接,面向连接的Socket通信与面向非连接的Socket通信相比有更高的可靠性和更有效的数据传输。本文基于有连接的套接字传输。
3 BIO、NIO、AIO技术比较
3.1 BIO技术简介
BIO技术同步并且阻塞,在这种情况下,服务器需要为每一个连接开启一个线程,只要有客户端有请求服务器就需要开启线程去进行处理。从客户端传来的每个请求,服务器都需要为其创建相应的线程去处理其请求。在BIO中,由于其线程的开销很大,适合于运用在并发量小的场景下。其基本模型如图1所示。
BIO网络通信基本步骤:
3.1.1 服务低端
(1)创建ServerSocket并绑定监听端口;
(2)创建Socket用来接收客户端请求;
(3)创建输入输出流用来接收客户端输入或向客户端输出数据;
(4)关闭输入输出流等系统资源。
3.1.2 客户端
(1)创建Socket绑定IP地址及端口;
(2)创建输入输出流用来接收服务器端相应或向服务器端发送数据;
(3)关闭输入输出流等系统资源。
3.2 NIO技术简介
NIO技术即New IO技术,NIO技术由很多类和组件构成,其最重要的由channel、Buffer、和Selectors三个核心部分组成。
Channel:Java NIO中的通道类似于流,但又不完全相同。既可以从通道中读取数据到Buffer也可以将数据从Buffer写入通道中。其中SocketChannel和ServerSocketChannel是NIO中提供的用来解决Socket通信中的服务器性能问题的。SocketChannel通过TCP协议来读取网络中的数据,ServerSocketChannel用来接收链接来的请求以供服务器相应。
Buffer:Buffer是用来为数据提供缓冲区的。在NIO技术中,所有的数据都必须经过缓冲区。缓冲区本质上为一块可读可写的内存块,NIO中提供了不同数据类型的缓冲区来处理不同的数据请求,和一些基本的方法来操作缓冲区中的数据。
Selector:Selector是单线程来处理多个链接请求的关键。在Socket通信中,如果将多个链接请求注册到多路复用器上,就可以用一个线程来处理多个链接请求,这样就提高了Socket通信的效率。NIO通信中的网络模型
如图2所示。
NIO通信基本步骤:
3.2.1 服务器端
(1)创建多路复用器Selector 用来选择通道;
(2)创建服务器端通道ServerSocketChannel;
(3)为多路复用器上注册ServerSocketChannel用来将数据通过通道读写;
(4)申请Buffer存储数据;
(5)多路选择器通过其key值轮询检查通道读写Buffer中的数据完成其通信过程。
3.2.2 客舳
(1)创建IP地址和端口号对应的SocketChannel ;
(2)将SocketChannel设置为非阻塞模式;
(3)创建多路复用器Selector注册SocketChannel到多路选择器;
(4)多路选择器轮询检查通道从Buffer中读写数据。
在NIO网络通信模型中,客户端向服务器端发起链接请求,客户端将数据写入服务器端Buffer中,然后通过channel来从Buffer读取数据或向Buffer写入数据,对于通道中的数据必须经过选择器来向服务器端的线程发起请求。而每一个多路选择器对应一个线程模型,这样一来,只有当链接请求有效时服务器才为客户端开启线程处理数据。对于同步阻塞的NIO模型中客户端在向服务器端发送完数据后会不断询问I/O操作是否就绪才能进行下一步的操作。此模型中由于不需要为每一个链接请求创建一个线程,大大减少了线程之间的切换带来的巨大开销。提高了I/O的效率,使得在socket网络通信更加高效。但由于NIO在链接请求中会不断询问I/O操作是否完成,其适合运用在短链接且并发量大的场合下。
3.3 AIO技术简介
AIO即为异步非阻塞IO,与NIO不同的是,在这种模式下,客户端发起一个链接请求后不在询问服务器端的I/O操作是否完成便立即返回。在使用过程中只需直接调用异步的read和write方法来读写数据,在读写过程完毕后由操作系统主动通知应用程序读写操作是否完成。由AIO的读写过程可以看出,因为在读写完成后客户端不需要再询问服务器端是完成了I/O操作,所以AIO非常适合于运用在那些并发量大且长连接的请求。AIO模型如图3所示。
AIO中有几个比较重要的类:
AsynchronousServerSocket:用来创建服务器端的ServerSocket并绑定地址监听端口。
AsynchronousSocketChannel:Socket在异步非阻塞通信中的应用,用来表示一个连接请求,并用来在通信过程中传递数据。
AsynchronousChannelGroup:异步通道的分组管理,目的是问了资源共享。创建AsynchronousChannelGroup时需要为其绑定一个线程执行器对象,这个线程池主要完成两个任务:处理I/O事件和派发CompletionHandler。在创建AsynchronousServerSocket时需要为其绑定一个AsynchronousChannelGroup。通过AsynchronousServerSocket创建的AsynchronousChannelGroup将属于同一组,共享其中资源。
CompletionHandler:用于定义在异步IO操作完成后的回调接口。
3.3.1 服务器端
(1)创建AsynchronousChannelGroup;
(2)创建AsynchronousServerSocketChannel并将它绑定在AsynchronousChannelGroup上;
(3)为AsynchronousServerSocketChannel对象绑定端口号;
(4)调用accept()接收客户端请求实现CompletionHandler接口调用读写方法进行读写数据。
3.3.2 客户端
(1)创建AsynchronousSocketChannel;
(2)绑定IP地址和端口号链接服务低端实现CompletionHandler接口中方法直接进行读写数据。
3.4 各种I/O技g比较分析
BIO:同步阻塞I/O,使用难度简单,可靠性低,适用于链接并发量小的架构且对服务器资源依赖更高。
NIO:同步非阻塞I/O,使用难度复杂,可靠性高,适用于并发量大且链接较短的场景。
AIO:异步非阻塞I/O,使用难度一般,可靠性高,适用于并发量大且链接长的场景。
4 结语
传统网络在IO处理方面存在着性能不足的问题,NIO与AIO包的引入解决了传统IO性能瓶颈问题,使的Java在网络通信中有了更搞得效率。对于不同的IO来说,它们各自有有不同的应用领域。对于那些并发量小,数据传输量小的场景来说,传统的BIO完全可以胜任其工作。相反对于那些对性能要求比较高,并发量大且对IO的要求比较搞得场合来说应该选择NIO或者BIO技术。如果客户端与服务器在长连接的情况下选择AIO相比NIO会更加高效一些。相反,如果是短连接的应用领域,则推荐使用NIO。
参考文献
[1]范宝德,马建生.Java非阻塞通讯研究[J].微计算机信息,2006,22(12-3):116-119.
[2]刘邦桂,李正凡.用Java实现流式Socket通讯[J].华东交通大学学报,2007,24(05):110-112.
[3]封玮,周世平.基于Java NIO的非阻塞通信的研究与实现[J].计算机系统应用,2004(09):32-35.
[5]任小强,陈金鹰,李文彬,胡波.网络通信之Java Socket多线程通信[J].信息通信,2015(06):206-207.
作者简介
王少辉(1992-),男,山西省平顺县人。硕士学位。现为山东大学(威海)机电与信息工程学院学生。主要研究方向为电路与系统。
陈晓鹏(1992-),男,天津市人。硕士学位。现为山东大学(威海)机电与信息工程学院学生。主要研究方向为电路与系统。
关键词:语音集群通信;移动网络;SSL Socket;opus
中图分类号:TP393 文献标识码:A 文章编号:1009-3044(2016)27-0014-04
Abstract: The paper analyzes the specific business demands and technical requirements of the industry users in the voice cluster communication, and gives a solution of voice cluster communication system for the mobile network environment. It designs the underlying transmission framework based on SSL Socket secure link and data transmission format, puts forward the basic method to establish interactive applications, according to the features of mobile network environment, it analyzes and uses the reasonable audio codec library, designs audio mixing algorithm, solves the core problem of the voice cluster communication system.
Key words: voice cluster communication; mobile network; SSL Socket; opus
随着公共网络服务和多媒体通信技术的发展,利用移动终端进行语音实时通信已成为民用市场或行业应用的热门业务[1]。相比较传统的电话语音业务,基于IP网络的语音通信系统具有跨地域、低成本、高质量、可定制等优势,且随着移动网络建设的不断升级,语音通信系统的通话效果和时延已与电话语音的差别不大。目前,市场上主流的语音通信应用有两类:一是采用频道、群组或聊天室形式的多人语音通信,如yy语音、qt语音等;二是采用一对一通话模式的IP网络电话,如Skype等。微信作为国内影响力较大的即时通信软件,也提供了一对一的实时语音功能。
对于满足专有行业或企业的语音集群通信业务[2],通常还需要考虑以下需求:1)群组通话是集群通信的主要业务模式,具有群组信道共享,避免通信互扰等服务要求;2)对于室外作业多采用移动网络,而非连接到有线网络共享的AP热点,网络质量受限。3)VPDN专网服务支持或其他网络安全要求;4)在语音业务基础上,提供文本通信、文件传输等定制功能;5)对语音、文本、共享文件等关键信息的私有存储或全业务存储。因此,在设计开发基于移动网络的语音集群通信系统时,应充分考虑行业应用特点,预留必要的业务应用接口。
1 基本传输框架
语音集群通信系统基于标准的客户端-服务器通信模型设计。客户端与服务器之间的数据传输采用TCP/UDP两种协议,分别适用不同的网络环境。当移动网络质量较好时,可以根据设置使音频数据通过TCP通道传输,保证通话语音的完整性和可靠性;当网络条件较差时,使用UDP通道传输可以避免TCP传输多次握手导致的网络拥塞,实现低延时传输,保证系统的可用性。语音通信过程中的控制信令和通信信令则是基于TCP协议可靠传输[3]。
1.1 构建安全通道
安全套接层(Secure Socket Layer,SSL)是Netscape公司研发的用于在IP网络上实现数据安全传输的专有协议,通过数据加密技术确保数据在网络传输过程中不会被截取及窃听。SSL及其后续发展的传输层安全(Transport Layer Security,TLS)提供了传输层的数据完整性保护,包括身份认证、协商加密算法、交换加密密钥等[4]。OpenSSL是互联网上适用性最广泛的SSL密码库之一,提供了多种编程语言的库支持。本系统引入OpenSSL 1.0.2方法库,将TCP Socket替换为SSL Socket,对传输层协议进行链路安全保护[5]。
SSL Socket的建立流程是:
1)客户端请求建立SSL Socket连接,并向服务器发送SSL版本、加密参数等必要信息。服务器返回自身的SSL版本、加密参数、安全证书等必要信息。客户端向服务器提供认证证书。
2)客户端验证服务器证书后,生成pre-master secret,并用公钥加密后发送给服务器。服务器对证书进行认证,通过后用私钥解密pre-master secret,并生成master secret。
3)通信双方通过master secret生成会话密钥,完成SSL Socket的创建,之后的通信数据将通过会话密钥加密传输。在本系统中,会话密钥采用TLSv1 AES256-SHA加密。
由于UDP是面向无连接的传输协议,为保证数据传输安全,在发送函数sendto()之前加入encrypt(),这里可采用与TLSv1 AES256-SHA强度相当的加密算法进行数据加密。接收侧在recvfrom()之后加入decrypt(),进行数据解密。
1.2 数据传输格式
本系统的数据报文格式如图1所示。客户端与服务器之间遵循此格式发送交互信息,其中报文头Ptrfix包含1个字节的类型信息,区别信令数据或语音数据,提供长度信息进行组包校验。载荷部分Data为实际交互数据,采用TLSv1 AES256-SHA或其他算法加密。由于UDP协议仅用于发送语音数据,其数据传输格式为图1的Payload部分,最大长度为UDP分片的理论最大长度65507B[6]。
2 应用流程
2.1 用户接入
本系统的接入流程如图2所示,主要包括:
1)建立连接:客户端向服务器请求建立用于控制信令和TCP数据通信的SSL Socket长连接。
2)版本确认:客户端与服务器相互发送版本信息,包括软件版本、操作系统版本、其他备注信息等,双方根据版本判断系统访问的兼容性。
3)用户登录:客户端向服务器发送登录认证信息,包括登录账户、登录密码,或是用于登录认证的证书信息。
4)密钥交换:该步骤为可选项,由于SSL Socket完成了TCP连接的密钥交换,如系统支持UDP传输,则需要服务器发送UDP加密的密钥信息给客户端。
5)群组信息:服务器向请求登录的客户端发送群组的状态信息,包括群组ID、名称、描述、拓扑关系,以及群组中用户的在线状况和当前状态。
6)用户信息:服务器向其他客户端发送新登录用户的状态信息,通知该用户已上线。
7)心跳信息:客户端完成基本登录流程,通过发送周期性的心跳报文,维持长连接状态。根据实际网络状况可调整心跳报文的发送周期,如部署地域的网络状态较好,可采用3至5秒的发送间隔。
2.2 信息响应
作为集群通信系统,信息响应的基本单位是群组。每个群组的数据处理和信息状态相对于其他群组保持独立,即群组之间的数据和资源是隔离的,这里通过建立用户信息的HashMap结构hmUsers和群组信息的HashMap结构hmGroups进行数据调用和处理。调用用户或群组对象时,需通过ReadWriteLock进行资源锁定,保护线程安全[7]。考虑到多线程存在资源切换和锁定的开销,在设计上主要对不同类型或分组的任务建立线程,如图3所示。其中:
1)接收线程主要响应网卡资源,监听服务端口,接收由客户端发送的业务数据或信令信息,如果是心跳消息,则直接返回响应;否则,将通过hmUsers和hmGourps查找所属群组,并将数据推送到指定群组的消息缓冲区中。
2)处理线程主要响应和实现具体业务。每个群组对象在实例化过程中都会创建本群组的处理线程和数据缓冲区,通过提取缓冲区中的数据并解析,线程将信令或其他业务数据交给业务响应函数处理,语音数据交给数据响应函数处理,并将处理后的数据发送给指定客户端。在处理线程中,可分模块响应语音业务以外的请求,对预留接口进行功能实现。
3)存储线程主要响应数据I/O操作。本系统对独占性的I/O操作采用单线程序列化处理,即所有群组的语音数据通过统一的缓冲序列顺序解码、混音和写入文件,降低线程切换的开销。为提高I/O读写效率,语音数据不会立即写入文件,而是积累时长2秒(可根据实际情况配置)的数据包后批量写入。
3 语音处理
3.1 音频编解码器选型
目前,国内4G LTE网络建设日趋完善,基于移动网络的语音通信技术已摆脱GSM时期小于16kbps的窄带传输限制。在音频编解码技术选型上,更多考虑的是适应16kbps到64kbps区间的高品质音频编解码算法。
本系统主要对Opus、Speex、AMR-WB和G.722.1四种编解码方案进行比较。Opus是在Skype的SILK编解码器和的CELT编解码器基础上发展的开源编解码方案,已形成RFC 6716标准,具有灵活的带宽适应性。Speex是基于CELP发展的音频编解码方案,但根据Speex官网提示,Opus的性能已在各方面优于Speex。AMR-WB(G.722.2)被广泛应用在WCDMA的话音业务上,其VBR特性可以较好的适应网络带宽变化。G.722.1提供了优于G.722的24kbps和32kbps音频编码压缩。
根据Opus-Codec给出的音频编码质量统计对比(图4),以及两份Google组织的主观评测报告[9][10],在同等比特率条件下,Opus编码能够达到或超过AMR-WB、G.722.1的编码质量。且考虑到国内各地区移动网络的传输质量差异较大,支持6kbps到510kbps比特率和VBR技术的Opus编解码器可为不同网络环境提供更稳定的语音通话效果。
3.2 Opus编解码实例
Opus是基于C语言编写的音频编解码器,Windows平台可直接加载Opus源代码进行函数调用,Android平台还需要对源代码进行NDK编译,生成供Java语言调用的so库。
使用Opus进行语音编码的代码片段如下:
OpusEncoder *opus_enc = opus_encoder_create(16000,1,OPUS_APPLICATION_VOIP, &err);
…
opus_encoder_ctl(opus_enc, OPUS_SET_BANDWIDTH(OPUS_BANDWIDTH_WIDEBAND));
opus_encoder_ctl(opus_enc, OPUS_SET_BITRATE(24000));
opus_encoder_ctl(opus_enc, OPUS_SET_VBR(1));
opus_encoder_ctl(opus_enc, OPUS_SET_FORCE_CHANNELS(1));
opus_encoder_ctl(opus_enc, OPUS_SET_DTX(0));
…
while (total_length - input_pos > frame_bytes) { //仅处理采集片段,而非长期占用线程
…
get_data(input_buf, input_pos);
num = opus_encode(opus_enc, input_buf, frame_size, output_buf, max_output_size);
send_data(output_buf, num);
… }
…
opus_encoder_destroy(opus_enc);
3.3 混音流程
多路语音的数据混音是语音集群通信的核心功能。通过混音算法和调平参数,同一群组内的多路通话语音将根据逐帧数据的时间戳进行合帧处理,形成一路语音数据,送入声卡缓冲区进行放音。不论语音数据是以8位、16位或浮点形式存储,在多路混音时仅做逻辑叠加运算将会导致数据越界,必须先通过调平参数进行数据调整,之后根据叠加系数按比例缩减,保证合帧后的语音数据不会越界[11][12]。
混音操作的代码片段如下:
mixlocker.lock();
…
foreach(audio_source as, mixlist) {
source_buf = as.getbuf();
level = volume_level * generate_adjust(); // 设置调平参数
…
for (unsigned int i = 0; i < sample_num; i++)
mix_buf[i] += source_buf[i] * level;
… }
…
for (unsigned int i = 0; i < sample_num; i++) // 数据越界保护
mix_buf[i] = bound(mix_buf[i], -32768, 32767);
…
mixlocker.lock();
4 实验与分析
本系统分别在10M专线、联通4G、移动4G、电信4G等网络条件下进行测试,测试结果如表1所示。
在不同网络环境下,语音数据传输的平均延时在可接受范围,UDP丢包率小于2.2%,网络抖动小于20ms。通过主观测试,语音通话连贯,无连续丢帧,可懂性良好。因此,语音集群通信系统在传输延迟、网络抖动等方面可以较好满足语音集群和实时通信要求。在编码处理方面,优于国际电联G.711语音编码的90kbps带宽占用。
5 结束语
本文主要根据移动网络环境下的语音集群通信需求,给出一套可行的系统解决方案,并对其中底层链路、应用构建、核心编码处理等关键环节进行详细阐述。在实际应用过程中,不同领域的业务单位可能对系统的数据存储、用户负载、网络安全有更高的业务需求,可以通过合理配置磁盘阵列、负载均衡、安全网关、服务器等硬件设备或软件服务来实现。
参考文献:
[1] 2015年中国移动语音社交应用行业研究报告[R]. 上海: iResearch, 2015:19-23
[2] 王芳. 数字集群通信系统的构成及功能[J]. 电信网技术, 2005(2):9-12.
[3] Chauncey D, Kuliner M. Secure wireless communications system and related method: WO, US7987363[P]. 2011.
[4] 曾强. 网络安全协议SSL原理及应用[D]. 天津:天津大学, 2005.
[5] 秦贞虎. 基于OpenSSL开发的聊天工具的设计与实现[D]. 成都:电子科技大学, 2013.
[6] 李一鸣, 任勇毛, 李俊. 基于UDP的传输协议性能比较与分析[J]. 计算机应用研究, 2010, 27(10):3906-3910.
[7] 赵兴. 基于VoIP技术的无线语音通信系统设计[D].长沙: 湖南大学, 2011.
[8] opus-codec. Quality vs Bitrate[EB/OL]. http:///comparison/.
[9] opus-codec. Google listening tests[EB/OL]. http:///comparison/GoogleTest1.pdf.
[10] opus-codec. Google listening tests[EB/OL]. http:///comparison/GoogleTest2.pdf.