Java实现UDP与TCP应用程序
三、Java实现UDP应用程序
3.1 InetAddress类
java.net.InteAddress
类是用于描述IP地址和域名的一个Java类;
常用方法如下:
public static InetAddress getByName(String host)
:根据主机名获取InetAddress对象public String getHostName()
:获取该对象对应的主机名public String getHostAddress()
获取该对象对应的IP地址
示例代码:
package com.dfbz.demo01_实现UDP;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01_InetAddress {
public static void main(String[] args) throws UnknownHostException {
// 根据域名获取InetAddress对象
InetAddress localhost = InetAddress.getByName("localhost");
// 获取主机名
String hostName = localhost.getHostName();
// 获取ip地址
String hostAddress = localhost.getHostAddress();
System.out.println(hostName); // localhost
System.out.println(hostAddress); // 127.0.0.1
System.out.println("-----------");
InetAddress baidu = InetAddress.getByName("www.baidu.com");
System.out.println(baidu.getHostName()); // www.baidu.com
System.out.println(baidu.getHostAddress()); // 14.119.104.254
}
}
3.2 DatagramPacket类
java.net.DatagramPacket
是一种UDP协议的数据包结构,它包含源地址、目标地址和要传输的数据,用于封装一个UDP数据报文。
使用DatagramPacket,可以在应用程序之间发送和接收UDP数据包。
3.2.1 构造方法
public DatagramPacket(byte[] buf, int length, InetAddress address, int port)
:创建一个数据包对象buf
:要发送的内容length
:要发送的内容⻓度,单位字节address
:接收端的ip地址port
:接收端⼝号
public DatagramPacket(byte buf[], int length)
:创建一个数据包对象
示例代码:
package com.dfbz.demo01_实现UDP;
import java.net.DatagramPacket;
import java.net.InetAddress;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo02_DatagramPacket_构造方法 {
public static void main(String[] args) throws Exception {
// 创建一个UDP报文用于发送
DatagramPacket packet = new DatagramPacket(
"abc".getBytes(), // 报文封装的数据
"abc".getBytes().length, // 报文的数据长度
InetAddress.getByName("localhost"), // 接收端的地址
8989 // 端口
);
// 创建一个UDP报文用于接收
byte[] data = new byte[1024];
DatagramPacket packet2 = new DatagramPacket(
data,
data.length
);
}
}
3.2.2 常用方法
public synchronized int getLength()
:获取此UDP数据包载荷的数据长度(单位字节)public synchronized int getPort()
:获取此UDP数据包的目的端口号public synchronized byte[] getData()
:获取此UDP数据包的载荷部分(数据)
示例代码:
package com.dfbz.demo01_实现UDP;
import java.net.DatagramPacket;
import java.net.InetAddress;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo03_DatagramPacket_成员方法 {
public static void main(String[] args) throws Exception {
// 创建一个UDP报文用于发送UDP报文
DatagramPacket packet = new DatagramPacket(
"abc".getBytes(),
"abc".getBytes().length,
InetAddress.getByName("localhost"),
8989
);
byte[] data = packet.getData(); // 获取UDP报文的数据
int length = packet.getLength(); // 获取UDP报文的数据长度
InetAddress address = packet.getAddress(); // 获取该UDP报文要发送的地址
int port = packet.getPort(); // 获取该UDP报文要发送的端口
System.out.println("getData(): " + new String(data)); // abc
System.out.println("getLength(): " + length); // 3
System.out.println("getAddress(): " + address.getHostAddress()); // 127.0.0.1
System.out.println("getPort(): " + port); // 8989
}
}
3.2 DatagramSocket类
java.net.DatagramSocket
它提供了发送和接收UDP数据包的功能,用于描述一个UDP发送端或接收端;
在 Java 中,可以通过 DatagramSocket 类来创建一个套接字,并且可以通过 socket 的 receive 和 send 方法来发送和接收 UDP 数据包。这些方法提供了更好的灵活性,可以让您轻松地从多个地址和端口接收 UDP 数据包,并且可以选择性地发送数据包到特定地址和端口。 总的来说,DatagramSocket 是 UDP 协议的一种高级抽象,可以让您方便地在 Java 应用程序中发送和接收 UDP 数据包。
3.2.1 构造方法
public DatagramSocket(int port)
:通过端口构建一个发送端/接收端
示例代码:
DatagramSocket socket = new DatagramSocket(6969);
3.2.2 常用方法
public void send(DatagramPacket p)
:发送一个UDP数据包public synchronized void receive(DatagramPacket p)
:接收一个UDP数据包public void close()
:释放该Socket占用的资源
3.3 设计UDP应用程序
- 发送端:
package com.dfbz.demo01_实现UDP;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo04_发送端 {
public static void main(String[] args) throws Exception {
// 创建一个UDP报文的发送/接收器
DatagramSocket socket = new DatagramSocket();
// 封装一个UDP报文
byte[] data = new byte[8192];
DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("localhost"), 9999);
// 设置UDP报文的数据
packet.setData("你好".getBytes());
packet.setLength("你好".getBytes().length);
// 发送UDP报文
socket.send(packet);
// 关闭资源
socket.close();
}
}
- 接收端:
package com.dfbz.demo01_实现UDP;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo05_接收端 {
public static void main(String[] args) throws IOException {
// 创建一个UDP报文的发送/接收器
DatagramSocket socket = new DatagramSocket(9999);
// 创建一个UDP报文用于接收数据
byte[] data = new byte[8192];
DatagramPacket packet = new DatagramPacket(data, data.length);
/*
将socket接收到的UDP报文赋值给packet
注意: 如果没有收到发送端的UDP报文,这一行代码将会阻塞当前线程的执行
*/
socket.receive(packet);
socket.close();
System.out.println("接收到了数据: " + new String(data, 0, packet.getLength()));
}
}
四、Java实现TCP程序
在TCP通信中,分为数据的发送端(客户端)和接收端(服务器),当建立连接成功后(三次握手),才可以进行数据的发送;
在Java中,提供了两个类用于实现TCP通信程序:
- 1)客户端:
java.net.Socket
类表示;用于与服务器端建立连接,向服务器端发送数据报文等; - 2)服务端:
java.net.ServerSocket
类表示;用于与客户端的交互;
4.1 Socket
java.net.Sokcet
用于封装一个TCP应用程序的客户端;
4.1.1 构造方法
public Socket(String host, int port)
:创建套接字对象并将其连接到指定主机上的指定端口号。如果指定的host是null ,则相当于指定地址为本机地址。
示例代码:
Socket client = new Socket("127.0.0.1", 6868);
4.1.2 成员方法
public InputStream getInputStream()
: 返回此套接字的输入流。关闭生成的InputStream也将关闭相关的Socket。public OutputStream getOutputStream()
: 返回此套接字的输出流。关闭生成的OutputStream也将关闭相关的Socket。public void close()
:关闭此套接字。关闭此socket也将关闭相关的InputStream和OutputStream 。public void shutdownOutput()
: 禁用此套接字的输出流。任何先前写出的数据将被发送,随后终止输出流。
2.2 ServerSocket
ServerSocket
类:这个类实现了服务器套接字,该对象等待通过网络的请求。
4.2.1 构造方法
public ServerSocket(int port)
:使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上,参数port就是端口号。
构造举例,代码如下:
ServerSocket server = new ServerSocket(6666);
4.2.2 成员方法
public Socket accept()
:监听并接受连接,返回一个新的Socket对象,用于和客户端实现通信。该方法会一直阻塞直到建立连接。
4.3 设计TCP应用程序
- 客户端代码:
package com.dfbz.demo01_实现TCP;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01_客户端 {
public static void main(String[] args) throws Exception {
// 创建一个客户端
Socket socket = new Socket("localhost", 6666);
// 获取与服务器的输入流(用于读取服务器的数据)
InputStream is = socket.getInputStream();
// 获取与服务器的输出流(用于向服务器写出数据)
OutputStream os = socket.getOutputStream();
// 发送数据到服务器
os.write("你好呀~!在吗?".getBytes());
byte[] data = new byte[1024];
/*
读取服务器的数据
注意: 如果没有接收到服务器的数据,这一行代码将会阻塞当前线程的执行
*/
int len = is.read(data);
System.out.println("接收到来自服务器的信息【" + new String(data, 0, len) + "】");
os.write("在干嘛?".getBytes());
/*
读取服务器的数据
注意: 如果没有接收到服务器的数据,这一行代码将会阻塞当前线程的执行
*/
len = is.read(data);
System.out.println("接收到来自服务器的信息【" + new String(data, 0, len) + "】");
socket.close();
}
}
- 服务端代码:
package com.dfbz.demo01_实现TCP;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo02_服务端 {
public static void main(String[] args) throws Exception {
// 创建了一个服务器
ServerSocket serverSocket = new ServerSocket(6666);
/*
接收一个客户端
注意: 如果没有客户端来连接服务器,这一行代码将会阻塞当前线程的执行
*/
Socket client = serverSocket.accept();
// 获取与客户端的输入流(用于读取客户端的数据)
InputStream is = client.getInputStream();
// 获取与客户端的输出流(用于向客户端写出数据)
OutputStream os = client.getOutputStream();
byte[] data = new byte[1024];
/*
从客户端读取数据
注意: 如果没有接收到客户端的数据,这一行代码将会阻塞当前线程的执行
*/
int len = is.read(data);
System.out.println("接收到来自客户端的信息【" + new String(data, 0, len) + "】");
os.write("在哦!".getBytes());
/*
从客户端读取数据
注意: 如果没有接收到客户端的数据,这一行代码将会阻塞当前线程的执行
*/
len = is.read(data);
System.out.println("接收到来自客户端的信息【" + new String(data, 0, len) + "】");
os.write("在吃饭呢!".getBytes());
// 关闭资源
client.close();
serverSocket.close();
}
}
上述程序中,发送内容都是写死在代码中,我们使用Scanner来接受键盘录入的数据进行发送;
- 改造客户端程序:
package com.dfbz.demo02_使用Scanner改造;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01_客户端 {
public static void main(String[] args) throws Exception {
// 创建一个客户端,并且去连接服务器
Socket socket = new Socket("localhost", 7777);
// 获取与服务器的输入/输出流(用于读取服务器的数据/向服务器写出数据)
InputStream is = socket.getInputStream();
OutputStream out = socket.getOutputStream();
Scanner scanner = new Scanner(System.in);
byte[] data = new byte[1024];
while (true) {
String str = scanner.nextLine();
// 向服务器写出数据
out.write(str.getBytes());
// 读取服务器的信息
int len = is.read(data); // 这一句代码会造成阻塞,如果服务器没有向客户端写出数据,那么这一句代码阻塞
System.out.println("来自服务器的信息【" + new String(data, 0, len) + "】");
}
}
}
- 改造服务端程序:
package com.dfbz.demo02_使用Scanner改造;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo02_服务端 {
public static void main(String[] args) throws Exception {
// 创建一个服务器
ServerSocket serverSocket = new ServerSocket(7777);
// 接收到一个客户端(这一段代码会造成阻塞,如果没有客户端来连接服务器,那么代码会一直阻塞在这里)
Socket client = serverSocket.accept();
// 获取这个客户端的ip地址
String hostAddress = client.getInetAddress().getHostAddress();
// 获取这个客户端的端口
int port = client.getPort();
// 获取与这个客户端的输入/输出流(用于与这个客户端的读写操作)
InputStream is = client.getInputStream();
OutputStream os = client.getOutputStream();
Scanner scanner = new Scanner(System.in);
byte[] data = new byte[1024];
while (true) {
// 读取这个客户端的信息(如果客户端没有发送数据过来,这句代码会造成阻塞)
int len = is.read(data);
System.out.println("" + "来自客户端【" + hostAddress + "】" + "端口为【" + port + "】" + "的应用程序发送的消息【" + new String(data, 0, len) + "】");
// 接收控制台的数据. 同样的,如果控制台没有输入数据,那么这一行代码也会阻塞当前线程的执行
String str = scanner.nextLine();
os.write(str.getBytes());
}
}
}
五、综合案例
5.1 图片上传案例
5.2.1 UDP实现图片上传
UDP图片上传流程:
需要注意的是:由于UDP的特点(面向无连接、不安全等),因此采用UDP协议上传的图片会造成数据丢失,图片失真、丢失像素等问题;
【发送端】
package com.dfbz.demo01_图片上传;
import java.io.FileInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01_UDP_发送端 {
public static void main(String[] args) throws Exception {
// 创建一个套接字
DatagramSocket socket = new DatagramSocket();
// 从磁盘中读取文件
FileInputStream fis = new FileInputStream("100.png");
byte[] data = new byte[8192];
// 创建一个UDP数据包
DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getByName("localhost"), 9999);
int len;
while ((len = fis.read(data)) != -1) {
// 将从磁盘中读取到的数据设置到UDP报文中
packet.setData(data);
packet.setLength(len);
// 发送UDP报文给接收端
socket.send(packet);
}
// 发送一个空报文,作为结束标识
packet.setLength(0);
socket.send(packet);
// 释放资源
fis.close();
socket.close();
}
}
【接收端】
package com.dfbz.demo01_图片上传;
import java.io.FileOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo02_UDP_接收端 {
public static void main(String[] args) throws Exception {
// 创建一个套接字(用于接收UDP报文)
DatagramSocket socket = new DatagramSocket(9999);
byte[] data = new byte[8192];
// 创建一个UDP数据包
DatagramPacket packet = new DatagramPacket(data, data.length);
// 创建一个文件输出流,将接收到的数据写入到这个文件中
FileOutputStream fos = new FileOutputStream("udp.png");
while (true) {
// 接收UDP报文
socket.receive(packet);
// 获取报文数据长度
int length = packet.getLength();
if (length == 0) {
// 说明读取到了末尾
break;
}
fos.write(packet.getData(),0,length);
}
// 释放资源
fos.close();
socket.close();
}
}
查看上传之前的原文件大小和上传之后的文件大小:
5.2.2 TCP实现图片上传
TCP图片上传流程:
1)客户端首先通过输入流将自己磁盘中的图片读取到内存中
2)客户端通过TCP连接的输出流,向服务器写出刚刚读取到的图片数据
3)服务器通过TCP连接的输入流,将客户端刚刚发送过来的图片数据读取到内存中
4)服务器通过输出流将内存中的数据写入到服务器的磁盘中
Tips:我们学习过程中,将服务器和客户端放在同一台机器。但实际开发中服务器和客户端不是在同一台机器,
【客户端代码实现】
package com.dfbz.demo02;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01_Server {
public static void main(String[] args) throws Exception {
// 声明服务器
ServerSocket serverSocket = new ServerSocket(8888);
// 接收到一个客户端
Socket client = serverSocket.accept();
System.out.println(client.getInetAddress().getHostAddress() + "连接成功");
// 读取客户端传递过来的数据
InputStream is = client.getInputStream();
// 随机生成一个文件名写出到磁盘
FileOutputStream fos = new FileOutputStream(UUID.randomUUID().toString() + ".png");
byte[] data = new byte[1024];
int len;
while ((len = is.read(data)) != -1) {
// 写出到磁盘
fos.write(data, 0, len);
}
// 释放资源
fos.close();
client.close();
}
}
【服务端代码实现】
package com.dfbz.demo01_图片上传;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo04_TCP_服务端 {
public static void main(String[] args) throws Exception {
// 声明服务器
ServerSocket serverSocket = new ServerSocket(8888);
// 接收到一个客户端
Socket client = serverSocket.accept();
System.out.println(client.getInetAddress().getHostAddress() + "连接成功");
// 读取客户端传递过来的数据
InputStream is = client.getInputStream();
// 随机生成一个文件名写出到磁盘
FileOutputStream fos = new FileOutputStream("tcp.png");
byte[] data = new byte[1024];
int len;
while ((len = is.read(data)) != -1) {
// 写出到磁盘
fos.write(data, 0, len);
}
// 释放资源
fos.close();
client.close();
}
}
查看上传之前的原文件大小和上传之后的文件大小:
5.2.3 多线程改进TCP图片上传
实际开发中一个服务器对应N多个客户端,其他客户端均可以上传图片。我们的代码在同一时间只允许一个人上传图片,如果这个人上传的文件较大,那么势必会造成其他用户处于等待状态;针对这种情况我们可以使用多线程来解决。
服务器每次接受到一个客户端时,都开启一个线程来独立处理这个客户端的上传任务。这样在很多人同时来上传文件时,都可以一起上传。
- 多线程改进服务器:
package com.dfbz.demo01_图片上传;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo05_TCP_服务端_多线程 {
public static void main(String[] args) throws Exception {
// 创建一个服务器
ServerSocket serverSocket = new ServerSocket(8888);
while (true) {
// 每接收到一个客户端都创建一个新的线程为它服务
Socket client = serverSocket.accept();
System.out.println("客户端【" + client.getInetAddress().getHostAddress() + "】连接成功啦!");
new Thread() {
@Override
public void run() {
try {
// 获取与客户端的输入流(用于读取客户端发送过来的字节)
InputStream is = client.getInputStream();
// 关联本地的一个文件(随机生成一个名称)
FileOutputStream fos = new FileOutputStream(UUID.randomUUID() + ".png");
byte[] data = new byte[8192];
int len;
while ((len = is.read(data)) != -1) {
fos.write(data, 0, len);
}
fos.close();
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
}
5.2 在线聊天案例
我们刚刚使用了TCP完成了聊天功能的编写;我们会发现我们的程序是由问题的,就是读写是串行的!
我们整个应用程序只有一个线程,那就mian线程,代码都是从上往下执行,如果main线程当前在读操作,那么就不能写。而且如果此时一方如果没有发送信息给另一方,那么另一方的read方法将会一直处于阻塞状态,代码不会往下执行;此时想往对方写出数据肯定是不行的;
我们利用多线程技术来改造我们之前的代码,让我们的代码既一直读,又可以一直写;
5.2.1 多线程UDP在线聊天案例
【多线程改进发送端】
package com.dfbz.demo02_多线程实现聊天案例;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01_UDP_发送端 {
public static void main(String[] args) throws Exception {
new Thread(() -> {
try {
// 1. 创建一个套接字,用于发送数据到接收端
DatagramSocket socket = new DatagramSocket();
// 2. 创建一个UDP报文,用于封装要发送到接收端的数据(接收端的端口为9999)
byte[] data = new byte[8192];
DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getLocalHost(), 9999);
// 3. 死循环发送信息给接收端
Scanner scanner = new Scanner(System.in);
while (true) {
// 接收键盘录入数据
String line = scanner.nextLine();
// 将数据设置到UDP报文中
packet.setData(line.getBytes());
packet.setLength(line.getBytes().length);
// 将此UDP报文发送给接收端
socket.send(packet);
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
// 1. 创建一个套接字,用于接收来自接收端的数据(发送端的端口为7777)
DatagramSocket socket = new DatagramSocket(7777);
// 2. 创建一个UDP报文,用于接收来自接收端的数据
byte[] data = new byte[8192];
DatagramPacket packet = new DatagramPacket(data, data.length);
// 3. 死循环接收来自接收端响应的信息
while (true) {
// 接收来自接收端的UDP报文
socket.receive(packet);
data = packet.getData();
int len = packet.getLength();
System.out.println("来自接收端【" + packet.getAddress().getHostAddress() + "】的信息: 【" + new String(data, 0, len) + "】");
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
【多线程改进接收端】
package com.dfbz.demo02_多线程实现聊天案例;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo02_UDP_接收端 {
public static void main(String[] args) throws Exception {
// 接收端的读线程
new Thread(() -> {
try {
// 1. 创建一个套接字,用于接收来自发送端的数据(接收端的端口为9999)
DatagramSocket socket = new DatagramSocket(9999);
// 2. 创建一个UDP报文,用于接收来自发送端的数据
byte[] data = new byte[8192];
DatagramPacket packet = new DatagramPacket(data, data.length);
// 3. 死循环接收来自发送端响应的信息
while (true) {
// 接收来自发送端发送的UDP报文
socket.receive(packet);
data = packet.getData();
int len = packet.getLength();
System.out.println("来自发送端的【" + packet.getAddress().getHostAddress() + "】的信息: 【" + new String(data, 0, len) + "】");
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
// 接收端的写线程
new Thread(() -> {
try {
// 1. 创建一个套接字,用于发送数据给发送端
DatagramSocket socket = new DatagramSocket();
// 2. 创建一个UDP报文,用于封装要发送到发送端的数据(发送端的端口为7777)
byte[] data = new byte[8192];
DatagramPacket packet = new DatagramPacket(data, data.length, InetAddress.getLocalHost(), 7777);
// 3. 死循环发送信息给发送端
Scanner scanner = new Scanner(System.in);
while (true) {
// 接收键盘录入数据
String line = scanner.nextLine();
// 将数据设置到UDP报文中
packet.setData(line.getBytes());
packet.setLength(line.getBytes().length);
// 将此UDP报文发送给发送端
socket.send(packet);
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
5.2.2 多线程TCP在线聊天案例
【多线程改进客户端】
package com.dfbz.demo02_多线程实现聊天案例;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo03_TCP_客户端 {
public static void main(String[] args) throws IOException {
// 创建一个客户端,去连接服务器
Socket socket = new Socket("127.0.0.1", 9999);
// 获取与服务器的输入输出流
InputStream is = socket.getInputStream();
OutputStream os = socket.getOutputStream();
// 读线程,专门用于读取服务器的信息
new Thread() {
@Override
public void run() {
try {
byte[] data = new byte[1024];
while (true) {
/*
一直死循环读取服务器发送过来的数据
如果服务器没有数据来也只是阻塞当前线程,并不会影响其他线程
*/
int len = is.read(data);
System.out.println("接收到来自服务器的信息【" + new String(data, 0, len) + "】");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
// 写线程,专门向服务器写出数据
new Thread() {
@Override
public void run() {
try {
Scanner scanner = new Scanner(System.in);
while (true) {
/*
一直死循环读取键盘录入的数据
如果键盘没有输入数据,也只是阻塞当前线程,并不会影响其他线程
*/
String str = scanner.nextLine();
os.write(str.getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
【多线程改进服务端】
package com.dfbz.demo02_多线程实现聊天案例;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo04_TCP_服务端 {
public static void main(String[] args) throws Exception {
// 创建一台服务器
ServerSocket serverSocket = new ServerSocket(9999);
// 接收一个客户端
Socket client = serverSocket.accept();
// 获取与这个客户端的输入输出流
InputStream is = client.getInputStream();
OutputStream os = client.getOutputStream();
// 获取客户端的IP地址
String hostAddress = client.getInetAddress().getHostAddress();
// 获取客户端的应用程序端口
int port = client.getPort();
System.out.println("有一个客户端来连接了,地址【" + hostAddress + "】,端口【" + port + "】");
// 读线程
new Thread() {
@Override
public void run() {
try {
byte[] data = new byte[1024];
while (true) {
/*
一直死循环读取客户端发送过来的数据
如果客户端没有数据来也只是阻塞当前线程,并不会影响其他线程
*/
int len = is.read(data);
System.out.println("接收到了来自客户端【" + hostAddress + "】的信息【" + new String(data, 0, len) + "】");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
// 写线程
new Thread() {
@Override
public void run() {
try {
Scanner scanner = new Scanner(System.in);
while (true) {
/*
一直死循环读取键盘录入的数据
如果键盘没有输入数据,也只是阻塞当前线程,并不会影响其他线程
*/
String str = scanner.nextLine();
os.write(str.getBytes());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}