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

这篇具有很好参考价值的文章主要介绍了Android Socket使用TCP协议实现手机投屏。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

实现效果:

Android Socket使用TCP协议实现手机投屏,android,网络编程,tcp/ip,SocketAndroid Socket使用TCP协议实现手机投屏,android,网络编程,tcp/ip,Socket

一、 Socket是什么?

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

Android Socket使用TCP协议实现手机投屏,android,网络编程,tcp/ip,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负责发起连接操作。连接成功后,双方通过输入和输出流进行同步阻塞式通信。

Android Socket使用TCP协议实现手机投屏,android,网络编程,tcp/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};

/**
 * 写入 协议头+投屏bitmap数据
 */
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);
            }
        }
    }

    /**
     * 使用ServerSocket创建TCP服务端
     *
     * @return
     */
    private boolean ServerCreate() {
        try {
            serverSocket = new ServerSocket(port, 1);
        } catch (Exception e) {
            e.printStackTrace();
            if (listener != null) {
                listener.onServerClose();
            }
            return false;
        }
        return true;
    }

    /**
     * 循环监听服务端是否被连接
     *
     * @return
     */
    private boolean ServerListen() {
        try {
            socket = serverSocket.accept();
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        if (listener != null) {
            listener.onServerConnect();
        }
        return true;
    }

    /**
     * 判断服务端是否被连接
     *
     * @return
     */
    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};

    /**
     * 写入 协议头+投屏bitmap数据
     */
    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();
        }
    }
}

3、对获取的屏幕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地址和端口号。

@Override
public 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://www.toymoban.com/news/detail-649412.html

到了这里,关于Android Socket使用TCP协议实现手机投屏的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • scrcpy之将Android手机投屏到Linux电脑实践

    参考:https://zhuanlan.zhihu.com/p/366378837 电脑端安装投屏程序 手机端设置 手机端无需安装任何软件,只需开启【开发者选项】-【USB调试】及相关选项,比如我开启了【USB调试、USB调试(安全设置)、无线调试、USB安装】 开启【USB调试】相关选项后,用USB数据线连接电脑与手机,

    2024年02月09日
    浏览(56)
  • 使用sunshine+moonlight 实现电脑串流到电视(Android 设备)低延迟投屏

    目前市面上的投屏软件大多存在视频质量不清晰,延迟严重,投屏收费等问题。 为解决电脑到电视或者投影仪无线连接进行投屏,目前使用基于nvidia开源项目的Moonlight是目前最佳的解决方案。 原本nVidia 的显卡内置的GameStream可以实现低延迟高画质的串流的,但是: 这就非常

    2024年02月08日
    浏览(58)
  • C++版Android实时投屏软件系统源码,安卓手机投屏软件源码,无需root权限

    QtScrcpy 可以通过 USB / 网络连接Android设备,并进行显示和控制。无需root权限。 同时支持 GNU/Linux ,Windows 和 MacOS 三大主流桌面平台。 完整代码下载地址:C++版Android实时投屏软件系统源码 它专注于: 精致 (仅显示设备屏幕) 性能 (30~60fps) 质量 (1920×1080以上) 低延迟 (35~70ms) 快速启

    2024年02月05日
    浏览(50)
  • socket概述 python中如何使用TCP/UDP协议实现通信-教程

    很多编程语言中,都 使用scoket套接字实现网络通信。 Socket是对TCP/IP协议的封装,Socket本身就是一个调用接口(API),方便程序员用Socket使用TCP/IP协议簇,实现网络通信。 不同编程语言,shiyongSocket通信的语法有所区别,但基本原理类型相似。 它的两种方式,分别是TCP和UDP协

    2024年02月13日
    浏览(42)
  • c++使用OpenSSL基于socket实现tcp双向认证ssl(使用TSL协议)代码实现

    相信各位对OpenSSL库已经不陌生了,目前笔者使用这个库实现了RSA、AES加解密和tcp的双向认证功能,下面来看tcp的双向认证。 简单说双向认证就是:客户端认证服务端是否合法,服务端认证客户端是否合法 。 可以借助于HTTPS来说明,http网络传输协议是超文本的明文协议,也就

    2024年02月06日
    浏览(59)
  • python实现Android实时投屏操控

            python中有一个scrcpy-client库,可以实现Android设备的实时投屏和操控。它和scrcpy实现Android投屏是一样的,都是把一个scrcpy-server.jar文件通过adb推送到Android设备,并利用adb指令执行scrcpy-server.jar开启投屏和操控服务端,电脑端通过python创建客户端来接收视频流数据和发送

    2024年02月03日
    浏览(39)
  • 【C语言实现windows环境下Socket编程TCP/IP协议】

    代码是别人的,问题是我的。顺便记录一下遇见的各种问题和我的解决办法。 可能的解决方案: 1、服务端和客户端不在一个局域网,可以开热点,这样就在了。然后ipconfig查看IP地址,就ok了。至于怎么查看在不在就ping一下对方就好了。 2、一个局域网下也ping不通:看看自己

    2024年02月04日
    浏览(48)
  • Android网络功能开发(6)——TCP协议通信

    TCP通信的双方需要建立连接,所以先由一方监听某个端口,等待其他设备来连接,这一方称为服务器端。另一方向服务器端发起连接请求,称为客户端。服务器端接受客户端的连接请求后,双方之间的连接建立起来。连接建立后,双方对于连接的使用是相同的,都可以通过连

    2024年02月09日
    浏览(56)
  • Android开发知识学习——TCP / IP 协议族

    一系列协议所组成的一个网络分层模型 为什么要分层? 因为网络的不稳定性 Application Layer 应用层:HTTP、FTP、DNS,提供了网络应用程序之间的通信机制 Transport Layer 传输层:TCP、UDP,提供可靠的端到端的数据传输服务的层次 Internet Layer 网络层:IP,网络中所有数据传输的入口

    2024年02月07日
    浏览(46)
  • socket的使用 | TCP/IP协议下服务器与客户端之间传送数据

    谨以此篇,记录TCP编程,方便日后查阅笔记 注意:用BufferedWriter write完后,一定要flush;否则字符不会进入流中。去看源码可知:真正将字符写入的不是write(),而是flush()。 服务器端代码: 客户端代码: 运行后结果: 服务器端: 客户端: 参考资料: https://www.bilibili.com/vid

    2024年02月09日
    浏览(60)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包