带网络功能的多媒体播放器

来源:互联网 发布:会计软件和财务软件 编辑:程序博客网 时间:2024/05/01 01:56

 

 

 

设计题目JAVA网络视频点播系统

    别:   计 算 机 学 院  

    级:  07计算2      

    号:  07xxxxxxxx        

    名:  cgxinyan          

指导老师:  …… 老 师      

小组成员:cgxinyan、幸福的猪、水清蓝

 

yy北工业大学

                                              00518

 


目录

 

摘要--- I

第一章      前言--- 1

第二章      概要设计--- 2

2.1        开发环境--- 2

2.2        播放器功能--- 2

2.3        界面设计--- 2

第三章      详细设计--- 6

3.1     系统入口--- 6

3.2        系统主界面--- 7

3.2.1       菜单栏--- 7

3.2.2       播放窗口--- 8

3.2.3       播放器状态栏--- 8

3.2.4       播放清单--- 9

3.2.5       截屏显示--- 9

3.3     流程图--- 9

3.4     网网络点播功能设计--- 10

3.4.1       概述--- 10

3.4.2       网络功能设计--- 10

3.4.2.1        服务器--- 10

3.4.2.2        客户端--- 11

3.4.3       网络功能的详细设计--- 11

3.4.3.1        rtp协议实现实时媒体信息的网络传输--- 11

3.4.3.2        java socket实现客户端和服务器的通信--- 14

第四章      运行结果--- 21

第五章      测试分析--- 23

第六章      参考文献--- 32

第七章          --- 33


摘要

该程序是一个利用jmf实现的图形界面的简单的播放器,具有良好的界面,使用人员能快捷简单地进行操作。该播放器可以实现   该系统是一个图形界面的简单的网络(单机)视频点播系统,具有良好的界面,用户能快捷简单地进行操作。本系统主要实现常见本地视频文件的播放和局域网电影点播的功能,播放器还包含截屏功能。本系统采用java语言开发, 通过jmf实现多媒体文件播放和实时传输功能,java socket网络编程技术实现系统的网络部分功能。实时流媒体传输采用RTP协议。

 

关键字:javajmfjava socketRTP协议、播放器、多媒体、流媒体、实时传输、视频点播系


第一章   前言

随着计算机技术和Internet的不断发展,网络视频点播系统越来越受到人们的亲睐。网络视频点播系统是计算机技术、网络通信技术、多媒体技术、电视技术和数字压缩技术等多学科、多领域融合、交叉、结合的产物。

网络视频点播,也称为交互式电视点播系统。传统的电视系统信息,单向传送,用户只能被动接收,而我们将要开发的网络视频点播系统是以“用户自主”的全新概念的基础的双向视音频信息系统,实现了用户可以自主选择本地和在线的自己喜欢的视音频节目,而且还可以截取自己觉得美好的画面。

本视频点播系统根据流媒体传输原理,RTP实时传输协议,模拟基于JMF的网络视频点播系统,主要实现常见本地视频文件的播放和局域网电影点播的功能,播放器还包含定时截屏功能。通过JAVA实现,主要论述了客户端(Client)和服务器端(Server)的视频服务平台搭建,流媒体相关原理,以及相关功能的具体实现。

本次课程设计主要阐述了基于JMF的交互式网络视频点播系统的协议原理,软件结构和设计实现。


 

第二章   概要设计

2.1         开发环境

开发平台:Microsoft Windows XP Professional Service Pack 3

开发工具:JDK 1.6+ JMF1.1JAVA多媒体框架)+ ECLIPSE

2.2         播放器功能

基本功能:

1、在本地实现mpgmovauavi等常用音视频格式的播放。

2、通过菜单设定定时截取视频成静态图像并按照jpg格式保存到制定目录,并在播放视频的同时展开这些图像文件,动态扩展。

3、设定常用快捷键,方便用户使用。

高级功能:

在局域网内按照C/S架构开展网络视频点播

2.3         界面设计

1 客户端主界面

2 打开某文件

3 截屏

4 输入服务器IP

 

5 网络连接成功

6 服务器界面


 

第三章   详细设计

 

类结构设计:

 

整个网络视频播放器由客户端(client)和服务器端(server)两部分组成。由于我们组三人各有分工,我主要负责网络部分的功能,即实现网络点播和实时数据流的接收并播放以及客户端和服务器的通信功能,所以在这里我将详细介绍有关网络部分的详细设计。截屏功能由我们小组的水清蓝同学完成,本地文件的播放主要有小组的幸福的猪完成。

 

3.1     系统入口

文件main.java,主要实现整个播放器的入口功能,通过调用main.java来进入播放器主界面,以实现播放器的各个功能。Main.java文件内容如下:

 

package ourplayer.client;

 

import javax.swing.UIManager;

 

public class Main {

 

    /**

     * @param args

     *            the command line arguments

     */

    public static void main(String args[]) {

       try {

           UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

       } catch (Exception e) {

       }

 

       ourplayer.client.gui.MainFrame mainFrame = new ourplayer.client.gui.MainFrame();

    }

 

}

 

 

3.2         系统主界面

系统主界面是在文件MainFrame.java里实现的。主界面是通过frame容器展现,并通过给各frame容器添加监听来实现各部分功能。

3.2.1   菜单栏

播放器菜单栏位于整个播放器的上部,他是播放器各功能的入口。通过选择菜单栏各菜单项,实现播放器的各个功能。其界面如下:

菜单栏

 

3.2.2   播放窗口

播放窗口放置在整个界面的中央,用以实现视频/音频的播放及显示功能,其界面如下:

播放视频窗口

播放音频文件

 

3.2.3   播放器状态栏

播放器状态栏用于显示当前播放器的状态(网络状态或者单机状态),其界面如下:

单机状态

网络状态

3.2.4   播放清单

播放清单放置在整个界面的右边,用来列出当前目录下所有符合要求的音视频文件,其界面如下:

播放清单

值得一提的是,在实现播放清单功能时,通过“文件”菜单下面的“打开”按钮,在本地目录找到需要播放的音视频文件过后,会把当前目录下的所有符合条件(即指可以播放的文件格式)的文件一起加入到播放清单里面。并且在添加之前清空原来的播放列表。

3.2.5   截屏显示

在整个界面的最下方有一个区域用来存放截屏的缩略图,其界面如下:

截屏缩略图显示

 

3.3     流程图

Ø  整体程序流程图

 

3.4     网网络点播功能设计

3.4.1   概述

网络点播功能由两个大的模块组成,他们分别是:1、网络点播客户端;2、网络点播服务器。在本课程设计的成品软件中已经将服务器作为播放器的一个功能集成到客户端了。点击播放器的“服务器”菜单或者按下快捷键“ctrl + S”来启动服务器。

3.4.2   网络功能设计

3.4.2.1   服务器

在播放器主界面启动服务器程序,在服务器端通过“文件”菜单的打开项来设定提供媒体文件的文件夹。这样,服务器就可以将这里面的文件提供到网络上,供网络上的客户端使用。同一台机子在同一时刻只允许运行一台服务器。服务器运行的端口为10010。服务器可以同时接受很多客户端的连接请求并给出实时响应。当客户端一旦连接成功,服务器就将可提供实时媒体传输的文件列表发送给客户端。服务器还能响应客户端的点播请求,并使用rtp协议讲媒体数据发送到客户端。

3.4.2.2   客户端

客户端能连接到指定IP的服务器,在右侧将网络连接的状态实时的显示出来。当收到服务器发送来的媒体列表后将他们显示在右侧的“播放清单”中。用户可以通过单机媒体的名字来向服务器请求媒体的点播。播放器屏蔽了网络连接的底层实现细节,给用户提供清爽的界面,简洁的操作和强大的功能。

3.4.3   网络功能的详细设计

3.4.3.1   rtp协议实现实时媒体信息的网络传输

本功能主要采用jmf提供的rtp协议实现的api完成。使用rtp协议的网络多媒体应用程序,分为两个部分1、负责通过网络发送数据的主机端,2、接收数据的客户端。

Ø  Rtp协议会话框架实现实时媒体的发送端

一个rtp协议会话通过一个IP和一对端口号来标识,一个端口传输媒体数据,另一个传输控制数据。每个不同的媒体类型都采用不同的会话来传输,且他们是独立传输的,如果在进行带音频的视频传输还要进行音视频同步,不过幸好这些繁琐的工作jmf都已经为我们做了。

采用jmf提供的rtp实现来进行媒体数据的网络传输步骤:

(1)   从本机选择待发送的媒体文件,获取数据源,并通过获取的数据源构造媒体定位器。

(2)   将数据以rtp流的形式发送出去

(3)   产生一个jmf处理器(Process)为每一种rtp格式设置相应的轨迹

(4)   从处理器获得输出数据源(输出到网络中的数据源)

(5)   会话管理器(SessionManager)产生一个发送数据流。

(6)   开始会话传输

(7)   通过监听ControlerEvent时间来控制会话过程

(8)   停止会话,删除会话管理器

下面是实现媒体传输的关键代码(由于代码较多,这里只放核心代码)

public void start() throws NoProcessorException, NoDataSourceException,

           CannotRealizeException, IOException,

           InvalidSessionAddressException, UnsupportedFormatException {

       /** ---------获得一个处理器------ */

       this.getProcessor();

       /** ---------产生RTP会话------ */

       this.getTransmit();

       processor.start();

}

 

Ø  Rtp协议会话框架实现实时媒体的接收端

实时接收网络媒体数据流失通过各种rtp时间监听器和rtp事件处理类来实现的,而jmf提供的Player接口就可以实现媒体数据流的播放

接收并且播放媒体数据流步骤:

(1)      实现ReceiveStreamListener监听接口监听接收数据的事件

(2)   通过事件获取接收媒体数据流,然后通过接收媒体数据流获取数据源

(3)   同过捕获的媒体数据流产生播放媒体的播放器

(4)   给播放器添加监听来实现事件的监听

关键代码如下:

       rtpManagers = new RTPManager[sessionMessage.length];

       for (int i = 0; i < rtpManagers.length; ++i) {

           session = new SessionLabel(sessionMessage[i]);// 解析RTP会话地址

           ipAddr = InetAddress.getByName(session.ip);// 获得RTP会话IP

           rtpManagers[i] = RTPManager.newInstance();// 产生RTP管理器实例

           rtpManagers[i].addSessionListener(this);// 添加会话监听

           rtpManagers[i].addReceiveStreamListener(this);// 添加接收监听

           if (ipAddr.isMulticastAddress()) {// 为组播地址 本地会话地址和目的会话地址一致

              localAddr = new SessionAddress(ipAddr, session.port);

              destination = new SessionAddress(ipAddr, session.port);

           } else { // 否则

              localAddr = new SessionAddress(InetAddress.getLocalHost(),

                     session.port);

              destination = new SessionAddress(ipAddr, session.port);

           }

           rtpManagers[i].initialize(localAddr);// 本地会话地址初始化RTP管理器

           bufferControl = (BufferControl) rtpManagers[i]

                  .getControl("javax.media.control.BufferControl");// 通过会话管理器获得缓冲管理器

           bufferControl.setBufferLength(1024);// 设置缓冲大小

           rtpManagers[i].addTarget(destination);// 加远程地址

    }

 

@Override

public synchronized void update(ReceiveStreamEvent arg0) {

    // TODO Auto-generated method stub

    RTPManager rtpManager;

    Participant participant;

    ReceiveStream receiveStream;

    DataSource dataSource;

    RTPControl rtpControl;

   

    rtpManager = (RTPManager) arg0.getSource();//得到管理器

    participant = arg0.getParticipant();//得到加入者

    receiveStream = arg0.getReceiveStream();//得到接收者

   

    if(arg0 instanceof NewReceiveStreamEvent){//获得的数据流是新的数据流

       receiveStream = ((NewReceiveStreamEvent)arg0).getReceiveStream();//获得新数据流

       dataSource = receiveStream.getDataSource();//通过数据流获得数据源

       rtpControl = (RTPControl) dataSource.getControl("javax.media.rtp.RTPControl");//通过数据源创建控制器

       try {

           player = Manager.createRealizedPlayer(dataSource);

       } catch (NoPlayerException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       } catch (IOException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       } catch (CannotRealizeException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       }

       player.addControllerListener(this);

       player.realize();

      

       dataReceived = true;

       // 通知initialize()函数中的等待过程:已经接收到了一个新数据流

       synchronized (dataSync) {

           dataReceived = true;

           dataSync.notifyAll();//唤醒线程

       }

    } else if (arg0 instanceof StreamMappedEvent) {// 数据流映射事件

       if (receiveStream != null && receiveStream.getDataSource() != null) {

           dataSource = receiveStream.getDataSource();

           rtpControl = (RTPControl) dataSource.getControl("javax.media.rtp.RTPControl");

       }

    } else if (arg0 instanceof ByeEvent) { // 数据接收完毕

       player.close();

       JOptionPane.showMessageDialog(null, "数据接收完毕");

    }

}

 

3.4.3.2   java socket实现客户端和服务器的通信

服务器端建立一个ServerSocket对象并且绑定端口10010然后就等待客户端的连接,一旦接收到某个客户端的连接请求,就为该客户开启一个新的线程来处理通信。这里采用多线程技术是十分必要的,试想一下同时有N个客户端同时发起了连接请求,如果采用单线程则这N个客户端就需要排队等待,直到前一个连接的客户端自动退出连接,否则其他请求只有继续等待。这种情况是不允许出现的,幸亏java的多线程机制完美的解决了这个问题。

客户端连接服务器需要知道服务器的IP和它所使用的端口号。端口号这里设定为10010。另外,客户和服务器通信需要遵循他们特别约定的通信协议。比如,服务器要知道客户端像他请求的服务是什么,他要做出什么动作来对请求进行响应。而这些协议是没有现成的,这部分工作是客户端和服务器端通信的主要工作。

客户端请求连接,服务器个出相应。当连接建立成功之后他们之间就可以自由通行啦。下面就对通行过程遵守的协议描述如下

由于URL和电脑中的路径都不会出现“>”字符,所以各个不同的信息段采用“>”符号来做分隔符,每条通行信息以回车换行(“/r/n”)来结束

客户端像服务器端发送“get media list”命令来获取没列表

如果服务器有可提供的媒体文件,则发送list命令

list命令格式:“list>文件名>文件名>.....>”(“>”用于隔开每一个不同文件,“list”说明给出的响应是媒体列表)

s

如果服务器端无媒体文件响应:“error>

 

客户端向服务器请求媒体,也就是实现点播。客户端一次只能点播一个媒体数据,显示的给出IP是为了处理的简单。

请求媒体数据命令:“request media data>”加上要请求媒体文件名 最后还要给出客户IP

服务器响应:首先检查命令格式是否正确,如果正确则获得发送端口然后将自己的IP和端口一并发送给客户端,并给出“media well send”命令

media well send格式:“media well send>服务器IP/发送端口”

s

如果检查出客户端发来的命令有错误,则给出错误响应指令“error>no target

 

客户端一旦接收到media well send”命令就表示服务器已经做好了数据发送的准备工作,现在就是通知客户端打开接收器进行接收。客户端新建一个线程来监听并且接收数据流,并设定超时值。当客户端准备好这些工作后就像服务器发送“receive done”指令

服务器受到receive done指令后才开始真正的发送数据,并且发送数据的线程是新开辟的。因为服务器要对客户端的请求产生实时响应,这样就不能产生阻塞。发送的参数都由这个指令给定。一旦发送出现错误就发送error指令通知客户端及时取消接收。发送成功则发送media is sending命令通知客户端一切正常,此时客户端不做任何回应。服务器线程继续监听客户端指令。

下面给出协议实现的主要代码:

Ø  客户端

    /**

     * 内嵌的网络客户端

     *

     * @author Administrator

     *

     */

    class Client {

       private static final int BUFSIZE = 2048;// 接收缓冲

       int servPort = 10010;// 与点点播服务器连接的默认端口--10010

       OutputStream out;

       InputStream in;

       Socket socket;

 

       public Client(String server) throws UnknownHostException, IOException {

           // TODO Auto-generated constructor stub

           socket = new Socket(server, servPort);

           in = socket.getInputStream();

           out = socket.getOutputStream();

 

       }

 

       public String handle(String message) throws UnknownHostException,

              IOException {

           byte[] receiveData = new byte[BUFSIZE];

 

           String temp = message.substring(0, message.indexOf("/n"));

           /** 动态构造发送缓冲数组---这样做很重要,因为只有如此才能保证传输过程不会出现错误* */

           byte[] sendData = temp.getBytes();// 因为指令不可能超过256个字符

           if (sendData == null || sendData.length < 1) {// 这么做是为了保持连接

              sendData = new byte[10];

              sendData = "error/r/n".getBytes();

           }

 

           try {

              System.out.println("连接到服务器-正在发送消息");

 

              out.write(sendData, 0, sendData.length);// //发送消息

              temp = new String(sendData);

              System.out.println(temp.length() + "发送:" + temp);

              int totalReceive = 0;

              if ((totalReceive = in.read(receiveData)) == -1) {// 接收消息

              // socket.close();

                  throw new SocketException("连接关闭");

              }

              temp = "";

              temp = new String(receiveData);

 

              String result;

              try {

                  result = temp.substring(0, temp.lastIndexOf("/r"));

              } catch (Exception e) {

                  result = temp.substring(0, totalReceive);

              }

              System.out.println("收到:" + result);

 

              return result;

           } catch (UnknownHostException e) {

              // TODO Auto-generated catch block

              e.printStackTrace();

              // socket.close();

           } catch (IOException e) {

              // TODO Auto-generated catch block

              e.printStackTrace();

              // socket.close();

           }

           return "null";

       }

 

       /**

        * 关闭连接

        */

       public void close() {

           try {

              socket.close();

           } catch (IOException e) {

              // TODO Auto-generated catch block

              e.printStackTrace();

           }

       }

    }

 

Ø  服务器端

/**

 * 处理客户端请求的命令

 *

 * @param message

 *            客户端请求 -- 格式:操作命令>操作目标>目的主机IP

 * @return 返回处理结果---若命令出错返回 error

 */

public String command(String message) {

    String str = "error";

    String cmd[] = message.split(">");// 主要考虑到,文件的路径中不包含字符,因此用作命令间的分隔符

    if ("get media list".equals(cmd[0])) {// 请求媒体数据信息

System.out.println(mediaPath);

       str = Tools.findMedias(mediaPath, true);// 获取所用媒体信息,并回馈给客户机

       if(!"".equals(str)){

           str = "list>" + str;

       }else {

           str = "error>";

       }

    } else if ("request media data".equals(cmd[0])) {// 请求媒体数据

        //port 发送数据的起始端口号

       try{

           if ((cmd[1] != null && !"".equals(cmd[1]))

                  && (cmd[2] != null && !"".equals(cmd[2]))) {// 操作目标和目的主机存在者进行下一步操作

              str = "media well send>" + InetAddress.getLocalHost().getHostAddress() +"/"+port;// 通知客户端,媒体数据已准备好,并通知待发送的端口号

              port += 14;//端口自动加14

           }

       }catch (ArrayIndexOutOfBoundsException aiobe){

           str = "error>no target";// 回馈错误

       } catch (UnknownHostException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       }

    } else if ("receive done".equals(cmd[0])) {// 已经准备好接收媒体数据

       try {

           File file;

           MediaLocator mediaLocator;

           InetAddress inetAddress;

           file = new File(mediaPath + cmd[1]);

           System.out.println(file.toURI().toURL());

           mediaLocator = new MediaLocator(file.toURI().toURL());

           inetAddress = InetAddress.getByName(cmd[2]);

           try {

              RTPTransmit rtpTransmit = new RTPTransmit(mediaLocator,

                     inetAddress, Integer.valueOf(cmd[3]));

              rtpTransmit.start();

              str = "media is sending>" + mediaPath;// 媒体数据正在发送

           } catch (Exception e) {

              str = "error>";// 媒体数据发送出错

           }

 

       } catch (UnknownHostException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       } catch (MalformedURLException e) {

           // TODO Auto-generated catch block

           e.printStackTrace();

       }

    }

    if ("".equals(str)) {

       str = "error";

    }

    return str;

}

 

/**

 * 解码

 *

 * @param b

 * @return

 */

public String decode(ByteBuffer b,int total) {

    String message = "";

    String m = Charset.defaultCharset().decode(b).toString();

    try{

       message = m.substring(0, m.indexOf("/r"));

    } catch (StringIndexOutOfBoundsException se){

       message = m.substring(0, total);;

    }

    return message;

}

 

@Override

public void run() {

    // TODO Auto-generated method stub

   

    try {

       handle(socket);

    } catch (Exception e) {

       // TODO Auto-generated catch block

    }

}

}


 

第四章   运行结果

运行结果显示



 

第五章   测试分析

5.1      程序运行情况:

程序正常的响应按钮事件,通过执行“文件”菜单——“打开”命令,找到需要播放的文件。此时,当前目录下所有可支持的文件都会加入到播放清单里面,但没有文件正在播放。在播放列表中选择一个文件,单击即可打开并播放。在播放过程中,可以实现“暂停”、“快进”、“慢放”、“拖动”、“音量调节”等常规播放器功能,而且还能实现播放过程中同步截屏的功能。

5.2      网络功能展示

由于我们的成品是一个可运行的jar文件,问了展示网络部分的细节我们在命令行中运行。java -jar ourplayer.jar 启动程序后界面如下

 

现在我们按下快捷键“ctrl + S”或者点击 开启服务器。如下图:

通过文件菜单中的“打开”项可以指定服务目录,我们选择E:/My Music目录,默认目录是D盘。现在将窗口切换到播放器窗口,通过快捷键“ctrl + N”或者点击 来打开网络功能。

输入127.0.0.1连接到本机,并且向服务器请求媒体列表

现在已经成功连接,并且获得了媒体列表,下面我们就来选中 进行播放。

好,成功。


 

5.3     程序清单

    /**

     * 内嵌的网络客户端

     *

     * @author Administrator

     *

     */

    class Client {

       private static final int BUFSIZE = 2048;// 接收缓冲

       int servPort = 10010;// 与点点播服务器连接的默认端口--10010

       OutputStream out;

       InputStream in;

       Socket socket;

 

       public Client(String server) throws UnknownHostException, IOException {

           // TODO Auto-generated constructor stub

           socket = new Socket(server, servPort);

           in = socket.getInputStream();

           out = socket.getOutputStream();

 

       }

 

       public String handle(String message) throws UnknownHostException,

              IOException {

           byte[] receiveData = new byte[BUFSIZE];

 

           String temp = message.substring(0, message.indexOf("/n"));

           /** 动态构造发送缓冲数组---这样做很重要,因为只有如此才能保证传输过程不会出现错误* */

           byte[] sendData = temp.getBytes();// 因为指令不可能超过256个字符

           if (sendData == null || sendData.length < 1) {// 这么做是为了保持连接

              sendData = new byte[10];

              sendData = "error/r/n".getBytes();

           }

 

           try {

              System.out.println("连接到服务器-正在发送消息");

 

              out.write(sendData, 0, sendData.length);// //发送消息

              temp = new String(sendData);

              System.out.println(temp.length() + "发送:" + temp);

              int totalReceive = 0;

              if ((totalReceive = in.read(receiveData)) == -1) {// 接收消息

              // socket.close();

                  throw new SocketException("连接关闭");

              }

              temp = "";

              temp = new String(receiveData);

 

              String result;

              try {

                  result = temp.substring(0, temp.lastIndexOf("/r"));

              } catch (Exception e) {

                  result = temp.substring(0, totalReceive);

              }

              System.out.println("收到:" + result);

 

              return result;

           } catch (UnknownHostException e) {

              // TODO Auto-generated catch block

              e.printStackTrace();

              // socket.close();

           } catch (IOException e) {

              // TODO Auto-generated catch block

              e.printStackTrace();

              // socket.close();

           }

           return "null";

       }

 

       /**

        * 关闭连接

        */

       public void close() {

           try {

              socket.close();

           } catch (IOException e) {

              // TODO Auto-generated catch block

              e.printStackTrace();

           }

    }

}

 

    public abstract void server();

    /********************************************************/

    /***

     * 类别类 -- 内嵌的网络服务器

     */

    protected class Protocol implements Runnable {

 

    private static final int BUFSIZE = 2048;// 接收缓冲

    private Socket socket;

 

    public Protocol(Socket clntSock) {

        // TODO Auto-generated constructor stub

        this.socket = clntSock;

    }

   

    public void handle(Socket clntSock) throws Exception {

        try {

            InputStream in = clntSock.getInputStream();// 获得输入流

            OutputStream out = clntSock.getOutputStream();// 获得输出流

 

//             while ((totalReceive = in.read(receiveData)) != -1) {// 直到客户端断开连接

            while(true){

               byte[] receiveData = new byte[BUFSIZE];

               int totalReceive = 0;

               if((totalReceive = in.read(receiveData)) == -1){

                   throw new SocketException("连接关闭");

               }

               ByteBuffer bb = ByteBuffer.wrap(receiveData);

               String result = decode(bb,totalReceive);// 获得客户端命令

               System.out.println(totalReceive+"接收:" + result);

 

               result = command(result);// 获取处理结果

               result += "/r/n";// 添加结束标记

               System.out.println("发送:" + result);

               /**动态构造发送缓冲数组---这样做很重要,因为只有如此才能保证传输过程不会出现错误**/

               byte[] sendData = result.getBytes();

               out.write(sendData, 0, sendData.length);

            }

 

        } catch (IOException e) {

            // TODO Auto-generated catch block

            System.exit(1);

            return ;

//             throw new Exception();

        } finally {

            try {

               if(clntSock.isClosed()){

                   clntSock.close();

               }

            } catch (IOException e) {

               // TODO Auto-generated catch block

               return ;

//                 throw new Exception();

            }

        }

 

    }

 

    /**

     * 处理客户端请求的命令

     *

     * @param message

     *            客户端请求 -- 格式:操作命令>操作目标>目的主机IP

     * @return 返回处理结果---若命令出错返回 error

     */

    public String command(String message) {

        String str = "error";

        String cmd[] = message.split(">");// 主要考虑到,文件的路径中不包含字符,因此用作命令间的分隔符

        if ("get media list".equals(cmd[0])) {// 请求媒体数据信息

System.out.println(mediaPath);

            str = Tools.findMedias(mediaPath, true);// 获取所用媒体信息,并回馈给客户机

            if(!"".equals(str)){

               str = "list>" + str;

            }else {

               str = "error>";

            }

        } else if ("request media data".equals(cmd[0])) {// 请求媒体数据

            //port 发送数据的起始端口号

            try{

               if ((cmd[1] != null && !"".equals(cmd[1]))

                      && (cmd[2] != null && !"".equals(cmd[2]))) {// 操作目标和目的主机存在者进行下一步操作

                   str = "media well send>" + InetAddress.getLocalHost().getHostAddress() +"/"+port;// 通知客户端,媒体数据已准备好,并通知待发送的端口号

                   port += 14;//端口自动加14

               }

            }catch (ArrayIndexOutOfBoundsException aiobe){

               str = "error>no target";// 回馈错误

            } catch (UnknownHostException e) {

               // TODO Auto-generated catch block

               e.printStackTrace();

            }

        } else if ("receive done".equals(cmd[0])) {// 已经准备好接收媒体数据

            try {

               File file;

               MediaLocator mediaLocator;

               InetAddress inetAddress;

               file = new File(mediaPath + cmd[1]);

               System.out.println(file.toURI().toURL());

               mediaLocator = new MediaLocator(file.toURI().toURL());

               inetAddress = InetAddress.getByName(cmd[2]);

               try {

                   RTPTransmit rtpTransmit = new RTPTransmit(mediaLocator,

                          inetAddress, Integer.valueOf(cmd[3]));

                   rtpTransmit.start();

                   str = "media is sending>" + mediaPath;// 媒体数据正在发送

               } catch (Exception e) {

                   str = "error>";// 媒体数据发送出错

               }

 

            } catch (UnknownHostException e) {

               // TODO Auto-generated catch block

               e.printStackTrace();

            } catch (MalformedURLException e) {

               // TODO Auto-generated catch block

               e.printStackTrace();

            }

        }

        if ("".equals(str)) {

            str = "error";

        }

        return str;

    }

 

    /**

     * 解码

     *

     * @param b

     * @return

     */

    public String decode(ByteBuffer b,int total) {

        String message = "";

        String m = Charset.defaultCharset().decode(b).toString();

        try{

            message = m.substring(0, m.indexOf("/r"));

        } catch (StringIndexOutOfBoundsException se){

            message = m.substring(0, total);;

        }

        return message;

    }

 

    @Override

    public void run() {

        // TODO Auto-generated method stub

       

        try {

              handle(socket);

           } catch (Exception e) {

              // TODO Auto-generated catch block

           }

    }


 

第六章   参考文献

[1] Kenneth L.Calvert  Michael J.DonahooTCP/IP Sockets in Java PRACTICAL GUIDE FOR PROGRANMMERSBaker & Taylor Books 2008

[2]   Bruce Eckel Thingking in Java 机械工程出版社 2007

[3]   彭波主编 《Java多媒体技术》清华大学出版社 2005


 

第七章      

我本人是很喜欢《计算机网络》这门课程的,再加上在这之前已经读过java socket编程方面的书籍并且在上个学期的《多媒体》课上接触过jmf,当接到这个课设时我的第一反应就是采用java来做,并且有自己来通过底层的socket编辑来实现网络部分的通信,并且采用rtp协议来传输视频。恰好在这个时候另两位同学(水清蓝幸福的猪)联系到我,于是我们一拍即合。由我负责实现网络部分,后来我觉得还不过瘾于是就有了给截图加上我们几个人的水印这个功能。

在这里我需要特别的感谢几个人,第一个就是我的《网络》老师,是他让我对这门单调的课程产生浓厚的兴趣;第二个要感谢的是Bruce Eckel 他是《java编程思想》的作者,我的java入门以及进阶很多都依赖他的这本著作;还有就是我的合作者水清蓝和幸福的猪,他们为本项目做了很多工作。另外因为本人喜欢刘亦菲所以坚持要把她的照片嵌入进来,希望她能原谅我的做法。

原创粉丝点击