实现功能(最终版):
1、基于上一版本,实现了swing图形界面,其中写的逻辑改变较大,要用到监听器监听发送按钮,而不是以前判断是否有键盘输入,另有背景颜色,字体及颜色,滚动条等设置。
2、该系统有强大的输入检查功能。
注:大家参考就好了,不要照搬啊!!!
结果展示
代码展示:
客户端:
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.Socket;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class TalkClient {
public static void main(String args[]) {
JFrame jf = new JFrame();//用JFrame创建一个名为frame的顶级容器
jf.setContentPane(new JPanel());//在jf里面添加一个JPanel对象,用来增加滚动条
jf.setTitle("JAVA聊天器");
jf.setResizable(true);//窗口可由用户调整大小
jf.setSize(540, 600);//大小
jf.setLocation(650, 100);//位置
jf.setLayout(null);//布局方式
JButton btnSend=new JButton("发送");//创建一个按钮
btnSend.setForeground(Color.red); //盖住背景色
final JTextArea xie=new JTextArea();//创建文本区域,写
final JTextArea du=new JTextArea();//读
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//右键关闭窗口时操作
jf.getContentPane().setBackground(Color.yellow);
jf.setVisible(true);//使图形可见
btnSend.setBounds(410,320,100,50);//位置,大小的设置
Font font = new Font("等线",Font.PLAIN, 20);//字体,20的字号
btnSend.setFont(font);
jf.add(btnSend);//添加到顶级容器
xie.setBounds(10,380,500,150);
xie.setFont(font);
jf.add(xie);
du.setEditable(false);//该控件不能被编辑,即显示区无法选择
//taChat.setBounds(10,10,500,300);
du.setFont(font);
//jf.add(taChat);
JScrollPane js = new JScrollPane(du);//把du封装到JScrollPane里面
js.setBounds(10,10,500,300);
jf.getContentPane().add(js);//添加到jf里,即添加滚动条
try{
//向本机的4700端口发出客户请求
@SuppressWarnings("resource")
final Socket socket=new Socket("127.0.0.1",4700);
DataInputStream is=new DataInputStream(socket.getInputStream());//消息,文件输入流
du.append("发消息:1 hello 发文件: 1 hello.txt 退出:1 bye"+"\n");
System.out.println("发消息:1 hello 发文件: 1 hello.txt 退出:1 bye"+"\n");
btnSend.addActionListener(//监听“发送”是否被点击
new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
String readline=xie.getText();//从输入框读取
xie.setText("");//清空输入区域
try {
char pd1=readline.charAt(0);
char pd2=readline.charAt(1);
if(pd1<='9'&&pd1>='0'&&pd2==' ')//判断输入格式是否正确
{
DataOutputStream os=new DataOutputStream(socket.getOutputStream());//消息,文件输出流
String[] talk=readline.split(" ");
String[] wj=talk[1].split("\\.");//分割字符串,判断是否要发送文件
boolean cz=false;//标志该文件是不是存在
if(wj.length==2)//发文件的话,长度等于2
{
File directory = new File("");// 参数为空
String courseFile = directory.getCanonicalPath();//获得项目所在路径,也就是发文件读取文件的路径
File file = new File(courseFile);
File filelist[] = file.listFiles();
for(File f : filelist){
String filename = f.getName();
if(filename.equals(talk[1])){
cz=true;//要是存在就真
}
}
}//例如hello.abc这是不存在的,就把他当作消息发过去,所以要判断该文件是否存在
if(cz)//如果要发送文件
{
os.writeUTF("9q8w7e6a5s4d");//奇怪的字符串,用来标志是不是发文件
os.flush();
os.writeUTF(readline);
os.flush();//刷新输出流,使Server马上收到该字符串
File fi = new File(talk[1]);
FileInputStream fis = new FileInputStream(fi);//读文件对象,该文件就在根目录下
int m,len;
long l=fi.length(); //获取文件长度,单位kb
if((l%512)!=0) len = m = 512;
else len = m = 511;//防止文件正好是512字节的倍数
byte buffer[] = new byte[m];//读取输入流 ,一次读取512字节
//while ((len=fis.read(buffer,0,m))!=-1) {
while (len>=m) {//这while不能用上面注释的那个,read会阻塞
len=fis.read(buffer,0,m);
os.write(buffer,0,len);
os.flush();
}
fis.close();
du.append("文件读取发送成功!"+"\n");
System.out.println("文件读取发送成功!");
}
else
{
os.writeUTF(readline);//正常发消息
os.flush();//刷新输出流,使Server马上收到该字符串
if(talk[1].equals("bye"))//若从标准输入读入的字符串为 "bye"则停止循环
{
System.exit(0);//退出系统
}
}
}
else if(readline.equals("bye"))
{
System.exit(0);//退出系统
}
else du.append("输入格式有误!"+"\n");
}catch(Exception e) {
}
}
}
);
while(true) {//无限次收消息
if(is.available()>0)//如果先收消息,也是先available()查看输入流的长度(未读取的情况下)
{
String get=is.readUTF();
if(get.equals("9q8w7e6a5s4d"))//判断是否要收文件
{
get=is.readUTF();//读取文件名等
du.append(get+"\n");
System.out.println(get);
String[] wj1=get.split(" ");
String name = "E:\\"+wj1[2];//文件名为原文件名,自己指定目录
File file = new File(name);
if(!file.exists()){
file.createNewFile();
du.append("文件已新建!"+"\n");
System.out.println("文件已新建!");
}
FileOutputStream fos = new FileOutputStream(name);//文件写入对象
int n=512,len=512;
byte[] data = new byte[n];//一次读取字节,一般不会太大
while(len>=n)//判断是否有可以读取的数据
{
len=is.read(data,0,n);
//一定要用DataInputStream,他有方法可以判断缓冲区是否有内容,这样就不会在没有内容时用read()阻塞了
fos.write(data,0,len);
fos.flush();
}
du.append("文件已上传成功:"+name+"\n");
System.out.println("文件已上传成功:"+name+"\n");
fos.close();
}
else
{
du.append(get+"\n");
System.out.println(get);
}
}
} //继续循环
}catch(Exception e) {
System.out.println("Error"+e); //出错,则打印出错信息
}
du.append("您已退出系统!"+"\n");
System.out.println("您已退出系统!");
}
}
线程:
import java.io.*;
import java.net.*;
import javax.swing.JTextArea;
public class ServerThread extends Thread{
Socket lssocket=null;
Socket socket=null; //保存与本线程相关的Socket对象
int p;//用来记录这是第几个客户端
static int clientnum; //保存本进程的客户计数
JTextArea jta;
public ServerThread(Socket socket,int num,JTextArea jta) { //构造函数
this.socket=socket; //初始化socket变量
clientnum=num+1; //初始化clientnum变量
p=num;//这里是从0开始编号客户端
this.jta=jta;
}
public void run() { //线程主体
try{
String line;
//消息,文件输入流
DataInputStream is=new DataInputStream(socket.getInputStream());
DataOutputStream os=new DataOutputStream(socket.getOutputStream());//消息,文件输出流
os.writeUTF("你的客户编号为: "+p+" \n");
os.flush();
while(true){
if(is.available()>0)
{
line=is.readUTF();//读取字符串,格式example: 1 aaa
if(line.equals("9q8w7e6a5s4d"))//判断是否要转发文件
{
line=is.readUTF();
String[] talk=line.split(" ");//以空格为分隔符分割字符串
int number = Integer.parseInt(talk[0]);//这是要收消息的客户端的编号
lssocket=MultiTalkServer.socket[number];//方便修改os
os=new DataOutputStream(lssocket.getOutputStream());//消息,文件输出流
os.writeUTF("9q8w7e6a5s4d");//向客户端输出该字符串,发文件的标志
os.flush();//刷新输出流,使Client马上收到该字符串
line="Client "+p+": "+talk[1];//把消息连接上发消息的编号,方便阅读
os.writeUTF(line);//向客户端输出该字符串
os.flush();//刷新输出流,使Client马上收到该字符串
jta.append(p+" is send file to "+number+": "+talk[1]+"\n");
System.out.println(p+" is send file to "+number+": "+talk[1]);
int n=512,len=512;
byte[] data = new byte[n];//一次读取字节,一般不会太大
while(len>=n)
{
len=is.read(data,0,n);
os.write(data,0,len);
os.flush();
}
jta.append("文件转发成功!"+"\n");
System.out.println("文件转发成功!");
}
else
{
String[] talk=line.split(" ");//以空格为分隔符分割字符串
int number = Integer.parseInt(talk[0]);//这是要收消息的客户端的编号
lssocket=MultiTalkServer.socket[number];//方便修改os
os=new DataOutputStream(lssocket.getOutputStream());//消息,文件输出流
line="Client "+p+": "+talk[1];//把消息连接上发消息的编号,方便阅读
os.writeUTF(line);//向客户端输出该字符串
os.flush();//刷新输出流,使Client马上收到该字符串
jta.append(p+" is talk to "+number+": "+talk[1]+"\n");
System.out.println(p+" is talk to "+number+": "+talk[1]);
if (talk[1].equals("bye"))
{
jta.append("客户 "+p+" 已退出系统!"+"\n");
System.out.println("客户 "+p+" 已退出系统!");
break;
}
}
}
}
is.close(); //关闭Socket输入流
socket.close(); //关闭Socket
}catch(Exception e){
System.out.println("Error:"+e);//出错,打印出错信息
}
}
}
服务器:
import java.awt.Color;
import java.awt.Font;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JTextArea;
public class MultiTalkServer{
public static Socket[] socket=new Socket[10];//建立Socket数组,这里简单编写,用静态数组
static int clientnum=-1; //静态成员变量,记录当前客户的个数
public static int getnum() {return clientnum;}
public static void main(String args[]) throws IOException {
JFrame jf = new JFrame();//用JFrame创建一个名为frame的顶级容器
jf.setTitle("服务器");//设置名称
jf.setSize(540, 600);//大小
jf.setLocation(50, 100);//在屏幕的位置
jf.setLayout(null); //布局方式,这里是默认
jf.getContentPane().setBackground(Color.yellow);//设置背景颜色
final JTextArea jta = new JTextArea();//添加文本区域
jta.setBounds(10,10, 500, 525);//位置和大小
jta.setFont(new Font("等线",Font.PLAIN, 20));//设置字体,风格,大小
jf.add(jta);//把文本区域添加到顶级容器里
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//右键关闭窗口时操作,即正常退出
jf.setVisible(true);//使图形可见
ServerSocket serverSocket=null;
boolean listening=true;
try{
//创建一个ServerSocket在端口4700监听客户请求
serverSocket=new ServerSocket(4700);
}catch(IOException e) {
System.out.println("Could not listen on port:4700.");
//出错,打印出错信息
System.exit(-1); //退出
}
jta.append("开始连接客户端......"+"\n");
System.out.println("开始连接客户端......");
while(listening){ //循环监听
clientnum++; //增加客户计数
//监听到客户请求,根据得到的Socket对象和客户计数创建服务线程,并启动之
socket[clientnum]=serverSocket.accept();//保存监听到的客户端,这步很重要
jta.append("客户 "+clientnum+" 已上线!"+"\n");
new ServerThread(socket[clientnum],clientnum,jta).start();
//clientnum++; //增加客户计数
}
serverSocket.close(); //关闭ServerSocket
}
}