版权声明:本文为博主原创文章,转载请说明出处,谢谢。 https://blog.csdn.net/qq_27070443/article/details/61619387
主要内容:
多线程的应用
读写分离思想
服务器转发
服务器转发是什么呢?
可以假想服务器是一个大型的数据中心,按照一定的规则,将信息送到你想送去的地方。
而且,这里的规则是由开发者自己定义的。
源码实例:
客户端
package me.mxzf;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
/**
*
* @Title: Client
* @Dscription: 客户端
* @author Deleter
* @date 2017年3月12日 下午2:29:00
* @version 1.0
*/
public class Client {
public static void main(String[] args) throws UnknownHostException,
IOException {
// 创建Socket实例
Socket socket = new Socket(InetAddress.getLocalHost(), 1234);
// 包装写
new ThreadWriter(socket).start();
// 包装读
new ThreadReader(socket).start();
}
}
客户端读线程
package me.mxzf;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
/**
*
* @Title: ThreadReader
* @Dscription: 读线程
* @author Deleter
* @date 2017年3月12日 下午2:32:46
* @version 1.0
*/
public class ThreadReader extends Thread {
private Socket socket;
private DataInputStream dis;
private String content;
public ThreadReader(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
// 用DataInputStream包装socket的输入流
dis = new DataInputStream(this.socket.getInputStream());
while (true) {
// 一直阻塞,直到有消息到来,进入循环体
while ((content = dis.readUTF()) != null) {
// 打印接收到的消息
System.out.println(content);
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端写线程
package me.mxzf;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
/**
*
* @Title: ThreadWriter
* @Dscription: 写线程
* @author Deleter
* @date 2017年3月12日 下午2:33:25
* @version 1.0
*/
public class ThreadWriter extends Thread {
private Socket socket;
private DataOutputStream dos;
public ThreadWriter(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try {
// 用DataOutputStream包装socket的输出流
dos = new DataOutputStream(socket.getOutputStream());
while (true) {
// 等待3后
Thread.sleep(3000);
// 向服务器发送"Hello World!"
dos.writeUTF("Client:Hello World!");
// 刷新缓冲区,防止粘包
dos.flush();
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
服务器
package me.mxzf;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashSet;
/**
*
* @Title: Server
* @Dscription: 服务器
* @author Deleter
* @date 2017年3月12日 下午2:28:09
* @version 1.0
*/
public class Server {
// 创建静态的集合来存放"客户通道"
public static HashSet<ServerThread> lists = new HashSet<>();
public static void main(String[] args) throws IOException {
// 创建ServerSocket实例
ServerSocket server = new ServerSocket(1234);
// 在循环的外部定义变量,节约内存空间
Socket socket;
ServerThread serverThread;
while (true) {
// 阻塞直到有客户端接入
socket = server.accept();
// 包装Socket
serverThread = new ServerThread(socket, lists);
serverThread.start();
// 将当前接入的"客户通道"保存在集合中
lists.add(serverThread);
}
}
/**
* 服务器转发
*
* 这种遍历的方式,效率极低,不介意使用
*
* 但是为了初学者更好的理解,我还是用了这种方式
*/
public void forword(String serverSocketName, String content) {
try {
// 遍历集合
for (ServerThread serverThread : lists) {
// 如果该ServerThread的toString与即将发送的目的地名称一致时
if (serverThread.toString().equals(serverSocketName)) {
// 获取该ServerThread的输出流,向其中写出信息
serverThread.getDataOutputStream().writeUTF(content);
// 同时,刷新该缓冲区
serverThread.getDataOutputStream().flush();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务器读写线程
package me.mxzf;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.HashSet;
/**
*
* @Title: ServerThread
* @Dscription: 服务器读写线程
* @author Deleter
* @date 2017年3月12日 下午2:31:57
* @version 1.0
*/
public class ServerThread extends Thread {
private Socket socket;
private HashSet<ServerThread> list;
private DataInputStream dis;
private DataOutputStream dos;
public ServerThread(Socket socket, HashSet<ServerThread> list) {
this.socket = socket;
this.list = list;
}
@Override
public void run() {
try {
// 用DataInputStream包装socket的输入流
dis = new DataInputStream(socket.getInputStream());
// 用DataOutputStream包装socket的输出流
dos = new DataOutputStream(socket.getOutputStream());
// 定义局部变量
String content;
while (true) {
// 一直阻塞,直到有消息到来,进入循环体
while ((content = dis.readUTF()) != null) {
// 打印接收到的消息
System.out.println(content);
// 服务器反馈消息
dos.writeUTF("Server:ok");
// 刷新缓冲区,防止粘包
dos.flush();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
/*
* 重写toString方法
*
* @see java.lang.Thread#toString()
*/
@Override
public String toString() {
return "ServerThread [socket=" + socket + "]";
}
/*
* 获取输出流
*/
public DataOutputStream getDataOutputStream() {
return dos;
}
}
注意
1、客户端先进行写操作(重要)
2、必须先启动服务端
扩展学习
可以自行扩展,添加自定义的信息格式,或者加入Scanner输入流等等