整体思路:1.服务端:多线程接受客户端。由clienthandler实现Runnable接口,并采用有参构造,参数为Socket。clienthandler的run方法阻塞读取客户端的消息,并判断消息类型,做出判断。若为chat,则服务端返回客户端的消息,若为file则新建线程传入文件名和文件长度接受文件。2.客户端:客户端监听控制台输入,输入格式为mess:+信息或者file:源文件绝对路径。内部类liunnablestener继承Runnable监听服务端返回消息。
第三方包:XStream包。(如果你的jdk大于1.8则最好使用版本高点的包,低版本不适用lmbda表达式)
1.类代码及作用:格式转化
public class ProtocalObj {
/**
* 生成xml
*
* @return
*/
public String toXML() {
XStream x = new XStream();
// 设置别名,默认是类的全路径名
x.alias(getClass().getSimpleName(), getClass());
String xml = x.toXML(this);// 将本类转换成xml返回
return xml;
}
/**
* xml-->实体类
*
* @param xml
* @return
*/
public Object fromXML(String xml) {
XStream x = new XStream();
x.alias(getClass().getSimpleName(), getClass());
return x.fromXML(xml);
}
}
2.类代码及作用:继承格式转化类,实现Serializable接口,客户端服务端数据交互类
public class Message extends ProtocalObj implements Serializable {
private static final long serialVersionUID = 1L;
private String content;
private int type;
private long fileLength;
private String fileName;
public Message(int type, long fileLength, String fileName) {
this.type = type;
this.fileLength = fileLength;
this.fileName = fileName;
}
public Message(String content, int type) {
this.content = content;
this.type = type;
}
public Message() {
}
public Message(int type) {
this.type = type;
}
public String getContent() {
return this.content;
}
public void setContent(String content) {
this.content = content;
}
@Override
public String toXML() {
return super.toXML();
}
@Override
public Object fromXML(String xml) {
return super.fromXML(xml);
}
public int getType() {
return this.type;
}
public void setType(int type) {
this.type = type;
}
public long getFileLength() {
return fileLength;
}
public void setFileLength(long fileLength) {
this.fileLength = fileLength;
}
public String getFileName() {
return fileName;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
}
3.类代码及作用:
//信息类型类,用来表示信息类型的类
public class MessageType {
public static final int MSG_TYPE_CHAT= 604;
public static final int MES_TYPE_FILE = 800;
public MessageType() {
}
}
4.类代码及作用:服务端启动类
public class Server {
// 程序入口类
// public static ClientManager manager = new ClientManager();
//服务器程序入口方法
public static void main(String[] args) {
try {
ServerSocket server = new ServerSocket(9999);
System.out.println("服务器启动");
//用死循环不断接受客户端
while(true) {
Socket client = server.accept();
(new Thread(new ClientHandler(client))).start();
}
} catch (IOException var3) {
var3.printStackTrace();
}
}
}
5.类代码及作用:服务端处理类
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
//服务端处理线程
public class ClientHandler implements Runnable {
private Socket client;
private DataOutputStream out;
private DataInputStream in;
//构造方法接受一个socket
public ClientHandler(Socket client) {
this.client = client;
try {
this.in = new DataInputStream(client.getInputStream());
this.out = new DataOutputStream(client.getOutputStream());
} catch (IOException var3) {
var3.printStackTrace();
}
}
//处理用户传来的消息,判断消息类型执行相应操作
public void run() {
try {
String xml = null;
Message message = null;
// 阻塞读取客户端信息
while (true) {
while ((xml = this.in.readUTF()) != null) {
message = new Message();
message = (Message) message.fromXML(xml);
System.out.println(message.toXML());
switch (message.getType()){
// 聊天
case MessageType.MSG_TYPE_CHAT:
reply(message);
break;
// 文件传输
case MessageType.MES_TYPE_FILE:
new Thread(new FileReceive(message.getFileName(),message.getFileLength())).start();
}
}
}
} catch (IOException var6) {
var6.printStackTrace();
}
}
//返回给对应客户端
public void reply(Message message) {
try {
System.out.println("客户端说:"+message.getContent());
this.out.writeUTF(message.toXML());
System.out.println("服务端说:"+message.getContent());
this.out.flush();
} catch (IOException var3) {
var3.printStackTrace();
}
}
// 文件接收线程,新建ServerSocket,监听指定接口10000;接受客户端信息
class FileReceive implements Runnable{
private ServerSocket serverSocket;
private Socket socket;
private DataInputStream in;
private String fileName;
private long fileLength;
public FileReceive( String fileName, long fileLength) {
try {
serverSocket=new ServerSocket(10000);
socket=serverSocket.accept();
in=new DataInputStream(socket.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
this.fileName = fileName;
this.fileLength = fileLength;
}
@Override
public void run() {
try {
FileOutputStream out=new FileOutputStream(new File("D:\\"+fileName));
byte[] bytes=new byte[1024*1024];
while (in.read(bytes)!=-1){
out.write(bytes);
out.flush();
System.out.println("文件传输完毕!");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
6.类代码及作用
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
public class client {
private static String content;
private static Socket socket;
private static DataOutputStream out;
private static DataInputStream in;
public static void main(String[] args) throws IOException {
socket = new Socket("localhost", 9999);
Scanner scanner = new Scanner(System.in);
// 键盘监听控制台输入内容
while (keyBoardListener(scanner)) {
new Thread(new Listener(socket)).start();
}
}
// 键盘监听
private static boolean keyBoardListener(Scanner scanner) {
if (scanner.hasNext()) {
content = scanner.next();
if (content.equals("exit")) {
System.out.print("程序退出");
return false;
} else {
// 判断输入内容
if (content.startsWith("mess:")) {
String s=content.substring(5);
Message message = new Message(s, MessageType.MSG_TYPE_CHAT);
// 向服务端发送信息
System.out.println("信息---->");
send(message);
} else if (content.startsWith("file:")) {
// 截取第五位开始的字符
File file=new File(content.substring(5));
Message message = new Message( MessageType.MES_TYPE_FILE,file.length(),file.getName());
send(message);
send(file);
} else {
System.out.println("请输入正确格式!mess: or file: 内容或者文件地址!");
}
System.out.println("您输入:" + content);
return true;
}
}
return true;
}
// 发送信息
private static void send(Message message) {
try {
out = new DataOutputStream(socket.getOutputStream());
out.writeUTF(message.toXML());
out.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
// 多线程发送文件
private static void send(File file) {
new Thread(new Runnable() {
@Override
public void run() {
byte[] bytes = new byte[1024];
try {
Socket socket=new Socket("localhost",10000);
FileInputStream inputStream = new FileInputStream(file);
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
while (inputStream.read(bytes) != -1) {
out.write(bytes);
}
inputStream.close();
out.flush();
// out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
//多线程,监听服务端返回消息
static class Listener implements Runnable {
private Socket socket;
private DataInputStream in;
public Listener(Socket socket) {
this.socket = socket;
try {
in = new DataInputStream(socket.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
String xml = null;
Message message = new Message();
while (true) {
try {
while ((xml=in.readUTF())!=null){
message=(Message)message.fromXML(xml);
switch (message.getType()){
case MessageType.MSG_TYPE_CHAT:
System.out.println("服务端说:"+message.getContent());
break;
case MessageType.MES_TYPE_FILE:
System.out.println("发送成功!");
break;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}