实现功能
1.多客户端模式下,实现客户与客户的单独通信,信息通过服务器中转
2.端到端的通信,实现并行通信模式
3.实现端到端的文件传输
4.图形用户界面
提示:
1 文件保存地址可能需自行创建或者修改到已有目录
2 还没两台机器跑过,需要修改ip地址和端口
测试图样
客户端
import java.net.*;
import java.io.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Client {
private final int port=4396;
private final String ipAddress="127.0.0.1";//调试时默认主机ip地址
private Socket client=null;
private Poster po;
private Chaser ch;
private UI ui;
private void Sender(){
Thread s = new Thread(po);
s.start();
//检测程序结束运行后向发送关闭指令
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
public void run() {
System.out.println("Closed");
try{
po.sendMsg("bye");
}catch(IOException d){
System.out.println(d);
}
}
}));
}
private void Receiver(){
Thread r = new Thread(ch);
r.start();
}
public Client()throws Exception{
//创建socket
client = new Socket(ipAddress, port);//服务端地址和端口
System.out.println("Connecting server...");
BufferedReader sin = new BufferedReader(new InputStreamReader(System.in));
po = new Poster(client,sin);
ui = new UI(po);
//注册窗口
String name = null;
while(name==null){
name = JOptionPane.showInputDialog(ui,"请输入用户名","输入用户名",1);
}
po.sendMsg(name);
JOptionPane.showMessageDialog(ui,"欢迎来到孤岛, " + name,"注册成功",JOptionPane.PLAIN_MESSAGE);
ui.setTitle("岛民 " + name);
po.sendMsg("alive");//初始化在线名单
ch = new Chaser(client,ui);
}
public static void main(String[] args){
try {
Client c = new Client();
c.Sender();
c.Receiver();
} catch(Exception e) {
System.out.println(e);
}
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
class UI extends JFrame {
private JTextArea j1 = new JTextArea("", 10, 20);//文本显示区域
private JTextArea j2 = new JTextArea("", 5, 9);//输入消息区域
private JComboBox cmb = new JComboBox();;//在线成员下拉列表
private JButton b1 = new JButton("发送");
private JButton b2 = new JButton("文件");
private Poster po;
public void ShowMsg(String msg){
j1.append(msg + "\n");
j1.setCaretPosition(j1.getText().length());
}
public void UpdateAlivers(String name,boolean alive){
if(alive){
cmb.addItem(name);
}else{
cmb.removeItem(name);
}
}
public UI(Poster po) {
this.po = po;
//显示文本域初始化
JScrollPane jsp1 = new JScrollPane(j1);//将文本域放入滚动窗口
j1.setBackground(Color.decode("#E1E1E1"));
j1.setEditable(false);
Dimension size1 = j1.getPreferredSize(); //获得文本域的首选大小
jsp1.setBounds(0, 0, size1.width, size1.height);
//成员列表初始化
cmb.addItem("所有人");
//输出文本域初始化
JScrollPane jsp2 = new JScrollPane(j2);
j2.setBackground(Color.decode("#E1E1E1"));
Dimension size2 = j2.getPreferredSize(); //获得文本域的首选大小
jsp2.setBounds(0, 0, size2.width, size2.height);
//发送按钮事件监听
b1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
String msg = j2.getText();
String targ = cmb.getSelectedItem().toString();
j2.setText("");//清空输出框
j1.append("[" + targ + "] 我: " + msg + "\n");
j1.setCaretPosition(j1.getText().length());
try {
po.sendMsg("to");
po.sendMsg(targ);
po.sendMsg(msg);
}catch(IOException a){
System.out.println(a);
}
}
});
//文件按钮事件监听
b2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
JFileChooser fc = new JFileChooser("F:\\");
int val = fc.showOpenDialog(null); //文件打开对话框
if (val == fc.APPROVE_OPTION) {
try{
File F = fc.getSelectedFile();
String targ = cmb.getSelectedItem().toString();
if(targ.equals("所有人")){
int tot = cmb.getItemCount();
for(int i=1;i<=tot-1;i++){
po.sendMsg("file");
po.sendMsg(cmb.getItemAt(i).toString());
po.sendMsg(F.getName());
po.sendFile(F.toString());
}
}else{
po.sendMsg("file");
po.sendMsg(targ);
po.sendMsg(F.getName());
po.sendFile(F.toString());
}
}catch(IOException e){
System.out.println(e);
}
}
}
});
//盒式布局
Box boxv = Box.createVerticalBox();
boxv.add(jsp1);
boxv.add(cmb);
Box boxh = Box.createHorizontalBox();
Box boxv2 = Box.createVerticalBox();
boxv2.add(b1);
boxv2.add(b2);
boxh.add(jsp2);
boxh.add(boxv2);
boxv.add(boxh);
//设置窗口属性
setContentPane(boxv);
setTitle("聊天室");
setSize(400, 300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setVisible(true);
}
}
import java.io.*;
import java.net.*;
/*
Poster类实现Runnable接口,用于并行向服务器发送数据
*/
public class Poster implements Runnable {
private Socket socket = null;
private DataOutputStream dos = null;
private BufferedReader sin = null;
public void sendMsg(String msg)throws IOException{
dos.writeUTF(msg);
dos.flush();
}
public void sendFile(String path) throws IOException{
int len = 512;
try {
File f = new File(path);
FileInputStream fis = new FileInputStream(f);//读文件对象
byte buffer[] = new byte[512];//读取输入流 ,一次读取512字节
while (len >= 512) {
len = fis.read(buffer, 0, 512);
dos.write(buffer, 0, len);
dos.flush();
}
fis.close();
System.out.println("[文件读取发送成功!]");
}catch (FileNotFoundException fnfe){
System.out.println("[文件未找到]");
}
}
public Poster(Socket socket,BufferedReader sin) throws IOException{
this.socket = socket;
this.sin = sin;
this.dos = new DataOutputStream(socket.getOutputStream());
}
public void run(){
try {
boolean flag = true;
do {
//拓展功能区域
} while(flag);
dos.close();
}catch(Exception e){
System.out.println(e);
}
}
}
import javax.swing.*;
import java.io.*;
import java.net.*;
/*
Chaser类实现Runnable接口,用于并行捕捉服务器传来的数据
*/
public class Chaser implements Runnable{
private Socket socket = null;
private DataInputStream dis = null;
private UI ui = null;
private void getFile() throws Exception{
String fileName = dis.readUTF();
String path = "D:/AhatDownload/" + fileName;//文件名为原文件名,自己指定目录
File file = new File(path);
if(file.exists()){
path += "#";
}
file.createNewFile();
System.out.println(path+"已新建!");
FileOutputStream fos = new FileOutputStream(path);//文件写入对象
int n=512,len=512;
byte[] data = new byte[n];//一次读取字节
while(len>=n)//判断是否有可以读取的数据
{
len=dis.read(data,0,n);
//一定要用DataInputStream,他有方法可以判断缓冲区是否有内容,这样就不会在没有内容时用read()阻塞了
fos.write(data,0,len);
fos.flush();
}
System.out.println("[文件下载成功!]");
JOptionPane.showMessageDialog(ui,"成功接收文件: " + fileName,"文件已接收",JOptionPane.PLAIN_MESSAGE);//弹窗
fos.close();
}
public Chaser(Socket socket,UI ui) throws IOException{
this.socket = socket;
this.ui = ui;
this.dis = new DataInputStream(socket.getInputStream());
}
public void run(){
try{
boolean flag = true;
do{
String op = dis.readUTF();//先读取操作
if(op.equals("msg")){
String msg = dis.readUTF();
ui.ShowMsg(msg);
}else if(op.equals("file")){
getFile();
}else if(op.equals("bye")){
flag = false;
}else if(op.equals("online")){
ui.UpdateAlivers(dis.readUTF(),true);
}else if(op.equals("offline")){
ui.UpdateAlivers(dis.readUTF(),false);
}else{
System.out.println("[来自服务器的未知指令]"+op);
}
}while(flag);
dis.close();
}catch(Exception e){
System.out.println(e);
}
}
}
服务器
import java.net.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public class Server {
private int port = 4396;
private ServerSocket server;
private Map<String, OutputStream> map = new ConcurrentHashMap<String,OutputStream>();
public Server() throws Exception{
server=new ServerSocket(port);
System.out.println("server on");
}
public void loading()throws Exception{
while(true){
Socket socket = server.accept();
//注册名字
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
DataInputStream dis = new DataInputStream(socket.getInputStream());
String name = dis.readUTF();
//广播其他用户该用户已上线
Set list = map.entrySet();
for (Object object : list) {
Map.Entry<String, OutputStream> entry = (Map.Entry<String, OutputStream>) object;
DataOutputStream ttdos = new DataOutputStream(entry.getValue());
ttdos.writeUTF("online");
ttdos.flush();
ttdos.writeUTF(name);
ttdos.flush();
}
//为用户开启服务线程
map.put(name,socket.getOutputStream());
new Thread(new Waiter(socket,map,name)).start();
System.out.println( "ip " + socket.getInetAddress() + " name: " + name + " is connected");
}
}
public static void main(String[] args){
try {
Server ChatRoomServer = new Server();
ChatRoomServer.loading();
} catch(Exception e){
System.out.println(e);
}
}
}
import java.io.*;
import java.net.*;
import java.util.*;
//服务项目
class Waiter implements Runnable{
private Socket socket;
private String name;
private Map<String, OutputStream> map;
private DataInputStream tdis;
private DataOutputStream tdos;
private void sendMsgAll(String msg)throws IOException {
Set list = map.entrySet();
for (Object object : list) {
Map.Entry<String, OutputStream> entry = (Map.Entry<String, OutputStream>) object;
if(!entry.getKey().equals(name)){
DataOutputStream ttdos = new DataOutputStream(entry.getValue());
ttdos.writeUTF("msg");
ttdos.flush();
ttdos.writeUTF("[公共] " + name + " : " + msg);
ttdos.flush();
}
}
System.out.println(name+":"+msg);//服务器监听
}
private void sendMsgTo(String targ, String msg) throws IOException{
DataOutputStream ttdos = new DataOutputStream(map.get(targ));
ttdos.writeUTF("msg");
ttdos.flush();
ttdos.writeUTF("[私聊] " + this.name + " : " + msg);
ttdos.flush();
}
private void sendFileTo(String targ, String filename) throws IOException{
DataOutputStream ttdos = new DataOutputStream(map.get(targ));
ttdos.writeUTF("file");
ttdos.flush();
ttdos.writeUTF(filename);
ttdos.flush();
byte data[] = new byte[512];
int temp = 512,len = 0;
while(temp>=512) {
temp=tdis.read(data,0,512);
len+=temp;
ttdos.write(data,0,temp);
ttdos.flush();
}
System.out.println("file length: "+len);
}
public void initAliver()throws IOException{
//将在线名单推送给客户端
Set list = map.entrySet();
String aliver="";
for (Object object : list) {
Map.Entry<String, OutputStream> entry = (Map.Entry<String, OutputStream>) object;
aliver=entry.getKey();
if(!aliver.equals(name)){
tdos.writeUTF("online");
tdos.flush();
tdos.writeUTF(aliver);
tdos.flush();
}
}
}
public Waiter(Socket s,Map<String,OutputStream> m,String n)throws IOException{
socket = s;
map=m;
name=n;
tdis = new DataInputStream(socket.getInputStream());
tdos = new DataOutputStream(socket.getOutputStream());
}
public void run() {
try {
boolean flag = true;
do{
//从客户端接收指令
String op = tdis.readUTF();
System.out.println(name + " op: " + op);
if(op.equals("to")){
String tg = tdis.readUTF();
if(tg.equals("所有人")){
sendMsgAll(tdis.readUTF());
}else if(tg.equals(this.name) || !map.containsKey(tg)){
System.out.println("[错误] " + name + " 访问的用户(" + tg + ")不存在");
}else{
sendMsgTo(tg,tdis.readUTF());
}
}else if(op.equals("file")){
String tg = tdis.readUTF();
String filename = tdis.readUTF();
System.out.println(filename);
if(tg.equals(this.name) || !map.containsKey(tg)){
System.out.println("[错误] " + name + " 访问的用户(" + tg + ")不存在");
}else{
sendFileTo(tg,filename);
}
}else if(op.equals("bye")){
flag = false;
}else if(op.equals("alive")){
initAliver();
}else{
System.out.println("[来自客户端"+this.name+"的未知指令]"+op);
}
}while(flag);
//用户离线后续操作;
Set list = map.entrySet();
for (Object object : list) {
Map.Entry<String, OutputStream> entry = (Map.Entry<String, OutputStream>) object;
DataOutputStream ttdos = new DataOutputStream(entry.getValue());
ttdos.writeUTF("offline");
ttdos.flush();
ttdos.writeUTF(name);
ttdos.flush();
}
System.out.println("ip " + socket.getInetAddress() + " name: " + name + " is disconnected");
map.remove(name);
} catch (IOException e) {
System.out.println(e);
}
}
}