package cgh20130727分部实现多人通信;
import java.io.IOException;
import javax.swing.JTextArea;
/**
* 定义一个TestServer的类创建一个服务器
* @author Allen
*
*/
public class TestServer extends Thread {
private int port;
private JTextArea jta;
//构造方法进行参数的传递
public TestServer(int port,JTextArea jta){
this.port = port;
this.jta = jta;
}
//空参数构造方法
public TestServer() {
}
//重写run方法
public void run(){
server(port);
}
//定义一个创建一个服务器的方法
public void server(int port){
try {
//创建服务器
java.net.ServerSocket server = new java.net.ServerSocket(port);
System.out.println("你的服务器创建成功了!!!\r\n"+"端口号: "+port);
while(true){
//阻塞等待客户端的连接
java.net.Socket soclicent = server.accept();
//创建一个多线程对象
ServerThread sth = new ServerThread(soclicent,jta);
sth.start();//线程开始运行
chatList.add(sth);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
创建一个服务器代码就那么三行,比较固定看看书也就搞定了.但在程序运行的时候遇到了一个bug,就是在已有的界面上(服务器界面)显示的时候点击"端口"按钮创建服务器对象的时候一直处于运行状态,最后知道了原来是没有添加线程的原因.
package cgh20130727分部实现多人通信;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import javax.swing.JTextArea;
import cgh20130728多人聊天客户端.customListe;
/**
* 定义一个客户端对象继承自多线程
* @author Allen
*
*/
public class ServerThread extends Thread {
private java.net.Socket soclicent;//定义一个客户端的属性
private java.io.InputStream ins;
private java.io.OutputStream ous;
private String ip;
private int port;
private JTextArea jta;
//构造方法进行参数的传递
public ServerThread(java.net.Socket soclicent,JTextArea jta){
this.soclicent = soclicent;
this.jta = jta;
}
public ServerThread(String ip,int port,JTextArea jta){
this.ip = ip;
this.port = port;
this.jta = jta;
}
//重写线程run的方法
public void run(){
work(this.soclicent);
}
//定义从客户端连接到服务器的方法
public void work(java.net.Socket soclicent){
try {
//客户端接收输入输出流
ins = soclicent.getInputStream();
ous = soclicent.getOutputStream();
//验证是否连接成功
String str = "hello,welcome to Lanjie!!!"+"\r\n";
byte [] bytes = str.getBytes();
ous.write(bytes);
ous.flush();
//将字符串读到服务器
String strRead = readString(ins);
//判断当读到exit时停止
while(!strRead.equals("exit")){
System.out.println("服务器接收到的内容是++++\r\n"+strRead);
String sing = "客户端保存的内容是----\r\n"+strRead;
byte [] by = sing.getBytes();
// ous.write(by);
// ous.flush();//强制刷新
chatList.sendMsg(by);//客户端接收到的消息
jta.setText(strRead);//将客户端接收到的消息显示在文本框中
strRead = readString(ins);//下一次得到的字符串
}
}catch (IOException e) {
e.printStackTrace();
}
}
//定义一个读入字符串的方法
public String readString(java.io.InputStream ins){
//创建一个字符串缓冲器对象
StringBuffer strb = new StringBuffer();
int reads = 0;
//判断当读到回车是停止字符的存入
while(reads != 35){
try {
reads = ins.read();//将字符读入到服务器
strb.append((char)reads);//将读到的字符放入字符串缓冲区
} catch (IOException e) {
e.printStackTrace();
}
}
String sting = strb.toString().trim();//将缓冲区的字符整合成字符串
return sting;
}
//定义一个服务器发送消息的方法
public void writeMsg(byte [] by){
try {
System.out.println("writeMsg执行了");
ous.write(by);
ous.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
写该类的时候遇到的一个是:当在程序运行的时候,不管是服务器的界面文本框还是客户端的文本框一直没有显示,纠结了半天最后终于找到原来是没有写该条指令jta.setText(strRead);
package cgh20130727分部实现多人通信;
import java.util.ArrayList;
import java.util.List;
/**
* 定义一个chatList的队列类用来存储客户端对象
* @author Allen
*
*/
public class chatList {
//定义一个私有的构造方法
private chatList(){
}
//调用系统提供的数组队列
public static List<ServerThread> list = new ArrayList<ServerThread>();
//定义一个数组队列的添加方法
public static void add(ServerThread sth){
list.add(sth);
}
//定义一个发送消息的方法
public static void sendMsg(byte [] by){
System.out.println("sendMsg执行了");
//循环遍历数组队列
for(int i=0;i<list.size();i++){
ServerThread st = list.get(i);
st.writeMsg(by);
}
}
}
该类主要是实现将ServerThread保存数组队列中,然后调用sendMsg(by);方法实现客户端与客户端之间,客户端与服务器之间互相发送消息.
package cgh20130727分部实现多人通信;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
/**
* 创建一个服务器界面类
* @author Allen
*
*/
public class ServerUI extends JFrame{
private JTextArea jta;
private TestServer tse;
private int nport;
/**
* 定义一个程序的入口主函数
* @param args
*/
public static void main(String[] args) {
//创建一个对象并调用窗体初始化的方法
ServerUI sui = new ServerUI();
sui.INtUI();
}
//定义一个窗体初始化的方法
public void INtUI() {
this.setTitle("服务器");
this.setSize(400,300);
this.setLocationRelativeTo(null);
this.setResizable(false);
//设置窗体的流式布局
this.setLayout(new FlowLayout());
//创建按钮对象
JButton jbu = new JButton("端口");
JButton jbu1 = new JButton("发送");
//创建文本框对象
final JTextField jte = new JTextField(25);
final JTextField jte1 = new JTextField(25);
//创建一个纯文本多行区域
jta = new JTextArea();
jta.setPreferredSize(new Dimension(350,180));
//添加到窗体
this.add(jbu);
this.add(jte);
this.add(jbu1);
this.add(jte1);
this.add(jta);
//创建监听器
ActionListener action = new ActionListener(){
//调用事件监听器的方法
public void actionPerformed(ActionEvent e) {
if(e.getActionCommand().equals("端口")){
nport = Integer.parseInt(jte.getText());
tse = new TestServer(nport,jta);//创建客户端对象
tse.start();//调用连接服务器的方法
System.out.println("------>"+nport);
}
if(e.getActionCommand().equals("发送")){
//从文本框得到消息
String info = jte1.getText();
//服务器将消息发送给每个客户端
chatList.sendMsg(info.getBytes());
//服务器的文本区域得到文本
jta.append("fuwuqi : \r\n"+info);
//发送完毕后清空文本框
jte1.setText("");
System.out.println("服务器发送消息给客户端成功"+info);
}
}
};
jbu.addActionListener(action);
jbu1.addActionListener(action);//按钮添加监听器
//设置窗体可见性
this.setVisible(true);
}
}
该类主要是写一个服务器的界面并实现创建服务器,群发消息的方法
package cgh20130727分部实现多人通信1;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import cgh20130727分部实现多人通信.ServerThread;
import cgh20130727分部实现多人通信.chatList;
/**
* 创建一个客户端的界面继承自JFrame
* @author Allen
*
*/
public class KehuduanUI extends JFrame {
private JTextArea jta;
private KehuWay khw;
/**
* 程序的入口主函数
* @param args
*/
public static void main(String[] args) {
//实例化一个对象并调用窗体初始化的方法
KehuduanUI khd= new KehuduanUI();
khd.INtUI();
}
//初始化一个界面的方法
public void INtUI() {
this.setTitle("客户端");
this.setSize(400,300);
this.setLocationRelativeTo(null);
this.setResizable(false);
//设置窗体的流式布局
this.setLayout(new FlowLayout());
//创建按钮对象
JButton jbu = new JButton("连接");
JButton jbu1 = new JButton("群聊");
//创建文本框对象
JTextField jte = new JTextField(25);
final JTextField jte1 = new JTextField(25);
//创建一个纯文本多行区域
jta = new JTextArea();
jta.setPreferredSize(new Dimension(350,180));
//添加到窗体
this.add(jbu);
this.add(jte);
this.add(jbu1);
this.add(jte1);
this.add(jta);
//创建监听器
ActionListener action = new ActionListener(){
//调用监听器的方法
public void actionPerformed(ActionEvent e) {
if(e.getActionCommand().equals("连接")){
khw = new KehuWay("localhost",9007,jta);//创建一个客户端对象
//判断客户端是否创建成功
if(khw.connectServer()){
khw.start();
}
}
if(e.getActionCommand().equals("群聊")){
String stri = jte1.getText();
jta.append("kehuduan : \r\n"+stri+"\r\n");
khw.sendInfo(stri);
jte1.setText("");//发完后清空文本框
}
}
};
//添加监听器
jbu.addActionListener(action);
jbu1.addActionListener(action);
//显示窗体的可见性
this.setVisible(true);
}
}
有了客户端界面就需要有一个服务器的界面.
package cgh20130727分部实现多人通信1;
import java.io.BufferedReader;
import java.io.IOException;
import javax.swing.JTextArea;
import cgh20130727分部实现多人通信.chatList;
/**
* 定义客户端方法的类继承自多线程
* @author Allen
*
*/
public class KehuWay extends Thread {
private String ip;
private int port;
private java.io.OutputStream ous;
private java.io.InputStream ins;
private BufferedReader bufr;
private JTextArea jta;
private String sting;
private String string = null;
//构造方法进行参数的传递
public KehuWay(String ip,int port,JTextArea jta){
this.ip = ip;
this.port = port;
this.jta = jta;
}
//判断客户端是否与服务器连接成功
public boolean connectServer(){
try {
//创建连接服务器的对象
java.net.Socket client = new java.net.Socket(ip,port);
System.out.println("客户端与服务器连接成功了!!!");
//获取输入输出流
ins = client.getInputStream();
ous = client.getOutputStream();
System.out.println("----++++");
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
//客户端发送消息
public void sendInfo(String info){
try {
//info+="\r\n";
ous.write(info.getBytes());
ous.flush();//强制刷新
} catch (IOException e) {
e.printStackTrace();
}
}
//客户端接收消息的方法
public void readInfo(){
System.out.println("readInfo执行了!");
while(true){
//创建一个字符串缓冲器对象
StringBuffer strb = new StringBuffer();
int reads = 0;
//判断当读到回车是#字符的存入
while(reads != 35){
try {
reads = ins.read();//将字符读入到服务器
strb.append((char)reads);//将读到的字符放入字符串缓冲区
} catch (IOException e) {
e.printStackTrace();
}
}
// System.out.println("while(reads != 13)循环退出了");
sting = strb.toString().trim();//将缓冲区的字符整合成字符串
System.out.println("客户端接收到的消息是: "+sting);
jta.setText(sting);
if(sting == "exit"){
break;
}
}
System.out.println("readInfo结束!");
}
//重写run方法
public void run(){
System.out.println("客户端的run方法执行了");
readInfo();
System.out.println("客户端的run方法结束了");
}
}
写该类遇到的一个问题就是,当执行readInfo();方法的时候,少些了while(true){}的一个死循环.当没有该循环的时候读入的方法只是执行一次当读到#的时候程序结束了readInfo(),同时也就结束了run();方法跳出了线程,这之后读不到了消息