Socket 连接前置机和手机APP的转发工具

这是一个用于转发Socket连接的工具,适用于前置机作为服务端,手机APP作为客户端的场景。通过创建双向通信通道,该工具允许手机通过USB与前置机进行Socket通信,实现数据流的交互。

以下为转发工具代码:

   前提:前置机为服务端,app为客户端.以下代码可以直接用,如果前置机和app都是客户端,则需要改转发工具总的客户端端为服务端,即可.

 该转发工具的应用场景:

        1.当手机需要通过USB连接前置机的,并采用socket通信的时候使用

应用原理:

   就是建立双向通道, 转发工具其实就是两个服务端或者一个客户端一个服务端拼接起来,建立双向通信通道,供连接的两端进行数据流交互使用.

package com.pemt.sockettools;

import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    ServerSocket serverSocket1 =null;
    Socket       socket1       =null;
    int inputPort=1234;
    int outPutPort=3131;//和掌机连接的端口
    InetAddress  address1       =null;
    InetAddress  address2      =null;
     private static final String TAG = "MainActivity";
    SharedPreferences sp ;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        sp = PreferenceManager.getDefaultSharedPreferences(this);
        SharedPreferences.Editor edit = sp.edit();
        edit.putString("exception","");
        edit.commit();
        init();
    }

    private void init() {
        initView();
        sp = PreferenceManager.getDefaultSharedPreferences(this);
        Log.e(TAG, "init:-------------------------------------- " );
        run();
    }

    private void run() {
        new Thread(new Runnable() {
            Socket socket2=null;
            @Override
            public void run() {
                try {
                  socket2 =new Socket("127.0.0.1",1234);//和前置机
                    Log.e(TAG, "init:----------------------try---------------- ");
              // ServerSocket serverSocket2 = new ServerSocket(1234);//和前置机

                    serverSocket1 =new ServerSocket(outPutPort, 2);//和统一版APP 默认同时连接两个客户端.
                    Log.e(TAG, "init: 服务端.......");
                    serverSocket1.setReuseAddress(true); //设置 ServerSocket 的选项
                   System.out.println("服务器即将启动,等待客户端的链接=====>");
                    Log.e(TAG, "服务器即将启动,等待客户端的链接=====>" );
                    //记录客户端的数量
                    int count = 0 ;

                   // while (true){
                        Log.e(TAG, "run: ...........");
                        socket1 = serverSocket1.accept();//阻塞,等待客户端请求
                       // socket2 = serverSocket2.accept();//阻塞,等待客户端请求

                        Log.e(TAG, "等待客户端请求" );
                        //创建一个新的线程
                     MyThread serverThread = new MyThread(socket1, socket2,MainActivity.this);
                        //启动线程
                        serverThread.start();
                        count ++ ;

                        System.out.println("当前客户连接数"+count);
                        Log.e(TAG, "当前客户连接数"+count);
                        address1 = socket1.getInetAddress();
                        Log.d(TAG, "run: socket2=="+socket2);
                        address2 = socket2.getInetAddress();
                        System.out.println("当前客户端的ip地址:"+address1.getHostAddress());
                        Log.e(TAG, "当前客户端的ip地址:"+address1.getHostAddress());
                        System.out.println("当前客户端的ip地址:"+address2.getHostAddress());
                        Log.e(TAG, "当前客户端的ip地址:"+address2.getHostAddress());
                 //   }

                } catch (IOException e) {
                    Log.e(TAG, "异常====>"+e );
                    String str =e+"";
                    if (str.contains("1234")&& str.contains("refused")){
                        str+="是否未连接前置机和主站";
                    }else if(str.contains("BindException")){
                        str+="端口号被占用!请退出程序,重新登入";
                    }else if(str.contains("SocketException") && str.contains("Socket closed")){
                     str+="连接的客户端,超过最大输入,请退出转发工具,重新登录";  
                    }else{
                        str+="是否未设置统一版APP的usb端口号和IP地址";
                    }
                    SharedPreferences.Editor edit = sp.edit();
                    edit.putString("exception",str);
                    edit.commit();
                    e.printStackTrace();
                }
            }
        }).start();
    }

    EditText et;
    EditText client;
    Button save;
    TextView result;
    private void initView() {
         et = (EditText) findViewById(R.id.tv1);//和前置机连接的端口
         client = (EditText) findViewById(R.id.client);//转发端口
         result = (TextView) findViewById(R.id.result);
        result.setMovementMethod(ScrollingMovementMethod.getInstance());
        save =(Button) findViewById(R.id.save);
        save.setOnClickListener(this);
    }
     @Override
    public void onClick(View v) {
      String text = et.getText().toString().trim();//和前置连接的端口
      String port = client.getText().toString().trim();//和掌机连接的端口
      Log.e(TAG, "onClick: "+text+","+port );
      if (TextUtils.isEmpty(text) || text == null || TextUtils.isEmpty(port) || port == null){
          Toast.makeText(MainActivity.this, "接入端口或转发端口为空,请检查!", Toast.LENGTH_SHORT).show();
          Log.e(TAG, "onClick: " +"接入端口或转发端口为空,请检查!");
      }else{

            inputPort = Integer.parseInt(text);
           outPutPort = Integer.parseInt(port);

          Toast.makeText(MainActivity.this, "端口保存成功!", Toast.LENGTH_SHORT).show();
          Log.e(TAG, "onClick: " + "端口保存成功!");
          SharedPreferences.Editor edit = sp.edit();
          edit.putString("exception","");
          edit.commit();
      }
         init();
  }
  @Override
    protected void onResume() {
        super.onResume();
        String exception = sp.getString("exception", "");
        result.setText(exception);
    }
}

线程
package com.pemt.sockettools;

import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;



public class MyThread {
    //和本线程相关的socket
    public  Socket socket;
    public Socket mSocket02;
    private Context mContext;
    private SharedPreferences sp;
    private static final String TAG = "MainActivity";
   public MyThread(Socket socket01, Socket socket02, Context context){
       socket = socket01;//和APP的端口
       mSocket02 = socket02;//和前置机的端口
       mContext = context;
       sp = PreferenceManager.getDefaultSharedPreferences(mContext);
   }
 public void start(){
     try {
         is1 = socket.getInputStream();//字节输入流  统一版APP-->转发工具
         is2 = mSocket02.getInputStream();//和前置机-->转发工具
         os1 = socket.getOutputStream();//转发工具--->统一版APP
         os2 = mSocket02.getOutputStream();//转发工具--->统一版APP
     } catch (IOException e) {
         SharedPreferences.Editor edit = sp.edit();
         edit.putString("exception","退出程序,请重新登录!");
         edit.commit();
         e.printStackTrace();
     }

     ReceiveThread thread =new ReceiveThread();
      thread.start();
     SendThread sendThread= new SendThread();
     sendThread.start();
 }
    //获取输入流,并读取客户端信息
    InputStream  is1    = null;
    OutputStream os1    = null;

    InputStream  is2 =null;
    OutputStream      os2             =null ;

    /********************
     * thread1 start:
     **************************/
    private class ReceiveThread extends Thread {



        @Override
        public void run() {
            try {

                byte[] buffer1 = new byte[1024 * 20];

                //获取输出流
              Log.e(TAG, "run: " + "MainActivity");
                int len = -1;
              while (true) {
                    while ((len = is1.read(buffer1)) != -1) {//读取统一版APP发过来的字节流
                        os2.write(buffer1, 0, len);//转发工具的客户端,写给-->前置机
                        Log.e(TAG, "run:----写往前置机------- " + new String(buffer1, 0, len));

                    }
                    int len2 = -1;
                    os1.flush();

                    os2.flush();
                }
            } catch (IOException e) {
                SharedPreferences.Editor edit = sp.edit();
                edit.putString("exception",e+"");
                edit.commit();
                e.printStackTrace();
            } finally {

                //关闭资源
                try {

                    os1.close();
                    os2.close();
                    is1.close();
                    is2.close();
                    socket.close();
                    mSocket02.close();
                } catch (IOException e) {
                    SharedPreferences.Editor edit = sp.edit();
                    edit.putString("exception",e+"");
                    edit.commit();
                    e.printStackTrace();
                }
            }
        }
    }
        /********************thread1 END*****************************/


        /********************
         * thread2 start:
         **************************/
        private class SendThread extends Thread {

          @Override
            public void run() {
                try {


                    byte[] buffer4 = new byte[1024 * 20];
                    Log.e(TAG, "run: " + "MainActivity");
                    int len = -1;

                  String str="";
                    while (true) {

                        int len2 = -1;
                        Log.d(TAG, "run: 开始写往...写给统一版APP");
                        while ((len2 = is2.read(buffer4)) != -1) {//读取转发工具获取到的输入流
                            os1.write(buffer4, 0, len2);//转发工具的服务端--->统一版APP
                            String s = new String(buffer4, 0, len2,"GB2312");
                            Log.e(TAG, "run:----统一APP---- " + s);
                            SharedPreferences.Editor edit = sp.edit();
                            str+=s;
                            edit.putString("exception",str+"\n");
                            edit.commit();
                        }//如果byte数组给的过大,还会刷出来空的byte.


                        os1.flush();
                        os2.flush();
                     }
                } catch (IOException e) {
                    SharedPreferences.Editor edit = sp.edit();
                    edit.putString("exception",e+"");
                    edit.commit();
                    e.printStackTrace();
                } finally {

                    //关闭资源
                    try {

                        os1.close();
                        os2.close();
                        is1.close();
                        is2.close();
                        socket.close();
                        mSocket02.close();
                    } catch (IOException e) {
                        SharedPreferences.Editor edit = sp.edit();
                        edit.putString("exception",e+"");
                        edit.commit();
                        e.printStackTrace();
                    }
                }
            }
        }
    }

        /********************thread2 END*****************************/


xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@android:color/black"
        android:textSize="25dp"
        android:text="端口设置"/>
    <LinearLayout
        android:layout_marginTop="20dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
         <EditText
             android:id="@+id/tv1"
             android:background="@drawable/button_shape"
             android:layout_width="80dp"
             android:text="1234"
             android:layout_height="30dp"/>
        <TextView
            android:layout_marginLeft="10dp"
            android:text="接入端口"
            android:inputType="number"
            android:textColor="@android:color/black"
            android:textSize="18dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </LinearLayout>
    <LinearLayout
        android:layout_marginTop="10dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
         <EditText
             android:id="@+id/client"
             android:background="@drawable/button_shape"
             android:layout_width="80dp"
             android:text="3131"
             android:inputType="number"
             android:layout_height="30dp"/>
        <TextView
            android:layout_marginLeft="10dp"
            android:text="转发端口"
            android:textColor="@android:color/black"
            android:textSize="18dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
         <Button
             android:id="@+id/save"
             android:text="保存"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
            />

    </LinearLayout>



       <TextView
           android:textColor="@android:color/black"
           android:textSize="18dp"
           android:ellipsize="end"
           android:singleLine="true"
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:text="日志显示"/>
     <TextView
         android:id="@+id/result"
         android:ellipsize="end"
         android:scrollbars="vertical"
         android:singleLine="false"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         android:text=""/>


</LinearLayout>



   

要实现一个“电视切换信号输入”的App,我们需要明确几个关键点: 1. **目标设备**:你的 App 是控制哪类电视?是智能电视(如 Android TV)、通过 HDMI 切换的普通电视,还是支持某种协议(如 HDMI-CEC、红外遥控、Wi-Fi 控制)的设备? 2. **控制方式**: - 如果是 Android TV 或支持网络控制的智能电视(如三星 Tizen、LG WebOS),可以通过局域网发送指令。 - 如果是传统电视 + 外接盒子,可能需要红外发射器(如手机带红外功能或外接红外设备)。 3. **平台选择**:你希望这个 App 运行在哪个平台?Android?iOS?还是跨平台? --- ### 场景假设 我们假设你要开发一个 **Android App**,用于控制一台支持 **HDMI-CEC** 或可通过 **红外遥控** 切换输入源的电视,并且你的手机具备红外发射功能(大多数中高端安卓手机都支持)。 我们将使用 Android 的 `ConsumerIrManager` API 来模拟红外信号,发送“Input”或“Source”按钮指令,从而切换电视的输入信号源。 --- ## ✅ 功能目标 - 用户点击按钮:“切换输入源” - 手机通过红外发送“INPUT”或“SOURCE”键码 - 电视响应并切换输入通道(如 HDMI1 → HDMI2) --- ### 📱 Android 红外控制 App 示例代码(Kotlin) ```kotlin // MainActivity.kt package com.example.tvinputswitcher import android.content.Context import android.os.Bundle import android.widget.Button import androidx.appcompat.app.AppCompatActivity import android.content.pm.PackageManager import android.hardware.ConsumerIrManager import android.widget.Toast class MainActivity : AppCompatActivity() { private var consumerIrManager: ConsumerIrManager? = null private val frequency = 38000 // 载波频率,单位 Hz,常见为 38kHz override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // 获取 ConsumerIrManager consumerIrManager = getSystemService(Context.CONSUMER_IR_SERVICE) as ConsumerIrManager // 检查设备是否支持红外发射 if (!packageManager.hasSystemFeature(PackageManager.FEATURE_CONSUMER_IR)) { Toast.makeText(this, "此设备不支持红外功能", Toast.LENGTH_LONG).show() finish() return } val buttonSwitch: Button = findViewById(R.id.button_switch) buttonSwitch.setOnClickListener { sendInputCommand() } } private fun sendInputCommand() { // 假设 INPUT 键的红外脉冲序列(不同品牌电视不同) // 下面是一个示例波形(单位:微秒),模拟按下“SOURCE”或“INPUT”键 // 注意:实际值需根据具体电视型号学习获得(可用万能遥控器学习获取) val pattern = intArrayOf( 9000, 4500, // 领导码(前置脉冲) 560, 560, 560, 1690, 560, 560, 560, 1690, // 示例数据位(代表某个键) 560, 1690, 560, 560, 560, 1690, 560, 1690, 560, 1690, 560, 560, 560, 1690, 560, 1690, 560, 1690, 560, 560, 560, 1690, 560, 1690, 560, 560, 560, 560, 560, 1690, 560, 560, 560, 560, 560, 560, 560, 450 ) // 发送红外信号 consumerIrManager?.transmit(frequency, pattern) Toast.makeText(this, "已发送切换输入源指令", Toast.LENGTH_SHORT).show() } } ``` --- ### 📄 布局文件:`res/layout/activity_main.xml` ```xml <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" android:padding="16dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="电视输入源切换器" android:textSize="24sp" android:layout_marginBottom="32dp" /> <Button android:id="@+id/button_switch" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="切换输入源" android:textSize="18sp" /> </LinearLayout> ``` --- ### 🔧 权限说明 无需特殊权限(从 Android 4.4 开始,`ConsumerIrManager` 不需要危险权限)。 但需确保 `AndroidManifest.xml` 中声明了功能支持(非必需,但推荐): ```xml <uses-feature android:name="android.hardware.consumerir" android:required="false" /> ``` --- ### ⚠️ 注意事项 1. **红外编码差异大**:每个电视品牌(Sony、Samsung、TCL 等)的红外协议不同。上面的 `pattern` 是 NEC 协议的一个示例,必须根据实际设备调整。 2. **如何获取正确的红外码?** - 使用支持学习功能的万能遥控器 + 示波器分析。 - 或使用开源项目如 [IRRemote](https://github.com/z3t0/Arduino-IRremote) 学习原始码。 3. **现代替代方案**: - 若电视是智能电视,建议使用 **网络控制**(如 Samsung TV 的 REST API、LG WebOS 的 WebSocket、Android TV 的 ADB)更稳定可靠。 --- ### ✅ 更优方案:网络控制(以 Android TV 为例) 如果你的电视是 Android TV 并开启了 ADB 调试,你可以通过局域网发送按键事件: ```bash # 通过 ADB 发送 KEYCODE_INPUT adb shell input keyevent KEYCODE_INPUT ``` 你可以在 App 中使用 Socket 或 HTTP 请求调用本地服务器执行该命令(需 root 或 PC 中转)。 --- ## 总结 上述代码展示了一个基础的 Android 红外遥控 App,能够发送“切换输入源”的红外信号。虽然简单,但实用性依赖于对目标电视红外协议的准确掌握。 如果你想让 App 更通用,可以考虑: - 内置多个品牌的红外码数据库。 - 提供用户自定义学习功能(配合外部硬件)。 - 支持 Wi-Fi 控制协议(如 UPnP、REST API)。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值