功能
- 多客户端模式下,实现客户与客户的并行通信,要求信息通过服务器中转
- 端到端文件传输
系统设计
设计思想
将服务器端作为客户端与客户端之间通信的桥梁,每个客户需要向服务器端发送信息,说明自己想要通信的对象,再由服务器端进行转发。
整体设计
服务器端MultiTalkServer下有线程ServerThread,客户端TalkClient下有线程ClientThread。MultiTalkServer负责监听并对新的客户产生新线程ServerThread来转发消息,TalkClient负责发送消息并产生新线程ClientThread来接受消息。
局部设计
- MultiTalkServer:在指定端口循环监听,当有客户端接入时,生成一个新线程来处理,并统计累计接入的客户端数。
- ServerThread:含有静态成员哈希表table,用来存储IP和对应的socket套接字,构造函数中会将该线程对应的IP和socket存入table中。sendDocument函数专门用来处理文件传输。对于收到的消息,先判断IP是否正确,然后判断对方是否在线,之后判断传输是否为文件,若传输消息,则从table中获取对方的socket并发送,继续接受消息。
- TalkClient:splitMessage负责接受并拆分输入,确保消息中包含IP和信息。sendDocument负责传递文件。当发送的消息是“quit”时,表示结束当前通信。
- ClientThread:receiveDocument负责接收文件。当收到“quit”命令时结束线程。
系统实现
- 客户与服务器之间的通信通过socket套接字实现
socket = serverSocket.accept();
- 并行通信通过多线程来实现,一个线程负责发送,一个线程负责接收
- 消息传输主要通过BufferedReader、PrintWriter等实现
BufferedReader sin=new BufferedReader(new InputStreamReader(System.in));
PrintWriter os=new PrintWriter(socket.getOutputStream());
- 文件传输通过File和DataOutputStream、DataInputStream等实现
DataInputStream dis = new DataInputStream(new BufferedInputStream(new FileInputStream(fileRead)));
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
- 对IP的判断、拆分信息等通过正则表达式实现
String re = "[0-9]{1,3}+\\.+[0-9]{1,3}+\\.+[0-9]{1,3}+\\.+[0-9]{1,3}", IP;
boolean match = false;
Pattern pattern = Pattern.compile(re);
lineArr = is.readLine().split(" ");
Matcher matcher = pattern.matcher(lineArr[0]);
match = matcher.matches();
- 接受到文件后,先判断是否存在以对方IP命名的文件夹,若不存在则创建;然后判断是否存在重名文件,若存在则在文件名后增加序号。
if(filename.indexOf(".") >= 0) {
lineArr[0] = filename.substring(0, filename.lastIndexOf("."));
lineArr[1] = filename.substring(1 + filename.lastIndexOf("."));
for(int i = 1; outputFile.exists(); i++) {
name = lineArr[0] + "(" + i + ")." + lineArr[1];
outputFile = new File(name);
}
}else {
for(int i = 1; outputFile.exists(); i++) {
line = name + "(" + i + ")";
outputFile = new File(line);
}
if(line != null) {
name = line;
}
}
Java代码
服务器端 MultiTalkServer
import java.io.*;
import java.net.*;
public class MultiTalkServer{
static int clientnum=0; //静态成员变量,记录当前客户的个数
public static void main(String args[]) throws IOException {
ServerSocket serverSocket=null;
Socket socket = null;
boolean listening=true;
try{
serverSocket=new ServerSocket(6666);
}catch(IOException e) {
System.out.println("Could not listen on port:6666.");
//出错,打印出错信息
System.exit(-1); //退出
}
while(listening){
//监听到客户请求,根据得到的Socket对象和客户计数创建服务线程,并启动之
socket = serverSocket.accept();
System.out.println(socket.getInetAddress() + " is connected!");
new ServerThread(socket ,clientnum).start();
clientnum++; //增加客户计数
}
serverSocket.close(); //关闭ServerSocket
}
}
服务器端线程 ServerThread
import java.io.*;
import java.net.*;
import java.util.Hashtable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ServerThread extends Thread{
Socket socket = null, targetS = null; //保存与本线程相关的Socket对象
int clientnum; //保存本进程的客户计数
volatile static Hashtable<String, Socket> table =