Java 实现简单的 Socket 通信
Java socket 封装了传输层的实现细节,开发人员可以基于 socket 实现应用层。本文介绍了 Java socket 简单用法。
1. 传输层协议
传输层包含了两种协议,分别是 TCP (Transmission Control Protocol,传输控制协议) 和 UDP (User Datagram Protocol,用户数据报协议)。
TCP 是一种面向连接,可靠的流协议。通信双方在“发送-接收”数据之前需要先建立 TCP 连接,然后通过互相发送二进制数据流来进行通信。所谓连接,指的是各种设备、线路,或网络中进行通信的应用程序为了相互传递消息而建立的专有、虚拟的通信线路。连接一旦建立,进行通信的应用程序只使用该虚拟的通信线路发送和接收数据。TCP 还需要处理端到端之间的流量控制。
UDP 是一种无连接的,不可靠的数据报协议。发送方不需要与接收方建立连接,通信双方通过发送一个个独立的数据报来进行通讯。
TCP 通过序列号、确认应答、数据校验等机制确保了传输的可靠性,适用于需要可靠数据传输的场景,应用层协议 HTTP,FTP 基于 TCP。UDP 没有复杂的控制机制,不纠错,不重发,不保证数据的准确性,不确保数据到达目的地;不过 UDP 传送等量数据花费更小的流量,适用于对时延要求高但对准确性要求不高的场景,如视频、音频通讯。
Java 中有 3 种套接字类,java.net.Socket 和 java.net.ServerSocket 基于 TCP,java.net.DatagramSocket 基于 UDP。
2. TCP 示例
TCP 是面向连接的,所以在进行通讯之前发送端(客户端)需要先连接到接收端(服务端)。客户端通过 new Socket("localhost", 9090) 来创建一个连接到服务端的套接字,这个套接字连接到主机 localhost 的 9090 端口。
ServerSocket 实现服务端套接字,通过 new ServerSocket(9090) 来创建一个监听端口为 9090 实例;ServerSocket.accept() 方法会阻塞等待客户端的连接,一旦有连接过来,会返回一个服务端的 Socket 实例。连接建立完成,客户端 Socket 实例和服务端 Socket 实例就可以面向输入输出流发送数据了。
2.1 示例效果
客户端程序接收控制台输入的内容,客户端控制台每输入一行,就往服务端发送,服务端接收到消息之后,将消息打印到控制台。
客户端输入 "Bye" 时,客户端断开与服务端的连接,客户端程序退出,服务端程序继续等待连接。
客户端控制台输入输出:
$ java Server.java
Bind Port 9090
New client connected.
Received Message --> Are you OK!
服务端控制台输出:
$ java Client.java
Are you OK!
Send Msg --> Are you OK!
Bye
$
2.2 服务端程序代码
import java.net.*;
import java.io.*;
class Server {
public static void main(String[] args) {
// ServerSocket 实现了 AutoCloseable 接口,所以支持 try-with-resource 语句
// 创建一个 ServerSocket,监听 9090 端口
try(ServerSocket serv = new ServerSocket(9090)){
System.out.printf("Bind Port %d\n", serv.getLocalPort());
Socket socket = null;
while(true){
// 接收连接,如果没有连接,accept() 方法会阻塞
socket = serv.accept();
// 获取输入流,并使用 BufferedInputStream 和 InputStreamReader 装饰,方便以字符流的形式处理,方便一行行读取内容
try(BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream()) )){
String msg = null;
char[] cbuf = new char[1024];
int len = 0;
while( (len = in.read(cbuf, 0, 1024)) != -1 ){ // 循环读取输入流中的内容
msg = new String(cbuf, 0, len);
if("Bye".equals(msg)) { // 如果检测到 "Bye" ,则跳出循环,不再读取输入流中内容。
break;
}
System.out.printf("Received Message --> %s \n", msg);
}
}catch (IOException e){
e.printStackTrace();
}
}
}catch (IOException e){
e.printStackTrace();
}
}
}
2.3 客户端程序代码
import java.net.*;
import java.io.*;
import java.util.*;
class Client{
public static void main(String[] args){
try(Socket socket = new Socket("localhost", 9090)){
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter("\r\n");
String msg = null;
while( !(msg = scanner.next()).equals("Bye") ){
System.out.printf("Send Msg --> %s \n", msg);
out.write(msg);
out.flush(); // 立即发送,否则需要积累到一定大小才一次性发送
}
}catch (IOException e){
e.printStackTrace();
}
}
}
3. UDP 示例
UDP 不需要连接,客户端与服务端通过发送数据报来完成通信。Java 中使用 java.net.DatagramSocket 来表示 UDP 客户端或服务端的套接字,使用 java.net.DatagramPacket 来表示 UDP 的数据报。客户端和服务端可以直接向对方发送数据报,不需要进行连接。
下面代码基于 UDP 实现了与上面程序同样的功能。不过消息可能会出错,某些消息可能也不能到达服务端。
3.1 服务端程序代码
import java.net.*;
import java.io.*;
class Server {
public static void main(String[] args){
// 创建一个 DatagramPacket 实例,用来接收客户端发送过来的 UDP 数据报,这个实例可以重复利用。
byte[] buf = new byte[8192]; // 缓存区
int len = buf.length; // 要利用的缓存区的大小
DatagramPacket pac = new DatagramPacket(buf, len);
// 创建服务端的套接字,需要指定绑定的端口号
try(DatagramSocket serv = new DatagramSocket(9191)){
while(true){
serv.receive(pac); // 接收数据报。如果没有数据报发送过来,会阻塞
System.out.println("Message --> " + new String(pac.getData(), 0, pac.getLength()));
}
}catch (IOException e){
e.printStackTrace();
}
}
}
3.2 客户端程序代码
import java.io.*;
import java.net.*;
import java.util.*;
class Client {
public static void main(String[] args){
// 创建一个客户端的 UDP 套接字,不需要指定任何信息
try(DatagramSocket client = new DatagramSocket()){
// 创建一个数据报实例,数据和长度在发送之前都会重新设置,所以这里直接置为 0 即可。
// 由于是发送端,所以需要设置服务端的地址和端口
DatagramPacket pac = new DatagramPacket(new byte[0], 0, InetAddress.getByName("localhost"), 9191);
// 扫描控制台输入
Scanner scanner = new Scanner(System.in);
scanner.useDelimiter("\r\n");
String msg = null;
while( !(msg = scanner.next()).equals("Bye") ){
// 设置要发送的数据
pac.setData(msg.getBytes());
// 发送数据报
client.send(pac);
System.out.println("Sent Message --> " + msg);
}
}catch (IOException e){
e.printStackTrace();
}
}
}
需要注意的是,UDP 是面向无连接的,但 DatagramSocket 的 API 中提供了带有 connect 字样的方法,这里的 connect 并非 TCP 中连接的意思。而是指定了当前的 UDP 套接字只能够向指定的主机和端口发送数据报。
Java 实现简单的 Socket 通信的更多相关文章
- Java实现简单的socket通信
今天学习了一下java如何实现socket通信,感觉难点反而是在io上,因为java对socket封装已经很完善了. 今天代码花了整个晚上调试,主要原因是io的flush问题和命令行下如何运行具有pa ...
- 我看不下去鸟。。。。Java和C#的socket通信真的简单吗?
这几天在博客园上看到好几个写Java和C#的socket通信的帖子.但是都为指出其中关键点. C# socket通信组件有很多,在vs 使用nuget搜索socket组件有很多类似的.本人使用的是自己 ...
- Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制
Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制 JAVA 中原生的 socket 通信机制 摘要:本文属于原创,欢迎转载,转载请保留出处:https://github.co ...
- Android简单实现Socket通信,client连接server后,server向client发送文字数据
案例实现的是简单的Socket通信,当client(Androidclient)连接到指定server以后,server向client发送一句话文字信息(你能够拓展其他的了) 先看一下服务端程序的实现 ...
- php简单实现socket通信
socket通信的原理在这里就不说了,它的用途还是比较广泛的,我们可以使用socket来做一个API接口出来,也可以使用socket来实现两个程序之间的通信,我们来研究一下在php里面如何实现sock ...
- Linux下简单的socket通信实例
Linux下简单的socket通信实例 If you spend too much time thinking about a thing, you’ll never get it done. —Br ...
- java和C#之间SOCKET通信的问题
转自:http://www.cdtarena.com/javapx/201307/9170.html java和C#之间SOCKET通信的问题 一.服务器端(使用java编写) /** * 监听客户端 ...
- Java和C#的socket通信相关(转)
这几天在博客园上看到好几个写Java和C#的socket通信的帖子.但是都为指出其中关键点. C# socket通信组件有很多,在vs 使用nuget搜索socket组件有很多类似的.本人使用的是自己 ...
- Java进阶(四十七)Socket通信
Java进阶(四十七)Socket通信 今天讲解一个 Hello Word 级别的 Java Socket 通信的例子.具体通讯过程如下: 先启动Server端,进入一个死循环以便一直监听某端口是 ...
随机推荐
- WinForm 加载大数据时不闪烁的ListView
自己之前做小工具的时候,遇到这个问题,记录一下... namespace 相册处理 { //将ListView重载为此新类,解决加载过程中闪烁的问题 //在designer.cs中改写: //priv ...
- POJ2466 棋盘覆盖
一张\(n*m\)的棋盘,有\(k\)个点不能被覆盖,问其余点能不能被\(1*2\)的小矩形完全覆盖,多测 这题先输入\(m\)是什么鬼啊!!! 其实是一个比较裸的二分图判定,把\(k\)个点挖去然后 ...
- 题解-NOI2016 优秀的拆分
NOI2016 优秀的拆分 \(T\) 组测试数据.求字符串 \(s\) 的所有子串拆成 \(AABB\) 形式的方案总和. 数据范围:\(1\le T\le 10\),\(1\le n\le 3\c ...
- 深入理解Java虚拟机(十)——线程安全与锁优化
什么是线程安全 当多个线程同时访问一个对象的时候,不需要考虑什么额外的操作就能获取正确的值,就是线程安全的. 线程安全的程度 1.不可变 不可变的对象一定是线程安全的,因为值始终只有一个. final ...
- Python 学习笔记 之 01 - 基础总结
数据类型 整数 十六进制和八进制使用0开头,0x12f, 010 浮点数 可以用科学记数法,如1.23x10^9 可以写成 12.3e8 ,0.000012可以写成 1.2e-5 空值 用None表示 ...
- Python最会变魔术的魔术方法,我觉得是它!
在上篇文章中,我有一个核心的发现:Python 内置类型的特殊方法(含魔术方法与其它方法)由 C 语言独立实现,在 Python 层面不存在调用关系. 但是,文中也提到了一个例外:一个非常神秘的魔术方 ...
- dataframe,list,numpy之间的互相转换
dataframe,numpy,list之间的互相转换 由于目前学校要做一些数据分析处理的作业有要用到dataframe,list,numpy之间的转化,所以在此总结一下这些用法. dataframe ...
- 《Stereo R-CNN based 3D Object Detection for Autonomous Driving》论文解读
论文链接:https://arxiv.org/pdf/1902.09738v2.pdf 这两个月忙着做实验 博客都有些荒废了,写篇用于3D检测的论文解读吧,有理解错误的地方,烦请有心人指正). 博客原 ...
- kali2020创建root用户
自kali更新到2020版后,默认取消了root用户的登录权限.只能用普通用户登录,这样做的优点在于对于kali的新手,在不懂的部分命令的情况下对系统的损害有所降低,也就说安全性提高了.但是普通用户权 ...
- 用python写图片格式批量处理工具
一.思路分析 其实,照片处理要求很简单,主要是两个方面:一个是调整图片尺寸(即宽x高),另一个是调整图片的大小(即压缩).为了实现这两个功能,利用python中的PIL库即可,其安装方法如下: pip ...