在安卓应用中实现Socket通信:创建服务端和客户端

介绍:本文主要介绍在安卓中使用Socket创建服务端和客户端进行通信,服务端可以管理多个客户端连接,完善的异常处理,接口回调,可以满足大部分需求,更多功能自行拓展…

关于Socket套接字:

是网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。

使用Socket进行网络通讯的一般步骤如下:

  1. 创建Socket对象,指定服务器的IP地址和端口号。
  2. 使用Socket对象的getInputStream()和getOutputStream()方法获取输入输出流,用于接收和发送数据。
  3. 使用输出流发送请求到服务器,使用输入流读取服务器返回的数据。
  4. 使用完毕后,通过调用Socket对象的close()方法关闭Socket连接。

创建Socket服务端

功能包括启动服务,关闭连接,发送消息,接收消息,管理客户端,将事件回调给外界等

class SocketServer(private val port: Int, private val listener: SocketServerListener? = null) : Thread() {private val TAG = "SocketServer"private lateinit var serverSocket: ServerSocket // serverSocket 对象private val clientHandlers = mutableListOf<ClientHandler>() // 存储每个客户端线程private val executor = Executors.newSingleThreadExecutor()override fun run() {startServer()}// 启动服务器的方法private fun startServer() {try {serverSocket = ServerSocket(port)Log.i(TAG, "Server is running on port $port")while (!isInterrupted) {val clientSocket = serverSocket.accept() // 接受客户端连接listener?.onClientConnected(clientSocket) // 通知客户端已连接val clientThread = ClientHandler(clientSocket, listener) // 为每个客户端创建一个新的线程clientThread.start()clientHandlers.add(clientThread)  // 安全地添加 Handler 到列表}} catch (e: Exception) {listener?.onError(e)}}// 关闭服务器的方法fun stopServer() {clientHandlers.forEach { it.closeConnection() } // 关闭所有客户端线程try {serverSocket.close() // 关闭 ServerSocket} catch (e: Exception) {e.printStackTrace()}}// 当线程被中断时,关闭服务器override fun interrupt() {stopServer()super.interrupt()}// 定义处理每个客户端连接的线程class ClientHandler(private val socket: Socket, private val listener: SocketServerListener? = null) : Thread() {private var printWriter: PrintWriter? = nullprivate var bufferedReader: BufferedReader? = nullfun sendMessage(msg: String) {if (printWriter != null) {synchronized(printWriter!!) {printWriter?.println(msg)}}}override fun run() {printWriter = PrintWriter(socket.getOutputStream(), true)bufferedReader = BufferedReader(InputStreamReader(socket.getInputStream()))try {var message: String? = null// 服务端接收数据while (socket.isConnected && bufferedReader?.readLine().also { message = it } != null) {// 当接收到消息时,通过 listener 通知外部listener?.onMessageReceived(message!!)// 回应客户端//printWriter?.println("Received: $message")}} catch (e: IOException) {listener?.onError(e)e.printStackTrace()} finally {// 确保最后会关闭连接closeConnection()}}// 关闭连接的方法fun closeConnection() {try {socket.close() // 关闭 socket 连接printWriter?.close()bufferedReader?.close()} catch (e: IOException) {listener?.onError(e)}Log.d("ClientHandler", "Client disconnected ${socket.inetAddress.hostAddress}")listener?.onClientDisconnected(socket) // 通知外部客户端已断开}}// 发送消息到客户端的方法fun sendMessage(message: String) {executor.submit {clientHandlers.forEach {it.sendMessage(message)}}}
}// 定义一个接口,用于接收消息和通知客户端连接和断开
interface SocketServerListener {fun onMessageReceived(message: String)fun onClientConnected(client: Socket)fun onClientDisconnected(client: Socket)fun onError(e: Exception)
}

创建Socket客户端

功能包括建立连接,关闭连接,发送消息,接收消息,异常处理,将事件回调给外界等

class SocketClient(private val serverAddress: String,private val serverPort: Int,private val listener: SocketClientListener?
) {private var socket: Socket? = nullprivate var out: PrintWriter? = nullprivate var reader: BufferedReader? = nullprivate val executor = Executors.newSingleThreadExecutor()private val connectLatch = CountDownLatch(1)fun connect() {executor.execute {try {socket = Socket(serverAddress, serverPort)out = PrintWriter(socket!!.getOutputStream(), true)reader = BufferedReader(InputStreamReader(socket!!.getInputStream()))listener?.onConnected()connectLatch.countDown() // 通知已经连接} catch (e: IOException) {listener?.onConnectionFailed(e)}}}fun sendMessage(message: String) {executor.execute {connectLatch.await() // 等待连接完成out?.println(message)}}fun receiveMessages() {executor.execute {try {connectLatch.await() // 等待连接完成var message: String? = nullwhile (reader?.readLine().also { message = it } != null) {listener?.onMessageReceived(message!!)}} catch (e: IOException) {listener?.onError(e)}}}fun close() {executor.execute {try {// socket 和 reader 关闭顺序不能变,否则会阻塞在 reader?.close()socket?.close()reader?.close()out?.close()listener?.onDisconnected()} catch (e: IOException) {listener?.onError(e)}}}
}interface SocketClientListener {fun onConnected()fun onMessageReceived(message: String)fun onConnectionFailed(e: Exception)fun onDisconnected()fun onError(e: Exception)
}

获取IP地址

public static String getIPAddress() {try {List<NetworkInterface> interfaces = Collections.list(NetworkInterface.getNetworkInterfaces());for (NetworkInterface intf : interfaces) {List<InetAddress> addrs = Collections.list(intf.getInetAddresses());for (InetAddress addr : addrs) {if (!addr.isLoopbackAddress() && addr instanceof Inet4Address) {return addr.getHostAddress();}}}} catch (Exception e) {Log.e("getIPAddress", e.toString());}return "";}

如何使用

服务端:

 val listener = object : SocketServerListener {override fun onMessageReceived(message: String) {XLog.i(" SocketServerListener  Message received from client: $message")}override fun onClientConnected(client: Socket) {XLog.i(" SocketServerListener  Client connected: ${client.inetAddress.hostAddress}")}override fun onClientDisconnected(client: Socket) {XLog.i(" SocketServerListener  disconnected: ${client.inetAddress.hostAddress}")}override fun onError(e: Exception) {XLog.i(" SocketServerListener  onError: ${e.message}")}}server = SocketServer(12345, listener)server.start()

客户端:

 val client = SocketClient(getIPAddress(), 12345, object : SocketClientListener {override fun onConnected() {XLog.i(" SocketClientListener  onConnected")}override fun onMessageReceived(message: String) {XLog.i(" SocketClientListener  onMessageReceived: $message")}override fun onConnectionFailed(e: Exception) {XLog.i(" SocketClientListener  onConnectionFailed: ${e.message}")}override fun onDisconnected() {XLog.i(" SocketClientListener  onDisconnected")}override fun onError(e: Exception) {XLog.i(" SocketClientListener  onError")}})client.connect()client.sendMessage("Hello, server!")client.sendMessage("啊哈哈哈 鸡汤来喽!!!")client.receiveMessages()

最后

此片文章用来记录学习,方便大家拿来即用,知识点不多,注释已经尽可能详细了,收工,下班!

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

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

相关文章

linux开发笔记(F1C200S)折腾weston桌面

参考文章&#xff1a; 1、嵌入式桌面&#xff08;1&#xff09;——weston桌面_qt weston-CSDN博客 2、https://blog.51cto.com/u_16213414/9171009 3、weston.ini: configuration file for Weston — the reference Wayland compositor | weston File Formats | Man Pages …

文献速递:深度学习医学影像心脏疾病检测与诊断--基于深度学习的PET图像重建与运动估计

Title 题目 Deep Learning Based Joint PET Image Reconstruction and Motion Estimation 基于深度学习的PET图像重建与运动估计 01 文献速递介绍 正电子发射断层扫描&#xff08;PET&#xff09;成像是一种非侵入性成像技术&#xff0c;通过使用放射性示踪剂在活体内可视化…

架构师:搭建Spring Security、OAuth2和JWT 的安全认证框架

1、简述 Spring Security 是 Spring 生态系统中的一个强大的安全框架,用于实现身份验证和授权。结合 OAuth2 和 JWT 技术,可以构建一个安全可靠的认证体系,本文将介绍如何在 Spring Boot 中配置并使用这三种技术实现安全认证,并分析它们的优点。 2、Spring Security Spri…

营销H5测试综述

H5页面是营销域最常见的一种运营形式&#xff0c;业务通过H5来提供服务&#xff0c;可以满足用户对于便捷、高效和低成本的需求。H5页面是业务直面用户的端点&#xff0c;其质量保证工作显得尤为重要。各业务的功能实现具有通用性&#xff0c;相应也有共性的测试方法&#xff0…

基于C++函数基础中的形参与实参

在C中&#xff0c;函数的形参&#xff08;形式参数&#xff09;是在函数定义时声明的参数&#xff0c;而实参&#xff08;实际参数&#xff09;是在函数调用时传递给函数的值或变量。 形参的作用是定义函数在执行时所需要的输入&#xff0c;它们在函数体内被当做局部变量使用。…

[1726]java试飞任务规划管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java试飞任务规划管理系统是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为 TOMCAT7.0,Myeclipse8.5开发&#xff0c;数据库为Mysql…

VS code放大缩小

​ 放大 ctrl和一起按 缩小 ctrl和-一起按 上面是键盘组合方式&#xff0c;如果需要Ctrl滚轮实现代码的缩放&#xff0c;可以这样。 在文件-->首选项-->设置-->用户设置&#xff1a; 在搜索栏输入mouseWheelZoom 选中即可。 ​ 输入 mouseWheelZoom 进行搜素 特…

linux 使用intel oneapi报错报错

使用intel oneapi 2024.1.0 时经常报这个错误 因为当前 intel2024.1.0没有在使用 需要改回2024.0.0并安装适配的torch的包来运行

Linux系统执行apt update报错 暂时不能解析域名“xxxx.xxx.com”

一、错误重现 错误:1 http://mirrors.aliyun.com/ubuntu jammy InRelease 暂时不能解析域名“mirrors.aliyun.com” 错误:2 http://mirrors.aliyun.com/ubuntu jammy-updates InRelease 暂时不能解析域名“mirrors.aliyun.com” 错误:3 http://mirrors.aliyun.com/ubuntu j…

1688工厂货源API接口:用于商品采集、商品搜索、商品详情数据抓取

item_get 获得1688商品详情item_search 按关键字搜索商品item_search_img 按图搜索1688商品&#xff08;拍立淘&#xff09;item_search_suggest 获得搜索词推荐item_fee 获得商品快递费用seller_info 获得店铺详情item_search_shop 获得店铺的所有商品item_password 获得淘口令…

机器学习:基于TF-IDF算法、决策树,使用NLTK库对亚马逊美食评论进行情绪分析

前言 系列专栏&#xff1a;机器学习&#xff1a;高级应用与实践【项目实战100】【2024】✨︎ 在本专栏中不仅包含一些适合初学者的最新机器学习项目&#xff0c;每个项目都处理一组不同的问题&#xff0c;包括监督和无监督学习、分类、回归和聚类&#xff0c;而且涉及创建深度学…

外贸人必备的9类工具合集-36个提高效率神器推荐!

做外贸&#xff0c;你需要的不只是一些基本的知识&#xff0c;还得有多种技能傍身&#xff0c;比如找新客户、写有效的邮件、分析竞争对手&#xff0c;还有在社交媒体上做营销但你可能发现了&#xff0c;有的人做外贸轻轻松松就能收到很多询盘&#xff0c;而有的人忙得团团转却…

python实现txt文件内容对比功能

欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~ 目录 一.前言 二.代码 三.演示 四.代码分析 一.前言 内容对比是一种常见的信息分析和研究方法,主要涉及对不同来源、类型或版本的内容进行比

HTTP请求三方接口绕过https证书检查

问题&#xff1a;在http请求https接口过程中经常会遇到SSL证书检查或者证书过期 ** sun.security.validator.ValidatorException: PKIX path validation failed: java.security.cert.CertPathValidatorException: validity check failed ** 解决办法&#xff1a;绕过证书检查…

[NSSRound#1 Basic]sql_by_sql

[NSSRound#1 Basic]sql_by_sql 这题没啥难的&#xff0c;二次注入盲注的套题 先注册&#xff0c;进去有个修改密码 可能是二次注入 修改密码处源码 <!-- update user set password%s where username%s; -->重新注册一个admin-- 获得admin身份&#xff08;原理看sqli-l…

分享10个高质量宝藏网站~

分享一波高质量宝藏网站~ 这10个宝藏网站&#xff0c;个个都好用到爆&#xff0c;娱乐、办公、学习都能在这里找到&#xff01; 1、Z-Library https://zh.zlibrary-be.se/ 世界最大的免费电子书下载网站&#xff01;电子书资源超千万&#xff0c;不过这个网站不太稳定&#…

MongoDB Atlas Vector Search与Amazon Bedrock集成已全面可用

亮点前瞻 ●MongoDB Atlas Vector Search知识库与Amazon Bedrock的最新集成&#xff0c;将极大加速生成式AI应用的开发。 ●诺和诺德利用MongoDB Atlas Vector Search与Amazon Bedrock集成&#xff0c;加速构建AI应用程序。 MongoDB&#xff08;纳斯达克股票代码&#xff1a…

springboot 整合 knife4j-openapi3

适用于&#xff1a;项目已使用shiro安全认证框架&#xff0c;整合knife4j-openapi3 1.引入依赖 <!-- knife4j-openapi3 --> <dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-openapi3-spring-boot-starter</artifa…

深度学习Day-16:实现天气预测

&#x1f368; 本文为&#xff1a;[&#x1f517;365天深度学习训练营] 中的学习记录博客 &#x1f356; 原作者&#xff1a;[K同学啊 | 接辅导、项目定制] 要求&#xff1a;根据提供的数据集对RainTomorrow进行预测 一、 基础配置 语言环境&#xff1a;Python3.7编译器选择…

【S32K3 MCAL配置】-7.2-GPT Driver:仿OS,周期/定时调用APP SWC和BSW模块的主函数

"><--返回「Autosar_MCAL高阶配置」专栏主页--> 案例背景:当没有移至FreeRTOS时,如何仿OS,快速搭建“若干个周期执行的Task”,在其中周期/定时调用APP SWC和BSW模块的主函数。 并在这个简易的仿OS中,如何设置“主函数调用的先后顺序”,以及如何设置“主函…