我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android Socket使用TCP协议实现手机投屏

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android Socket使用TCP协议实现手机投屏

本节主要通过实战来了解Socket在TCP/IP协议中充当的是一个什么角色,有什么作用。通过Socket使用TCP协议实现局域网内手机A充当服务端,手机B充当客户端,手机B连接手机A,手机A获取屏幕数据转化为Bitmap,通过Socket传递个手机B显示。

实现效果:

一、 Socket是什么?

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

主机 A 的应用程序要能和主机 B 的应用程序通信,必须通过 Socket 建立连接,而建立 Socket 连接必须需要底层TCP/IP 协议来建立 TCP 连接。建立 TCP 连接需要底层 IP 协议来寻址网络中的主机。我们知道网络层使用的 IP 协议可以帮助我们根据 IP 地址来找到目标主机,但是一台主机上可能运行着多个应用程序,如何才能与指定的应用程序通信就要通过 TCP 或 UPD 的地址也就是端口号来指定。这样就可以通过一个 Socket 实例唯一代表一个主机上的一个应用程序的通信链路了。 

短连接:连接->传输数据->关闭连接:

传统HTTP是无状态的,浏览器和服务器每进行一次HTTP操作,就建立一次连接,但任务结束就中断连接。也可以这样说:短连接是指SOCKET连接后发送后接收完数据后马上断开连接。

长连接:连接->传输数据->保持连接 -> 传输数据-> …… ->关闭连接:

长连接指建立SOCKET连接后不管是否使用都保持连接。

什么时候用长连接,短连接?

长连接多用于操作频繁,点对点的通讯,而且连接数不能太多情况,。每个TCP连接都需要三步握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,下次处理时直接发送数据包就OK了,不用建立TCP连接。例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。

而像WEB网站的http服务一般都用短链接,因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源。

总之,长连接和短连接的选择要视情况而定。 而我们接下来要实现的手机实时投屏效果使用的就是长连接。

二、Socket的使用:

在使用Socket时,我们会使用到ServiceSocket和Socket,ServerSocket负责绑定IP地址,启动监听端口;Socket负责发起连接操作。连接成功后,双方通过输入和输出流进行同步阻塞式通信。

1、创建TcpServerRunnable TCP服务端

1)由于Android在使用网络通讯时要放在子线程中执行,所以可以将TcpServerRunnable实现Runnable接口。

public class TcpServerRunnable implements Runnable {    @Override    public void run() {           }}

2)在执行run方法里面创建ServiceSocket,ServerSocket内部使用的是TCP协议,如果想要使用UDP协议可以使用DatagramSocket。

private boolean ServerCreate() {try {serverSocket = new ServerSocket(port, 1);} catch (Exception e) {e.printStackTrace();if (listener != null) {listener.onServerClose();}return false;}return true;}

3)创建成功后,开启 while 循环监听TCP服务端是否被客户端连接;

private void ServerRun() {if (!ServerCreate()) {return;}while (true) {if (!ServerListen()) {break;}}}private boolean ServerListen() {try {socket = serverSocket.accept();} catch (Exception e) {e.printStackTrace();return false;}if (listener != null) {listener.onServerConnect();}return true;}

4)当有客户端连接到服务端时,开启 while 循环,从内存中拿取bitmap(屏幕数据),组装协议数据,发送给客户端。

private void ServerRun() {if (!ServerCreate()) {return;}while (true) {if (!ServerListen()) {break;}while (ServerIsConnect()) {ServerTransmitBitmap();ServerSleep(10);}}}private final static byte[] PACKAGE_HEAD = {(byte) 0xFF, (byte) 0xCF, (byte) 0xFA, (byte) 0xBF, (byte) 0xF6, (byte) 0xAF, (byte) 0xFE, (byte) 0xFF};private void ServerTransmitBitmap() {try {DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());if (bitmap != null) {byte[] bytes = MyUtils.BitmaptoBytes(bitmap);dataOutputStream.write(PACKAGE_HEAD);dataOutputStream.writeInt(MyUtils.getScreenWidth());dataOutputStream.writeInt(MyUtils.getScreenHeight());dataOutputStream.writeInt(bytes.length);dataOutputStream.write(bytes);}dataOutputStream.flush();} catch (IOException e) {e.printStackTrace();}}

当执行到dataOutputStream.flush();时,就会将数据发送给客户端,由于ServerTransmitBitmap()方法是在 while 循环里面的,所以当手机屏幕数据刷新后,重新赋值给bitmap,服务端会自动将新的bitmap再次组装发送给客户端,实现投屏实时刷新的效果。

public class TcpServerRunnable implements Runnable {    private static final String TAG = "TcpServerRunnable";    private ServerSocket serverSocket;    private Socket socket;    private int port;    private Bitmap bitmap;    public void setPort(int port) {        this.port = port;    }    public void setBitmap(Bitmap bitmap) {        this.bitmap = bitmap;    }    @Override    public void run() {        ServerRun();    }        private void ServerRun() {        if (!ServerCreate()) {            return;        }        while (true) {            if (!ServerListen()) {                break;            }            while (ServerIsConnect()) {                ServerTransmitBitmap();                ServerSleep(10);            }        }    }        private boolean ServerCreate() {        try {            serverSocket = new ServerSocket(port, 1);        } catch (Exception e) {            e.printStackTrace();            if (listener != null) {                listener.onServerClose();            }            return false;        }        return true;    }        private boolean ServerListen() {        try {            socket = serverSocket.accept();        } catch (Exception e) {            e.printStackTrace();            return false;        }        if (listener != null) {            listener.onServerConnect();        }        return true;    }        private boolean ServerIsConnect() {        return socket != null && !socket.isClosed() && socket.isConnected();    }    private final static byte[] PACKAGE_HEAD = {(byte) 0xFF, (byte) 0xCF, (byte) 0xFA, (byte) 0xBF, (byte) 0xF6, (byte) 0xAF, (byte) 0xFE, (byte) 0xFF};        private void ServerTransmitBitmap() {        try {            DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());            if (bitmap != null) {                byte[] bytes = MyUtils.BitmaptoBytes(bitmap);                dataOutputStream.write(PACKAGE_HEAD);                dataOutputStream.writeInt(MyUtils.getScreenWidth());                dataOutputStream.writeInt(MyUtils.getScreenHeight());                dataOutputStream.writeInt(bytes.length);                dataOutputStream.write(bytes);            }            dataOutputStream.flush();        } catch (IOException e) {            e.printStackTrace();        }    }    private void ServerSleep(long millis) {        try {            Thread.sleep(millis);        } catch (InterruptedException e) {            e.printStackTrace();        }    }        public void close() {        ServerClose();    }    private void ServerClose() {        try {            if (socket != null) {                socket.close();                serverSocket.close();            }        } catch (Exception e) {            e.printStackTrace();        }        if (listener != null) {            listener.onServerClose();        }    }    private ServerListener listener;    public void setListener(ServerListener listener) {        this.listener = listener;    }    public interface ServerListener {        void onServerConnect();        void onServerClose();    }}

2、获取手机屏幕数据,并转化为bitmap

1)创建ScreenCaptureService前台服务,执行处理捕获设备屏幕的单例类ScreenCapture。

public class ScreenCaptureService extends Service {    private ScreenCapture screenCapture;    public ScreenCaptureService() {    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {            createNotificationChannel(); //创建通知栏,你正在录屏        }        Bundle bundle = intent.getExtras();        if (bundle != null) {            int resultCode = bundle.getInt("resultCode");            Intent data = bundle.getParcelable("resultData");            screenCapture = ScreenCapture.getInstance(this, resultCode, data);        }        screenCapture.startScreenCapture();        return START_STICKY;    }    @Override    public void onDestroy() {        super.onDestroy();        screenCapture.stopScreenCapture();    }    private void createNotificationChannel() {           }}

2)创建MediaProjection,捕获设备屏幕上的内容,并将数据转化为Bitmap,MyUtils.setBitmap(bitmap) 存储到静态变量中。

public class ScreenCapture implements ImageReader.OnImageAvailableListener{    private static final String TAG = "ScreenCapture";    private final MediaProjection mMediaProjection; // 用于捕获设备屏幕上的内容并进行录制或截图    private VirtualDisplay mVirtualDisplay;    private final ImageReader mImageReader;    private final int screen_width;    private final int screen_height;    private final int screen_density;    private static volatile ScreenCapture screenCapture;    @SuppressLint("WrongConstant")    private ScreenCapture(Context context, int resultCode, Intent data) {        MediaProjectionManager mMediaProjectionManager = (MediaProjectionManager) context.getSystemService(Context.MEDIA_PROJECTION_SERVICE);        mMediaProjection = mMediaProjectionManager.getMediaProjection(resultCode, data);        screen_width = MyUtils.getScreenWidth();        screen_height = MyUtils.getScreenHeight();        screen_density = MyUtils.getScreenDensity();        mImageReader = ImageReader.newInstance(                screen_width,                screen_height,                PixelFormat.RGBA_8888,                2);    }    public static ScreenCapture getInstance(Context context, int resultCode, Intent data) {        if(screenCapture == null) {            synchronized (ScreenCapture.class) {                if(screenCapture == null) {                    screenCapture = new ScreenCapture(context, resultCode, data);                }            }        }        return screenCapture;    }    public void startScreenCapture() {        if (mMediaProjection != null) {            setUpVirtualDisplay();        }    }    private void setUpVirtualDisplay() {        mVirtualDisplay = mMediaProjection.createVirtualDisplay(                "ScreenCapture",                screen_width,                screen_height,                screen_density,                DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,                mImageReader.getSurface(),                null,                null);        mImageReader.setOnImageAvailableListener(this, null);    }    public void stopScreenCapture() {        if (mVirtualDisplay != null) {            mVirtualDisplay.release();        }    }    @Override    public void onImageAvailable(ImageReader imageReader) {        try {            Image image = imageReader.acquireLatestImage();            if(image != null) {                Image.Plane[] planes = image.getPlanes();                ByteBuffer buffer = planes[0].getBuffer();                int pixelStride = planes[0].getPixelStride();                int rowStride = planes[0].getRowStride();                int rowPadding = rowStride - pixelStride * screen_width;                Bitmap bitmap = Bitmap.createBitmap(screen_width + rowPadding / pixelStride, screen_height, Bitmap.Config.ARGB_8888);                bitmap.copyPixelsFromBuffer(buffer);                MyUtils.setBitmap(bitmap);                image.close();            }        } catch (Exception e) {            e.printStackTrace();        }    }}

对获取的屏幕Bitmap进行压缩,降低Bitmap大小,加快Socket传输速度。(非必须,不压缩也行)。创建BitmapProcessRunnable实现Runnable接口,在子线程执行bitmap压缩操作。

public class BitmapProcessRunnable implements Runnable {    private static final String TAG = "BitmapProcessRunnable";    private boolean isRun = false;    public void setRun(boolean isRun) {        this.isRun = isRun;    }    @Override    public void run() {        while (isRun) {            try {                Bitmap bitmap = MyUtils.getBitmap();                Log.i(TAG, "bitmap:" + bitmap);                if (bitmap != null) {                    bitmap = MyUtils.BitmapMatrixCompress(bitmap);                    if (listener != null) {                        listener.onProcessBitmap(bitmap);                    }                }                Thread.sleep(10);            } catch (Exception e) {                e.printStackTrace();            }        }    }    private ProcessListener listener;    public interface ProcessListener {        void onProcessBitmap(Bitmap bitmap);    }    public void setListener(ProcessListener listener) {        this.listener = listener;    }}
public static Bitmap BitmapMatrixCompress(Bitmap bitmap) {Matrix matrix = new Matrix();matrix.setScale(0.5f, 0.5f);return Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);}

3、关联TCP客户端ServerFragment页面

1)在ServerFragment页面创建TcpServerRunnable(TCP服务端)和BitmapProcessRunnable(图片处理线程),当用户点击创建按钮时,调用MyUtils.ExecuteRunnable(tcpServerRunnable),执行TcpServerRunnable的run方法。

2)客户端连接TCP服务端后,在回调接口onServerConnect()里面,开启前台服务ScreenCaptureService,捕获屏幕数据,同时执行BitmapProcessRunnable的run方法,对获取到的bitmap进行压缩,压缩完成,将新bitmap赋值给TcpServerRunnable(TCP服务端)。

3)TcpServerRunnable(TCP服务端)的 while 循环里面读取到新的bitmap,进行组装bitmap协议数据,发送给客户端。

public class ServerFragment extends Fragment implements View.OnClickListener {    private static final String TAG = "ServerFragment";    private static boolean server_create = false;    private TextView server_text;    private Button create_button;    private TcpServerRunnable tcpServerRunnable; // TCP服务端    private BitmapProcessRunnable bitmapProcessRunnable; // 图片处理线程    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        return inflater.inflate(R.layout.fragment_server, container, false);    }    @Override    public void onViewCreated(@NonNull @NotNull View view, @Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {        super.onViewCreated(view, savedInstanceState);        server_text = view.findViewById(R.id.server_text);        server_text.setText(MyUtils.getLocalAddress());        create_button = view.findViewById(R.id.create_button);        create_button.setOnClickListener(this);        bitmapProcessRunnable = new BitmapProcessRunnable();        bitmapProcessRunnable.setListener(bitmapProcessListener);        tcpServerRunnable = new TcpServerRunnable();        tcpServerRunnable.setPort(MyUtils.tcpSocketPort);        tcpServerRunnable.setListener(tcpServerListener);    }    @Override    public void onClick(View view) {        if (view.getId() == R.id.create_button) {            if (!server_create) {                server_create = true;                MyUtils.ExecuteRunnable(tcpServerRunnable);                create_button.setText("关闭");            } else {                server_create = false;                tcpServerRunnable.close();                create_button.setText("创建");            }        }    }    @Override    public void onDestroy() {        super.onDestroy();        tcpServerRunnable.close();    }    TcpServerRunnable.ServerListener tcpServerListener = new TcpServerRunnable.ServerListener() {        @Override        public void onServerConnect() {            bitmapProcessRunnable.setRun(true);            // 压缩图片            MyUtils.ExecuteRunnable(bitmapProcessRunnable);            Bundle bundle = new Bundle();            Intent start = new Intent(getActivity(), ScreenCaptureService.class);            bundle.putInt("resultCode", MyUtils.getResultCode());            bundle.putParcelable("resultData", MyUtils.getResultData());            start.putExtras(bundle);            // 启动投屏服务            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {                getActivity().startForegroundService(start);            } else {                getActivity().startService(start);            }        }        @Override        public void onServerClose() {            bitmapProcessRunnable.setRun(false);            Intent stop = new Intent(getActivity(), ScreenCaptureService.class);            getActivity().stopService(stop);        }    };    BitmapProcessRunnable.ProcessListener bitmapProcessListener = new BitmapProcessRunnable.ProcessListener() {        @Override        public void onProcessBitmap(Bitmap bitmap) {            // 将压缩后的图片传给tcpServer,tcpServer发送给客户端            tcpServerRunnable.setBitmap(bitmap);        }    };}

4、创建TcpClientRunnable TCP客户端

1)同样在子线程中开启连接TCP服务端,这里需要知道服务端的IP地址和端口号。

@Overridepublic void run() {ClientRun();}private void ClientRun() {if (!ClientConnect()) {return;}}private boolean ClientConnect() {try {socket = new Socket(ip, port);} catch (Exception e) {e.printStackTrace();return false;}if (listener != null) {listener.onClientConnect();}return true;}

2)连接成功后,开启 while 循环,接收服务端发送过来的bitmap数据,赋值给静态变量MyUtils.setBitmap()。

private void ClientRun() {if (!ClientConnect()) {return;}while (true) {while (ClientIsConnect()) {ClientReceiveBitmap();ClientSleep(10);}}}private void ClientReceiveBitmap() {try {Log.i(TAG,"循环读取服务端传过来的投屏Bitmap");InputStream inputStream = socket.getInputStream();boolean isHead = true;for (byte b : PACKAGE_HEAD) {byte head = (byte) inputStream.read();if (head != b) {isHead = false;break;}}if (isHead) {DataInputStream dataInputStream = new DataInputStream(inputStream);int width = dataInputStream.readInt();int height = dataInputStream.readInt();int len = dataInputStream.readInt();byte[] bytes = new byte[len];dataInputStream.readFully(bytes, 0, len);Bitmap bitmap = MyUtils.BytestoBitmap(bytes);if (bitmap != null && width != 0 && height != 0) {if (listener != null) {listener.onClientReceiveBitmap(bitmap, width, height);}}}} catch (Exception e) {e.printStackTrace();}}

5、客户端创建显示投屏的DisplayActivity

1)DisplayActivity通过自定义DisplayView来实时显示投屏数据;

public class DisplayActivity extends AppCompatActivity {    private static final String TAG = "DisplayActivity";    private DisplayView displayView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_display);        initView();    }    private void initView() {        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);        displayView = findViewById(R.id.displayView);        displayView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {            @Override            public void onGlobalLayout() {                displayView.setViewWidth(displayView.getWidth());                displayView.setViewHeight(displayView.getHeight());                displayView.getViewTreeObserver().removeOnGlobalLayoutListener(this);            }        });    }}

2)DisplayView继承自SurfaceView,并实现Runnable接口,当DisplayView添加到Activity后,Surface第一次被创建时回调到void surfaceCreated(),然后在surfaceCreated方法里面启动自己的run方法,循环的将bitmap绘制到页面上。

public class DisplayView extends SurfaceView implements SurfaceHolder.Callback,  Runnable{    private static final String TAG = "DisplayView";    private int viewWidth;    private int viewHeight;    private Bitmap bitmap;    private int width;    private int height;    private SurfaceHolder surfaceHolder;    private boolean isDraw = false;    public DisplayView(Context context) {        super(context);        initView();    }    public DisplayView(Context context, AttributeSet attrs) {        super(context, attrs);        initView();    }    public DisplayView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        initView();    }    private void initView() {        surfaceHolder = getHolder();        surfaceHolder.addCallback(this);        surfaceHolder.setFormat(PixelFormat.TRANSLUCENT);        setZOrderMediaOverlay(true);    }    @Override    public void surfaceCreated(@NonNull SurfaceHolder holder) {        isDraw = true;        MyUtils.ExecuteRunnable(this);    }    @Override    public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {    }    @Override    public void surfaceDestroyed(@NonNull SurfaceHolder holder) {        isDraw = false;    }    @Override    public void run() {        while (isDraw) {            try {                drawBitmap();                Thread.sleep(10);            } catch (Exception e) {                e.printStackTrace();            }        }    }    public void drawBitmap() {        Canvas canvas = surfaceHolder.lockCanvas();        if (canvas != null) {            bitmap = getBitmap();            if (bitmap != null) {                canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);                Rect rect = new Rect(0, 0, viewWidth, viewHeight);                canvas.drawBitmap(bitmap, null, rect, null);            }            surfaceHolder.unlockCanvasAndPost(canvas);        }    }    public Bitmap getBitmap() {        // return bitmap;        return MyUtils.getShowBitmap();    }    public void setBitmap(Bitmap bitmap, int width, int height) {        this.bitmap = bitmap;        this.width = width;        this.height = height;    }    public void setViewWidth(int width) {        this.viewWidth = width;    }    public void setViewHeight(int height) {        this.viewHeight = height;    }}

6、关联TCP客户端ClientFragment页面

1)在ClientFragment页面创建TcpClientRunnable(TCP客户端端)当用户输入服务端IP地址,点击连接按钮时,调用MyUtils.ExecuteRunnable(tcpClientRunnable),执行TcpClientRunnable的run方法。

2)客户端连接TCP服务端后,在回调接口onClientConnect()里面,开启DisplayActivity,DisplayView创建,等待bitmap写入MyUtils.setBitmap,循环的将bitmap绘制到页面上。

3)当客户端接收到服务端发送过来的投屏bitmap时,回调到onClientReceiveBitmap(),将bitmap设置到静态变量里面MyUtils.setBitmap(bitmap, width, height)。

public class ClientFragment extends Fragment implements View.OnClickListener {    private static final String TAG = "ClientFragment";    private static boolean client_connect = false;    private TextView client_edit;    private Button connect_button;    private TcpClientRunnable tcpClientRunnable;    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {        return inflater.inflate(R.layout.fragment_client, container, false);    }    @Override    public void onViewCreated(@NonNull @NotNull View view, @Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {        super.onViewCreated(view, savedInstanceState);        client_edit = view.findViewById(R.id.client_edit);        client_edit.setText(MyUtils.getLocalIp());        connect_button = view.findViewById(R.id.connect_button);        connect_button.setOnClickListener(this);        tcpClientRunnable = new TcpClientRunnable();        tcpClientRunnable.setPort(MyUtils.tcpSocketPort);        tcpClientRunnable.setListener(tcpClientListener);    }    @Override    public void onClick(View view) {        if (view.getId() == R.id.connect_button) {            if (client_edit.getText().toString().trim().length() == 0) {                Toast.makeText(getActivity(), "请输入服务端IP", Toast.LENGTH_SHORT).show();            } else {                if (!client_connect) {                    client_connect = true;                    tcpClientRunnable.setIp(client_edit.getText().toString().trim());                    MyUtils.ExecuteRunnable(tcpClientRunnable);                    client_edit.setText(MyUtils.getLocalIp());                    client_edit.setEnabled(false);                    connect_button.setText("断开");                } else {                    client_connect = false;                    tcpClientRunnable.close();                    client_edit.setText(client_edit.getText().toString().trim());                    client_edit.setEnabled(true);                    connect_button.setText("连接");                }            }        }    }    @Override    public void onDestroy() {        super.onDestroy();        tcpClientRunnable.close();    }    TcpClientRunnable.ClientListener tcpClientListener = new TcpClientRunnable.ClientListener() {        @Override        public void onClientConnect() {            Log.i(TAG,"TCP连接成功,跳转DisplayActivity");            Intent intent = new Intent(getActivity(), DisplayActivity.class);            startActivity(intent);        }        @Override        public void onClientClose() {            ToastUtils.showShort("TCP连接断开!");        }        @Override        public void onClientReceiveBitmap(Bitmap bitmap, int width, int height) {            MyUtils.setBitmap(bitmap, width, height);        }    };}

至此,通过Socket实现的手机局域网投屏软件完成。

来源地址:https://blog.csdn.net/sziitjin/article/details/132240113

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android Socket使用TCP协议实现手机投屏

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

在c#中使用Socket实现一个tcp协议

这篇文章给大家介绍在c#中使用Socket实现一个tcp协议,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。一、概述UDP和TCP是网络通讯常用的两个传输协议,C#一般可以通过Socket来实现UDP和TCP通讯,由于.
2023-06-06

python如何使用socket实现TCP协议长连接框架

这篇文章主要介绍了python如何使用socket实现TCP协议长连接框架,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。分析多了协议就会发现,很多的应用,特别是游戏类和IM类
2023-06-29

python中怎么用socket实现协议TCP长连接框架

这篇文章主要介绍“python中怎么用socket实现协议TCP长连接框架”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“python中怎么用socket实现协议TCP长连接框架”文章能帮助大家解决问
2023-06-29

Android编程使用HTTP协议与TCP协议实现上传文件的方法

本文实例讲述了Android编程使用HTTP协议与TCP协议实现上传文件的方法。分享给大家供大家参考,具体如下: Android上传文件有两种方式,第一种是基于Http协议的HttpURLConnection,第二种是基于TCP协议的Soc
2022-06-06

使用Python Socket实现搭建HTTP协议

本教程讲解了如何使用PythonSocket模块实现HTTP服务器和客户端。服务器端通过监听8080端口的连接请求,接受客户端请求,解析HTTP请求行,并返回简单的HTML响应。客户端端构建HTTP请求,发送给服务器,并接收并打印HTTP响应。通过扩展此代码,可以实现更多高级功能,例如处理不同HTTP方法、解析复杂请求、提供文件服务和实现身份验证和加密等。
使用Python Socket实现搭建HTTP协议
2024-04-02

如何使用linux shell 脚本实现tcp/upd协议通讯

本篇内容主要讲解“如何使用linux shell 脚本实现tcp/upd协议通讯”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何使用linux shell 脚本实现tcp/upd协议通讯”吧!
2023-06-09

实现统计 android手机 CPU使用

1 # -*- coding:utf-8 -*- 2 ''' 3 Created on Sep 10, 2018 4 5 @author: SaShuangYiBing 6 ''' 7 import subprocess 8 import
2023-01-30

使用Android的OkHttp包实现基于HTTP协议的文件上传下载

OkHttp的HTTP连接基础 虽然在使用 OkHttp 发送 HTTP 请求时只需要提供 URL 即可,OkHttp 在实现中需要综合考虑 3 种不同的要素来确定与 HTTP 服务器之间实际建立的 HTTP 连接。这样做的目的是为了达到最
2022-06-06

Android应用中实现截取手机屏幕的方法有哪些

这篇文章给大家介绍Android应用中实现截取手机屏幕的方法有哪些,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。方法1:首先想到的思路是利用SDK提供的View.getDrawingCache()方法: public
2023-05-31

Android使用AsyncQueryHandler实现获取手机联系人功能

利用AsyncQueryHandler能异步任务获取手机联系人,增加用户体验,使用起来也很方便。不多说,上干货。布局文件main.xml
2023-05-30

Android中使用 AutoCompleteTextView 实现手机号格式化附带清空历史的操作

有个小伙伴遇到了这样一个问题,就是AutoCompleteTextView实现自动填充的功能。同时要具备手机格式化的功能。下拉列表最后一行是有个清除历史的功能。可是点击“清除历史”却把文字要设置进去AutoCompleteTextView中
2022-06-06

Android手机开发 使用线性布局和相对布局实现Button垂直水平居中

居中呢,这里分两种不同布局方式的居中!分别是 LinearLayout 和RelativeLayout。 一、首先说的是LinearLayout布局下的居中: 注意:android:layout_width="fill_parent" an
2022-06-06

怎么使用Android实现打开手机淘宝并自动识别淘宝口令弹出商品信息功能

小编给大家分享一下怎么使用Android实现打开手机淘宝并自动识别淘宝口令弹出商品信息功能,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!1.首先我们需要后台帮助我
2023-05-30

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录