当前位置: 首页 > news >正文

Kotlin的Socket连接与UDP广播和接收

TCP建立连接

服务端

val server = ServerSocket(0) val client = server.accept()

这段代码可以生成一个端口随机的ServerSocket,其中可以用

InetAddress.getLocalHost().hostAddress获得ip地址,

server.localPort获得随机到的端口。

注意,其中的accept()是阻塞的,且accept()返回的是TCP的连接(client),但这里的client和客户端的socket并不是一个对象。

客户端

val socket = Socket(serverAddress, serverPort)

根据ip和端口生成一个socket,这里不是阻塞的,因此找不到就会直接报错。

数据交流

注意,客户端和服务端均可以主动发送消息和接收消息。且方法差不多,但要注意的是用的其实都是Socket对象,而不是ServerSocket对象,因为Socket才是连接。而ServerSocket的身份更像是一个门卫,可以产生连接却不可以自己进行连接。(可以产生多个连接,一个client对应一个socket)

socket间传输的本质都是Byte数组。当然,开发的时候直接用Byte数组十分麻烦,因为有两位可以依仗的类:PrintWriter和BufferedReader。

发送(PrintWriter)

val output = PrintWriter(client.getOutputStream(), true) val output = PrintWriter(socket.getOutputStream(), true)

两者都是向对面发送信息的工具对象,使用也很简单。

output.println("Hello!")

这里单次传输用println,至于为什么不用print等下会说。

接收(BufferedReader)

val input = BufferedReader(InputStreamReader(socket.getInputStream())) val input = BufferedReader(InputStreamReader(client.getInputStream()))
input.readLine()

readline()会等待一行结束,然后返回String类型的数据,如果对面端正常关闭(.close())的话就会返回null。因为等待的是一行结束,所以有output.print()时次函数会继续阻塞,且结果会在一行内积累。当然,如果你不希望不立即发送可以用print().

文件传输

文件本身也就一个byte数组。大体思路是把文件转为byte数组然后发送,当然有更好的方法。这里我们要知道socket的传输是依赖于socket.getOutputStream()和client.getInputStream()对象上的,而BufferedReader和PrintWriter本身作为便利文本发送,但我们可以直接用前者的函数write()和read()。

发送和接收两者十分对称。分别是读文件,写网络流和写文件,读网络流。代码如下:

// 发送 val socket = Socket("服务器IP", 12345) val fis = FileInputStream("test.jpg") val out = socket.getOutputStream() val buffer = ByteArray(1024) var len: Int while (fis.read(buffer).also { len = it } != -1) { out.write(buffer, 0, len) } socket.shutdownOutput() // 很关键! fis.close() socket.close() // 接收 val server = ServerSocket(12345) val client = server.accept() val input = client.getInputStream() val fos = FileOutputStream("copy.jpg") val buffer = ByteArray(1024) var len: Int while (input.read(buffer).also { len = it } != -1) { fos.write(buffer, 0, len) } fos.close() client.close() server.close()

我们不可能一次发送就把一个大图片发完。因为我们会一段一段的发,所以说socket.shutdownOutput(); 很关键。它告诉了接收端传输已完成。这里的read()是不断地抽数据到buffer,然后传输,直到传输完长度为-1。这种代码多看看多写写就差不多熟了,当然大家可以去更详细地了解(文件)输入,输出流。

UDP广播(Broadcast动名同形)

相信不少人玩过星露谷,我的世界,泰拉瑞亚,元气骑士这样的支持局域网联机游戏,即使创建房间打开联机后的端口随机,也不知道对方的ip,但就是会显示在另一个设备局域网列表上。这依赖的就是广播。

发送广播

import java.net.DatagramPacket import java.net.DatagramSocket import java.net.InetAddress fun main() { val broadcastIP = "255.255.255.255" // 子网广播地址 val port = 9999 val socket = DatagramSocket() socket.broadcast = true // 允许发送广播 val message = "Hello, LAN!" val buffer = message.toByteArray() val packet = DatagramPacket(buffer, buffer.size, InetAddress.getByName(broadcastIP), port) socket.send(packet) println("广播消息发送成功") socket.close() }

接收广播

import java.net.DatagramPacket import java.net.DatagramSocket fun main() { val port = 9999 val socket = DatagramSocket(null) socket.reuseAddress = true // 可选,可使多个客户端在同一个端口听广播 socket.bind(InetSocketAddress(port)) val buffer = ByteArray(1024) println("等待局域网广播...") while (true) { val packet = DatagramPacket(buffer, buffer.size) socket.receive(packet) val msg = String(packet.data, 0, packet.length) println("收到广播: $msg 来自 ${packet.address}:${packet.port}") } }

这里要注意的是UDP广播并不适合传太多数据,通常操作是用广播穿端口号,和ip(广播本身有ip信息)。这里适合的话推荐让广播循环播放几次,毕竟UDP广播会有丢包情况,不一定一次搞定。

http://www.jsqmd.com/news/555575/

相关文章:

  • 跨平台虚拟机自动化控制:从繁琐操作到一键部署的效率革命
  • 终极指南:3分钟掌握QMK Toolbox键盘固件刷写技巧
  • 为什么92%的工业网关Python配置在上线72小时内崩溃?——基于37个真实产线案例的配置健壮性白皮书
  • Mac Mouse Fix技术架构深度解析:从Objective-C到Swift的混合架构演进
  • MQTT遗嘱消息实战:如何用LWT+保留消息打造智能家居设备离线预警系统
  • WhisperLive:高性能实时语音转文本架构解析与多引擎优化方案
  • 3种PostHog部署模式:为不同规模团队定制的数据分析平台搭建指南
  • 华三M-LAG实战:从零构建高可用数据中心网络
  • OpenClaw异常处理大全:nanobot任务失败自救指南
  • Agnet
  • foobox-cn:让foobar2000音乐播放体验提升300%的开源界面增强工具
  • springboot-vue基于web的小说在线阅读平台
  • springboot-vue基于web的智慧党建平台设计与实现
  • 微信小游戏过审实战:JS混淆与马甲包规避技巧
  • Pixel Dream Workshop参数详解:CFG/Steps/Scale三维度精准控制像素粒度
  • 3个技巧让LibreTranslate翻译模型部署速度提升80%
  • 中西医结合内科主治备考,找对机构才靠谱 - 医考机构品牌测评专家
  • 模拟IC设计中的‘效率’权衡:深入理解gm/ID如何平衡增益、带宽与噪声
  • 别只当摆设!深度挖掘Kylin V10 SP1安全中心的‘应用保护’与‘设备安全’实战用法
  • 【架构实战】数据备份与灾难恢复策略
  • 别只测正常工况了!用CAPL给ECU做‘压力测试’:模拟总线错误全场景复盘
  • Django+MySQL遇到emoji报错?5分钟搞定utf8mb4字符集配置
  • 别再让用户下载乱码文件了!华为云OBS临时链接重命名实战(Java版)
  • 别再死记硬背命令了!用eNSP模拟器搞懂三层交换的‘一次路由,多次交换’
  • 实测!新疆护栏定制工厂哪家靠谱?新疆昆仑宏博护栏厂 本地自营 按需定制 全方位测评(市政/小区/工地适用) - 宁夏壹山网络
  • OpenClaw技能开发入门:基于nanobot定制个人自动化模块
  • 计算机毕业设计springboot盐城市亭湖区药店销售管理系统 基于SpringBoot的盐城亭湖区医药零售信息化管理平台 亭湖区智慧药店进销存与在线服务系统
  • JekyllNet .Net 版本的Jekyll , 你博客 文档的静态生成利器 。
  • gitlab-ci-local 社区贡献指南:如何参与项目开发和功能改进
  • STM32 Bootloader跳转失败?别慌!可能是你的APP2固件链接地址没烧对(附ST-LINK Utility操作指南)