基于Socket的Android手机视频实时传输

来源:互联网 发布:阿里云 进销存 编辑:程序博客网 时间:2024/05/02 04:37

首先,简单介绍一下原理。主要是在手机客户端(Android)通过实现Camera.PreviewCallback接口,在其onPreviewFrame重载函数里面获取摄像头当前图像数据,然后通过Socket将图像数据和相关的用户名、命令等数据传输到服务器程序中。服务器端(PC端)采用C#编写,通过监听相应的端口,在获取数据后进行相应的命令解析和图像数据还原,然后将图像数据传递至PictureBox控件中用于显示,这样就实现了手机摄像头的视频数据实时传输到服务器上。如果需要将这些视频进行转发,通过服务器再将这些数据复制转发即可。效果如下:

对于Android客户端上主要有几个地方需要注意,第一个就是Socket通信。Socket通信可以通过Socket类来实现,直接结合PrintWriter来写入命令,如下定义的一个专门用于发送命令的线程类,当要连接到服务器和与服务器断开时,都需要发送命令通知服务器,此外在进行其他文字传输时也可以采用该方法,具体代码如下:

1. /**发送命令线程*/  2.   class MySendCommondThread extends Thread{  3.     private String commond;  4.     public MySendCommondThread(String commond){  5.         this.commond=commond;  6.     }  7.     public void run(){  8.         //实例化Socket    9.           try {  10.            Socket socket=new Socket(serverUrl,serverPort);  11.            PrintWriter out = new PrintWriter(socket.getOutputStream());  12.            out.println(commond);  13.            out.flush();  14.        } catch (UnknownHostException e) {  15.        } catch (IOException e) {  16.        }    17.    }  18.  }  
</pre></div><p><span style="color:#333333">如果是采用</span><span style="color:#333333">Socket</span><span style="color:#333333">发送文件,则可以通过</span><span style="color:#333333">OutputStream</span><span style="color:#333333">将</span><span style="color:#333333">ByteArrayInputStream</span><span style="color:#333333">数据流读入,而文件数据流则转换为</span><span style="color:#333333">ByteArrayOutputStream</span><span style="color:#333333">。如果需要在前面添加文字,同样也需要转换为</span><span style="color:#333333">byte</span><span style="color:#333333">,然后写入</span><span style="color:#333333">OutputStream</span><span style="color:#333333">。同样也可以通过定义一个线程类发送文件,如下:</span></p><div><p><span style="color:#5C5C5C"></span></p><pre name="code" class="java">1. /**发送文件线程*/  2.    class MySendFileThread extends Thread{     3.     private String username;  4.     private String ipname;  5.     private int port;  6.     private byte byteBuffer[] = new byte[1024];  7.     private OutputStream outsocket;   8.     private ByteArrayOutputStream myoutputstream;  9.       10.    public MySendFileThread(ByteArrayOutputStream myoutputstream,String username,String ipname,int port){  11.        this.myoutputstream = myoutputstream;  12.        this.username=username;  13.        this.ipname = ipname;  14.        this.port=port;  15.           try {  16.            myoutputstream.close();  17.        } catch (IOException e) {  18.            e.printStackTrace();  19.        }  20.    }  21.      22.       public void run() {  23.           try{  24.            //将图像数据通过Socket发送出去  25.               Socket tempSocket = new Socket(ipname, port);  26.               outsocket = tempSocket.getOutputStream();  27.               //写入头部数据信息  28.            String msg=java.net.URLEncoder.encode("PHONEVIDEO|"+username+"|","utf-8");  29.               byte[] buffer= msg.getBytes();  30.               outsocket.write(buffer);  31.                 32.               ByteArrayInputStream inputstream = new ByteArrayInputStream(myoutputstream.toByteArray());  33.               int amount;  34.               while ((amount = inputstream.read(byteBuffer)) != -1) {  35.                   outsocket.write(byteBuffer, 0, amount);  36.               }  37.               myoutputstream.flush();  38.               myoutputstream.close();  39.               tempSocket.close();                     40.           } catch (IOException e) {  41.               e.printStackTrace();  42.           }  43.       }  44.   }  


而获取摄像头当前图像的关键在于onPreviewFrame()重载函数里面,该函数里面有两个参数,第一个参数为byte[],为摄像头当前图像数据,通过YuvImage可以将该数据转换为图片文件,同时还可用对该图片进行压缩和裁剪,将图片进行压缩转换后转换为    ByteArrayOutputStream数据,即前面发送文件线程类中所需的文件数据,然后采用线程发送文件,如下代码:

1. @Override  2. public void onPreviewFrame(byte[] data, Camera camera) {  3.     // TODO Auto-generated method stub  4.     //如果没有指令传输视频,就先不传  5.     if(!startSendVideo)  6.         return;  7.     if(tempPreRate<VideoPreRate){  8.         tempPreRate++;  9.         return;  10.    }  11.    tempPreRate=0;        12.    try {  13.          if(data!=null)  14.          {  15.            YuvImage image = new YuvImage(data,VideoFormatIndex, VideoWidth, VideoHeight,null);  16.            if(image!=null)  17.            {  18.                ByteArrayOutputStream outstream = new ByteArrayOutputStream();  19.                //在此设置图片的尺寸和质量   20.                image.compressToJpeg(new Rect(0, 0, (int)(VideoWidthRatio*VideoWidth),   21.                    (int)(VideoHeightRatio*VideoHeight)), VideoQuality, outstream);    22.                outstream.flush();  23.                //启用线程将图像数据发送出去  24.                Thread th = new MySendFileThread(outstream,pUsername,serverUrl,serverPort);  25.                th.start();    26.            }  27.          }  28.      } catch (IOException e) {  29.          e.printStackTrace();  30.      }  31.}  


值得注意的是,在调试中YuvImage可能找不到,在模拟机上无法执行该过程,但是编译后在真机中可以通过。此外,以上传输文字字符都是采用UTF编码,在服务器端接收时进行解析时需要采用对应的编码进行解析,否则可能会出现错误解析。

Android客户端中关键的部分主要就这些,新建一个Android项目(项目名称为SocketCamera),在main布局中添加一个SurfaceView和两个按钮,如下图所示:

然后在SocketCameraActivity.java中添加代码,具体如下:

1. package com.xzy;  2.   3. import java.io.ByteArrayInputStream;  4. import java.io.ByteArrayOutputStream;  5. import java.io.IOException;  6. import java.io.OutputStream;  7. import java.io.PrintWriter;  8. import java.net.Socket;  9. import java.net.UnknownHostException;  10.import android.app.Activity;  11.import android.app.AlertDialog;  12.import android.content.DialogInterface;  13.import android.content.Intent;  14.import android.content.SharedPreferences;  15.import android.graphics.Rect;  16.import android.graphics.YuvImage;  17.import android.hardware.Camera;  18.import android.hardware.Camera.Size;  19.import android.os.Bundle;  20.import android.preference.PreferenceManager;  21.import android.view.Menu;  22.import android.view.MenuItem;  23.import android.view.SurfaceHolder;  24.import android.view.SurfaceView;  25.import android.view.View;  26.import android.view.WindowManager;  27.import android.view.View.OnClickListener;  28.import android.widget.Button;  29.  30.public class SocketCameraActivity extends Activity implements SurfaceHolder.Callback,  31.Camera.PreviewCallback{       32.    private SurfaceView mSurfaceview = null; // SurfaceView对象:(视图组件)视频显示  33.    private SurfaceHolder mSurfaceHolder = null; // SurfaceHolder对象:(抽象接口)SurfaceView支持类  34.    private Camera mCamera = null; // Camera对象,相机预览   35.      36.    /**服务器地址*/  37.    private String pUsername="XZY";  38.    /**服务器地址*/  39.    private String serverUrl="192.168.1.100";  40.    /**服务器端口*/  41.    private int serverPort=8888;  42.    /**视频刷新间隔*/  43.    private int VideoPreRate=1;  44.    /**当前视频序号*/  45.    private int tempPreRate=0;  46.    /**视频质量*/  47.    private int VideoQuality=85;  48.      49.    /**发送视频宽度比例*/  50.    private float VideoWidthRatio=1;  51.    /**发送视频高度比例*/  52.    private float VideoHeightRatio=1;  53.      54.    /**发送视频宽度*/  55.    private int VideoWidth=320;  56.    /**发送视频高度*/  57.    private int VideoHeight=240;  58.    /**视频格式索引*/  59.    private int VideoFormatIndex=0;  60.    /**是否发送视频*/  61.    private boolean startSendVideo=false;  62.    /**是否连接主机*/  63.    private boolean connectedServer=false;  64.      65.    private Button myBtn01, myBtn02;  66.    /** Called when the activity is first created. */  67.    @Override  68.    public void onCreate(Bundle savedInstanceState) {  69.        super.onCreate(savedInstanceState);  70.        setContentView(R.layout.main);  71.          72.        //禁止屏幕休眠        getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,   73.                WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);  74.                 75.        mSurfaceview = (SurfaceView) findViewById(R.id.camera_preview);  76.        myBtn01=(Button)findViewById(R.id.button1);  77.        myBtn02=(Button)findViewById(R.id.button2);  78.                  79.        //开始连接主机按钮  80.        myBtn01.setOnClickListener(new OnClickListener(){  81.            public void onClick(View v) {  82.                //Common.SetGPSConnected(LoginActivity.this, false);  83.                if(connectedServer){//停止连接主机,同时断开传输  84.                    startSendVideo=false;  85.                    connectedServer=false;                    86.                    myBtn02.setEnabled(false);  87.                    myBtn01.setText("开始连接");  88.                    myBtn02.setText("开始传输");  89.                    //断开连接  90.                    Thread th = new MySendCommondThread("PHONEDISCONNECT|"+pUsername+"|");  91.                    th.start();   92.                }  93.                else//连接主机  94.                {  95.                    //启用线程发送命令PHONECONNECT  96.                    Thread th = new MySendCommondThread("PHONECONNECT|"+pUsername+"|");  97.                    th.start();   98.                    connectedServer=true;  99.                    myBtn02.setEnabled(true);  100.                    myBtn01.setText("停止连接");  101.                }  102.            }});  103.          104.        myBtn02.setEnabled(false);  105.        myBtn02.setOnClickListener(new OnClickListener(){  106.            public void onClick(View v) {  107.                if(startSendVideo)//停止传输视频  108.                {  109.                    startSendVideo=false;  110.                    myBtn02.setText("开始传输");  111.                }  112.                else{ // 开始传输视频  113.                    startSendVideo=true;  114.                    myBtn02.setText("停止传输");  115.                }  116.            }});  117.    }  118.      119.    @Override  120.    public void onStart()//重新启动的时候  121.    {     122.        mSurfaceHolder = mSurfaceview.getHolder(); // 绑定SurfaceView,取得SurfaceHolder对象  123.        mSurfaceHolder.addCallback(this); // SurfaceHolder加入回调接口         124.        mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);// 设置显示器类型,setType必须设置  125.        //读取配置文件  126.        SharedPreferences preParas = PreferenceManager.getDefaultSharedPreferences(SocketCameraActivity.this);  127.        pUsername=preParas.getString("Username", "XZY");  128.        serverUrl=preParas.getString("ServerUrl", "192.168.0.100");  129.        String tempStr=preParas.getString("ServerPort", "8888");  130.        serverPort=Integer.parseInt(tempStr);  131.        tempStr=preParas.getString("VideoPreRate", "1");  132.        VideoPreRate=Integer.parseInt(tempStr);               133.        tempStr=preParas.getString("VideoQuality", "85");  134.        VideoQuality=Integer.parseInt(tempStr);  135.        tempStr=preParas.getString("VideoWidthRatio", "100");  136.        VideoWidthRatio=Integer.parseInt(tempStr);  137.        tempStr=preParas.getString("VideoHeightRatio", "100");  138.        VideoHeightRatio=Integer.parseInt(tempStr);  139.        VideoWidthRatio=VideoWidthRatio/100f;  140.        VideoHeightRatio=VideoHeightRatio/100f;  141.          142.        super.onStart();  143.    }  144.      145.    @Override  146.    protected void onResume() {  147.        super.onResume();          148.        InitCamera();  149.    }  150.      151.    /**初始化摄像头*/  152.    private void InitCamera(){  153.        try{  154.            mCamera = Camera.open();  155.        } catch (Exception e) {  156.            e.printStackTrace();  157.        }  158.    }  159.   160.    @Override  161.    protected void onPause() {  162.        super.onPause();  163.        try{  164.            if (mCamera != null) {  165.                mCamera.setPreviewCallback(null); // !!这个必须在前,不然退出出错  166.                mCamera.stopPreview();  167.                mCamera.release();  168.                mCamera = null;  169.            }   170.        } catch (Exception e) {  171.            e.printStackTrace();  172.        }   173.    }  174.    @Override  175.    public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {  176.        // TODO Auto-generated method stub  177.        if (mCamera == null) {  178.            return;  179.        }  180.        mCamera.stopPreview();  181.        mCamera.setPreviewCallback(this);  182.        mCamera.setDisplayOrientation(90); //设置横行录制  183.        //获取摄像头参数  184.        Camera.Parameters parameters = mCamera.getParameters();  185.        Size size = parameters.getPreviewSize();  186.        VideoWidth=size.width;  187.        VideoHeight=size.height;  188.        VideoFormatIndex=parameters.getPreviewFormat();  189.          190.        mCamera.startPreview();  191.    }  192.  193.    @Override  194.    public void surfaceCreated(SurfaceHolder holder) {  195.        // TODO Auto-generated method stub  196.        try {  197.            if (mCamera != null) {  198.                mCamera.setPreviewDisplay(mSurfaceHolder);  199.                mCamera.startPreview();  200.            }  201.        } catch (IOException e) {  202.            e.printStackTrace();  203.        }   204.    }  205.  206.    @Override  207.    public void surfaceDestroyed(SurfaceHolder holder) {  208.        // TODO Auto-generated method stub  209.        if (null != mCamera) {  210.            mCamera.setPreviewCallback(null); // !!这个必须在前,不然退出出错  211.            mCamera.stopPreview();  212.            mCamera.release();  213.            mCamera = null;  214.        }  215.    }  216.  217.    @Override  218.    public void onPreviewFrame(byte[] data, Camera camera) {  219.        // TODO Auto-generated method stub  220.        //如果没有指令传输视频,就先不传  221.        if(!startSendVideo)  222.            return;  223.        if(tempPreRate<VideoPreRate){  224.            tempPreRate++;  225.            return;  226.        }  227.        tempPreRate=0;        228.        try {  229.              if(data!=null)  230.              {  231.                YuvImage image = new YuvImage(data,VideoFormatIndex, VideoWidth, VideoHeight,null);  232.                if(image!=null)  233.                {  234.                    ByteArrayOutputStream outstream = new ByteArrayOutputStream();  235.                    //在此设置图片的尺寸和质量   236.                    image.compressToJpeg(new Rect(0, 0, (int)(VideoWidthRatio*VideoWidth),   237.                        (int)(VideoHeightRatio*VideoHeight)), VideoQuality, outstream);    238.                    outstream.flush();  239.                    //启用线程将图像数据发送出去  240.                    Thread th = new MySendFileThread(outstream,pUsername,serverUrl,serverPort);  241.                    th.start();    242.                }  243.              }  244.          } catch (IOException e) {  245.              e.printStackTrace();  246.          }  247.    }  248.          249.    /**创建菜单*/      250.    public boolean onCreateOptionsMenu(Menu menu)  251.    {  252.        menu.add(0,0,0,"系统设置");  253.        menu.add(0,1,1,"关于程序");   254.        menu.add(0,2,2,"退出程序");   255.        return super.onCreateOptionsMenu(menu);  256.    }  257.    /**菜单选中时发生的相应事件*/    258.    public boolean onOptionsItemSelected(MenuItem item)  259.    {  260.        super.onOptionsItemSelected(item);//获取菜单  261.        switch(item.getItemId())//菜单序号  262.        {  263.            case 0:  264.                //系统设置  265.                {  266.                    Intent intent=new Intent(this,SettingActivity.class);  267.                    startActivity(intent);    268.                }  269.                break;    270.            case 1://关于程序  271.            {  272.                new AlertDialog.Builder(this)  273.                .setTitle("关于本程序")  274.                .setMessage("本程序由武汉大学水利水电学院肖泽云设计、编写。\nEmail:xwebsite@163.com")  275.                .setPositiveButton  276.                (  277.                    "我知道了",  278.                    new DialogInterface.OnClickListener()  279.                    {                         280.                        @Override  281.                        public void onClick(DialogInterface dialog, int which)   282.                        {  283.                        }  284.                    }  285.                )  286.                .show();  287.            }  288.            break;  289.            case 2://退出程序  290.                {  291.                    //杀掉线程强制退出  292.                    android.os.Process.killProcess(android.os.Process.myPid());  293.                }  294.                break;  295.        }         296.        return true;  297.    }  298.      299.    /**发送命令线程*/  300.    class MySendCommondThread extends Thread{  301.        private String commond;  302.        public MySendCommondThread(String commond){  303.            this.commond=commond;  304.        }  305.        public void run(){  306.            //实例化Socket    307.            try {  308.                Socket socket=new Socket(serverUrl,serverPort);  309.                PrintWriter out = new PrintWriter(socket.getOutputStream());  310.                out.println(commond);  311.                out.flush();  312.            } catch (UnknownHostException e) {  313.            } catch (IOException e) {  314.            }    315.        }  316.    }  317.      318.    /**发送文件线程*/  319.    class MySendFileThread extends Thread{    320.        private String username;  321.        private String ipname;  322.        private int port;  323.        private byte byteBuffer[] = new byte[1024];  324.        private OutputStream outsocket;   325.        private ByteArrayOutputStream myoutputstream;  326.          327.        public MySendFileThread(ByteArrayOutputStream myoutputstream,String username,String ipname,int port){  328.            this.myoutputstream = myoutputstream;  329.            this.username=username;  330.            this.ipname = ipname;  331.            this.port=port;  332.            try {  333.                myoutputstream.close();  334.            } catch (IOException e) {  335.                e.printStackTrace();  336.            }  337.        }  338.          339.        public void run() {  340.            try{  341.                //将图像数据通过Socket发送出去  342.                Socket tempSocket = new Socket(ipname, port);  343.                outsocket = tempSocket.getOutputStream();  344.                //写入头部数据信息  345.                String msg=java.net.URLEncoder.encode("PHONEVIDEO|"+username+"|","utf-8");  346.                byte[] buffer= msg.getBytes();  347.                outsocket.write(buffer);  348.                  349.                ByteArrayInputStream inputstream = new ByteArrayInputStream(myoutputstream.toByteArray());  350.                int amount;  351.                while ((amount = inputstream.read(byteBuffer)) != -1) {  352.                    outsocket.write(byteBuffer, 0, amount);  353.                }  354.                myoutputstream.flush();  355.                myoutputstream.close();  356.                tempSocket.close();      357.            } catch (IOException e) {  358.                e.printStackTrace();  359.            }  360.        }  361.    }  362.}  


此外还有一些参数,在res/xml新建一个setting.xml文件,添加服务器地址、端口、用户名等参数设置,如下:

1. <?xml version="1.0" encoding="utf-8"?>  2. <PreferenceScreen  3.   xmlns:android="http://schemas.android.com/apk/res/android">  4.  <PreferenceCategory android:title="服务器设置">  5.     <EditTextPreference  6.    android:key="Username"  7.    android:title="用户名"      8.    android:summary="用于连接服务器的用户名"  9.    android:defaultValue="XZY"/>  10.   <EditTextPreference  11.   android:key="ServerUrl"  12.   android:title="视频服务器地址"      13.   android:summary="保存服务器地址"  14.   android:defaultValue="192.168.1.100"/>  15.<EditTextPreference  16.   android:key="ServerPort"  17.   android:title="服务器端口"      18.   android:summary="连接服务器的端口地址"  19.   android:defaultValue="8888"/>  20.   </PreferenceCategory>  21.<PreferenceCategory android:title="视频设置">  22.    <EditTextPreference  23.   android:key="VideoPreRate"  24.   android:title="视频刷新间隔"      25.   android:summary="设置视频刷新的间隔值,应大于等于0,值越大视频传输间隔越长"  26.   android:defaultValue="1"/>  27.<EditTextPreference  28.   android:key="VideoQuality"  29.   android:title="图像质量"      30.   android:summary="设置图像压缩的质量,值为0~100,值越高越清晰,但同时数据也更大"  31.   android:defaultValue="85"/>  32.<EditTextPreference  33.   android:key="VideoWidthRatio"  34.   android:title="图像宽度缩放比例"      35.   android:summary="设置图像的宽度缩放比例,值为0~100,值越高图像分辨率越高"  36.   android:defaultValue="100"/>  37.   <EditTextPreference  38.   android:key="VideoHeightRatio"  39.   android:title="图像高度缩放比例"      40.   android:summary="设置图像的高度缩放比例,值为0~100,值越高图像分辨率越高"  41.   android:defaultValue="100"/>  42.    </PreferenceCategory>  43.</PreferenceScreen>  


编译程序,在模拟机上效果如下:

接下来就是服务器端接收手机传输的视频数据,这与一般CS架构中服务器程序类似,主要是监听端口,然后解析数据。现新建一个C#应用程序项目(项目名称为手机摄像头),首先定义一些全局变量,主要包括服务器地址、端口以及相关监听对象等,如下:

1. /// <summary>  2.         /// 服务器状态,如果为false表示服务器暂停,true表示服务器开启  3.         /// </summary>  4.         public bool ServerStatus = false;  5.         /// <summary>  6.         /// 服务器地址  7.         /// </summary>  8.         public string ServerAddress;  9.         /// <summary>  10.        /// 服务器端口  11.        /// </summary>  12.        public int ServerPort;  13.        /// <summary>  14.        /// 开启服务的线程  15.        /// </summary>  16.        private Thread processor;  17.        /// <summary>  18.        /// 用于TCP监听  19.        /// </summary>  20.        private TcpListener tcpListener;  21.        /// <summary>  22.        /// 与客户端连接的套接字接口  23.        /// </summary>  24.        private Socket clientSocket;  25.        /// <summary>  26.        /// 用于处理客户事件的线程  27.        /// </summary>  28.        private Thread clientThread;  29.        /// <summary>  30.        /// 手机客户端所有客户端的套接字接口  31.        /// </summary>  32.        private Hashtable PhoneClientSockets = new Hashtable();  33.        /// <summary>  34.        /// 手机用户类数组  35.        /// </summary>  36.        public ArrayList PhoneUsersArray = new ArrayList();  37.        /// <summary>  38.        /// 手机用户名数组  39.        /// </summary>  40.        public ArrayList PhoneUserNamesArray = new ArrayList();  41.        /// <summary>  42.        /// 图像数据流  43.        /// </summary>  44.        private ArrayList StreamArray;  


然后定义处理客户端传递数据的函数ProcessClient(),主要对接收数据进行命令解析。如果是手机连接的命令("PHONECONNECT"),就在记录该套接字对象,同时在列表中添加该对象;如果是断开连接的命令("PHONEDISCONNECT",就移除该对象;如果是手机视频命令("PHONEVIDEO"),就分解其包含的图像数据,如果存在该用户对应的视频窗口,就传递该图像数据到这个视频窗口中。具体代码如下:

1. #region 处理客户端传递数据及处理事情  2.       /// <summary>  3.       /// 处理客户端传递数据及处理事情  4.       /// </summary>  5.       private void ProcessClient()  6.       {  7.           Socket client = clientSocket;  8.           bool keepalive = true;  9.           while (keepalive)  10.          {  11.              Thread.Sleep(50);  12.              Byte[] buffer = null;  13.              bool tag = false;  14.              try  15.              {  16.                  buffer = new Byte[1024];//client.Available  17.                  int count = client.Receive(buffer, SocketFlags.None);//接收客户端套接字数据  18.                  if (count > 0)//接收到数据  19.                      tag = true;  20.              }  21.              catch (Exception e)  22.              {  23.                  keepalive = false;  24.                  if (client.Connected)  25.                      client.Disconnect(true);  26.                  client.Close();  27.              }  28.              if (!tag)  29.              {  30.                  if (client.Connected)  31.                      client.Disconnect(true);  32.                  client.Close();  33.                  keepalive = false;  34.              }  35.  36.              string clientCommand = "";  37.              try  38.              {  39.                  clientCommand = System.Text.Encoding.UTF8.GetString(buffer);//转换接收的数据,数据来源于客户端发送的消息  40.                  if (clientCommand.Contains("%7C"))//从Android客户端传递部分数据  41.                      clientCommand = clientCommand.Replace("%7C", "|");//替换UTF中字符%7C为|  42.              }  43.              catch  44.              {  45.              }  46.              //分析客户端传递的命令来判断各种操作  47.              string[] messages = clientCommand.Split('|');  48.              if (messages != null && messages.Length > 0)  49.              {  50.                  string tempStr = messages[0];//第一个字符串为命令  51.                  if (tempStr == "PHONECONNECT")//手机连接服务器  52.                  {  53.                      try  54.                      {  55.                          string tempClientName = messages[1].Trim();  56.                          PhoneClientSockets.Remove(messages[1]);//删除之前与该用户的连接  57.                          PhoneClientSockets.Add(messages[1], client);//建立与该客户端的Socket连接                          58.  59.                          UserClass tempUser = new UserClass();  60.                          tempUser.UserName = tempClientName;  61.                          tempUser.LoginTime = DateTime.Now;  62.                          Socket tempSocket = (Socket)PhoneClientSockets[tempClientName];  63.                          tempUser.IPAddress = tempSocket.RemoteEndPoint.ToString();  64.  65.                          int tempIndex = PhoneUserNamesArray.IndexOf(tempClientName);  66.                          if (tempIndex >= 0)  67.                          {  68.                              PhoneUserNamesArray[tempIndex] = tempClientName;  69.                              PhoneUsersArray[tempIndex] = tempUser;  70.                              MemoryStream stream2 = (MemoryStream)StreamArray[tempIndex];  71.                              if (stream2 != null)  72.                              {  73.                                  stream2.Close();  74.                                  stream2.Dispose();  75.                              }  76.                          }  77.                          else//新增加  78.                          {  79.                              PhoneUserNamesArray.Add(tempClientName);  80.                              PhoneUsersArray.Add(tempUser);  81.                              StreamArray.Add(null);  82.                          }  83.                          RefreshPhoneUsers();  84.                      }  85.                      catch (Exception except)  86.                      {  87.                      }  88.                  }  89.                  else if (tempStr == "PHONEDISCONNECT")//某个客户端退出了  90.                  {  91.                      try  92.                      {  93.                          string tempClientName = messages[1];  94.                          RemovePhoneUser(tempClientName);  95.  96.                          int tempPhoneIndex = PhoneUserNamesArray.IndexOf(tempClientName);  97.                          if (tempPhoneIndex >= 0)  98.                          {  99.                              PhoneUserNamesArray.RemoveAt(tempPhoneIndex);  100.                              MemoryStream memStream = (MemoryStream)StreamArray[tempPhoneIndex];  101.                              if (memStream != null)  102.                              {  103.                                  memStream.Close();  104.                                  memStream.Dispose();  105.                              }  106.                              StreamArray.RemoveAt(tempPhoneIndex);  107.                          }  108.                          Socket tempSocket = (Socket)PhoneClientSockets[tempClientName];//第1个为客户端的ID,找到该套接字  109.                          if (tempSocket != null)  110.                          {  111.                              tempSocket.Close();  112.                              PhoneClientSockets.Remove(tempClientName);  113.                          }  114.                          keepalive = false;  115.                      }  116.                      catch (Exception except)  117.                      {  118.                      }  119.                      RefreshPhoneUsers();  120.                  }  121.                  else if (tempStr == "PHONEVIDEO")//接收手机数据流  122.                  {  123.                      try  124.                      {  125.                          string tempClientName = messages[1];  126.                          string tempForeStr = messages[0] + "%7C" + messages[1] + "%7C";  127.                          int startCount = System.Text.Encoding.UTF8.GetByteCount(tempForeStr);  128.                          try  129.                          {  130.                              MemoryStream stream = new MemoryStream();  131.                              if (stream.CanWrite)  132.                              {  133.                                  stream.Write(buffer, startCount, buffer.Length - startCount);  134.                                  int len = -1;  135.                                  while ((len = client.Receive(buffer)) > 0)  136.                                  {  137.                                      stream.Write(buffer, 0, len);  138.                                  }  139.                              }  140.                              stream.Flush();  141.  142.                              int tempPhoneIndex = PhoneUserNamesArray.IndexOf(tempClientName);  143.                              if (tempPhoneIndex >= 0)  144.                              {  145.                                  MemoryStream stream2 = (MemoryStream)StreamArray[tempPhoneIndex];  146.                                  if (stream2 != null)  147.                                  {  148.                                      stream2.Close();  149.                                      stream2.Dispose();  150.                                  }  151.                                  StreamArray[tempPhoneIndex] = stream;  152.  153.                                  PhoneVideoForm form = GetPhoneVideoForm(tempClientName);  154.                                  if (form != null)  155.                                      form.DataStream = stream;  156.                              }  157.                          }  158.                          catch  159.                          {  160.                          }  161.                      }  162.                      catch (Exception except)  163.                      {  164.                      }  165.                  }  166.              }  167.              else//客户端发送的命令或字符串为空,结束连接  168.              {  169.                  try  170.                  {  171.                      client.Close();  172.                      keepalive = false;  173.                  }  174.                  catch  175.                  {  176.                      keepalive = false;  177.                  }  178.              }  179.          }  180.      }  181.      #endregion  


关于开启服务监听、刷新用户列表、获取手机视频窗体、删除用户、寻找用户序号等代码在此就不详细介绍,具体参见源代码。

基于SocketAndroid手机视频实时传输手机客户端下载地址:

http://download.csdn.net/detail/xwebsite/4973592

基于SocketAndroid手机视频实时传输服务器端下载地址:

http://download.csdn.net/detail/xwebsite/4973601

基于SocketAndroid手机视频实时传输所有源程序下载地址:

http://download.csdn.net/detail/xwebsite/4973613

 

0 0
原创粉丝点击