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 {@Overridepublic 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;}@Overridepublic 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() {}@Overridepublic 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;}@Overridepublic 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();}}@Overridepublic 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;}@Overridepublic 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; // 图片处理线程@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {return inflater.inflate(R.layout.fragment_server, container, false);}@Overridepublic 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);}@Overridepublic 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("创建");}}}@Overridepublic void onDestroy() {super.onDestroy();tcpServerRunnable.close();}TcpServerRunnable.ServerListener tcpServerListener = new TcpServerRunnable.ServerListener() {@Overridepublic 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);}}@Overridepublic void onServerClose() {bitmapProcessRunnable.setRun(false);Intent stop = new Intent(getActivity(), ScreenCaptureService.class);getActivity().stopService(stop);}};BitmapProcessRunnable.ProcessListener bitmapProcessListener = new BitmapProcessRunnable.ProcessListener() {@Overridepublic 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;@Overrideprotected 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() {@Overridepublic 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);}@Overridepublic void surfaceCreated(@NonNull SurfaceHolder holder) {isDraw = true;MyUtils.ExecuteRunnable(this);}@Overridepublic void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(@NonNull SurfaceHolder holder) {isDraw = false;}@Overridepublic 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;@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {return inflater.inflate(R.layout.fragment_client, container, false);}@Overridepublic 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);}@Overridepublic 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("连接");}}}}@Overridepublic void onDestroy() {super.onDestroy();tcpClientRunnable.close();}TcpClientRunnable.ClientListener tcpClientListener = new TcpClientRunnable.ClientListener() {@Overridepublic void onClientConnect() {Log.i(TAG,"TCP连接成功,跳转DisplayActivity");Intent intent = new Intent(getActivity(), DisplayActivity.class);startActivity(intent);}@Overridepublic void onClientClose() {ToastUtils.showShort("TCP连接断开!");}@Overridepublic void onClientReceiveBitmap(Bitmap bitmap, int width, int height) {MyUtils.setBitmap(bitmap, width, height);}};
}

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/95135.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux——KVM虚拟化

目录标题 虚拟化技术虚拟化技术发展案例KVM简介KVM架构及原理KVM原理KVM虚拟化架构/三种模式虚拟化前、虚拟化后对比KVM盖中盖套娃实验 虚拟化技术 通过虚拟化技术将一台计算机虚拟为多台逻辑计算机,在一台计算机上同时运行多个逻辑计算机,同时每个逻辑…

ValueError: check_hostname requires server_hostname

ValueError: check_hostname requires server_hostname 可能是代理的问题,在pip下载库的时候如果之前电脑连过梯子,就会出现。 这个时候就算关掉梯子也无法解决这个错误。 下面是解决方法 右键网络,打开设置,代理 点击手动设置代…

excel 之 VBA

1、excel和VBA 高效办公,把重复性的工作写成VBA代码(VB代码的衍生物,语法和VBA相同)。 首先打开开发工具模式,如果没有选显卡,需要手动打开 打开程序编辑界面 快捷键 altF11一般操作 程序调试&#xf…

hive--给表名和字段加注释

1.建表添加注释 CREATE EXTERNAL TABLE test(loc_province string comment 省份,loc_city string comment 城市,loc_district string comment 区,loc_street string comment 街道,)COMMENT 每日数据处理后的表 PARTITIONED BY (par_dt string) ROW FORMAT SERDEorg.apache.had…

《离散数学及其应用(原书第8版)》ISBN978-7-111-63687-8 第11章 11.1.3 树的性质 节 第664页的例9说明

《离散数学及其应用(原书第8版)》ISBN978-7-111-63687-8 第11章 11.1.3 树的性质 节 第664页的定理3的引申 定理3 带有i个内点的m叉树含有nmi1个顶点 见本人博文 内点定义不同的讨论 如果对于一个m叉正则树,即任意分支节点的儿子恰好有m个&am…

Hlang社区-前端社区宣传首页实现

文章目录 前言页面结构固定钉头部轮播JS特效完整代码总结前言 这里的话,博主其实也是今年参与考研的大军之一,所以的话,是抽空去完成这个项目的,当然这个项目的肯定是可以在较短的时间内完成的。 那么废话不多说,昨天也是干到1点多,把这个首页写出来了。先看看看效果吧:…

Maven之JDK编译问题

IDEA Maven 默认使用 JDK 1.5 编译问题 IDEA 在「调用」maven 时,IDEA 默认都会采用 JDK 1.5 编译,不管你安装的 JDK 版本是 JDK 7 还是 JDK 8 或者更高。这样一来非常不方便,尤其是时不时使用 JDK 7/8 的新特性时。如果使用新特性&#xff…

opencv-python使用鼠标点击图片显示该点坐标和像素值IPM逆透视变换车道线二值化处理

OpenCV的鼠标操作 实现获取像素点的功能主要基于OpenCV的内置函数cv2.setMouseCallback(),即鼠标事件回调 setMouseCallback(winname, onMouse,userdata0) winname: 接收鼠标事件的窗口名称 onMouse: 处理鼠标事件的回调函数指针 userdata: 传给回调函数的用户数据…

学点Selenium玩点新鲜~,让分布式测试有更多玩法

前 言 我们都知道 Selenium 是一款在 Web 应用测试领域使用的自动化测试工具,而 Selenium Grid 是 Selenium 中的一大组件,通过它能够实现分布式测试,能够帮助团队简单快速在不同的环境中测试他们的 Web 应用。 分布式执行测试其实并不是一…

MATLAB高分辨率图片

把背景调黑,把曲线调黄,把grid调白,调调字体字号的操作 close all a0:0.1:10; noise2*rand(1,length(a)); bsin(a)sin(3*a)noise;plot(a,b,y,linewidth,2); ylim([-3 4]) %y轴范围 set(gca,xgrid,on,ygrid,on,gridlinestyle,-,Grid…

软考笔记——9.软件工程

软件工程的基本原理:用分阶段的生命周期计划严格管理、坚持进行阶段评审、实现严格的产品控制、采用现代程序设计技术、结果应能清除的审查、开发小组的人员应少而精、承认不断改进软件工程事件的必要性。 软件工程的基本要素:方法、工具、过程 软件生…

Apache-DBUtils

目录 封装方法 引出dbutils 案例 当关闭connection后,resultset结果集就无法使用了,这就使得resultset不利于数据的管理 封装方法 我们可以将结果集先存储在一个集合中,当connection关闭后,我们可以通过访问集合来访问结果集 …

渗透测试面试题汇总(附答题解析+配套资料)

注:所有的资料都整理成了PDF,面试题和答案将会持续更新,因为无论如何也不可能覆盖所有的面试题。 一、思路流程 1、信息收集 a、服务器的相关信息(真实ip,系统类型,版本,开放端口,…

(学习笔记-进程管理)怎么避免死锁?

死锁的概念 在多线程编程中,我们为了防止多线程竞争共享资源而导致数据错乱,都会在操作共享资源之前加上互斥锁,只有成功获得到锁的线程,才能操作共享资源,获取不到锁的线程就只能等待,直到锁被释放。 那…

网络通信原理TCP的四次断开连接(第四十九课)

FIN:发端完成发送任务标识。用来释放一个连接。FIN=1表明此报文段的发送端的数据已经发送完毕,并要求释放连接。 SEQ:序号字段。 TCP链接中传输的数据流中每个字节都编上一个序号。序号字段的值指的是本报文段所发送的数据的第一个字节的序号。 序列号为X ACK :确认号 。 …

Github下载任意版本的VsCode

下载历史版本VsCode(zip) 下载链接由三部分组成: 固定部分commit idVSCode-win32-x64-版本号.zip 固定部分: https://vscode.cdn.azure.cn/stable/ Commit id: 打开 vscode的GitHub:[https://github.com/microsoft/vscode/r…

Spring系列篇 -- Bean的生命周期

目录 经典面试题目: 一,Bean的生命周期图 二,关于Bean的生命周期流程介绍: 三,Bean的单例与多例模式 总结: 前言:今天小编给大家带来的是关于Spring系列篇中的Bean的生命周期讲解。在了解B…

innodb的锁

一致性锁定读和一致性非锁定读 Read Committed和Repetable Read级别下采用MVCC 实现非锁定读 但在一些情况下,要使用加锁来保障数据的逻辑一致性 自增列 锁的算法 唯一值 MySQL 中关于gap lock / next-key lock 的一个问题_呜呜呜啦啦啦的博客-CSDN博客 RR可以通过…

实践教程|基于 pytorch 实现模型剪枝

PyTorch剪枝方法详解,附详细代码。 一,剪枝分类 1.1,非结构化剪枝 1.2,结构化剪枝 1.3,本地与全局修剪 二,PyTorch 的剪枝 2.1,pytorch 剪枝工作原理 2.2,局部剪枝 2.3&#…

C++之ostream与ifstream读写文件操作(一百八十二)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生…