基于Socket跟多线程的聊天程序实现

基于Socket和多线程的聊天程序实现

Java最后一道作业题竟然是写个多线程聊天工具。以前在一次实训中做过linux版的。当时就有人说Java版的实现比较简单,如今看来确实有理。尤其是在做UI上,不用像gtk那么麻烦。先将我搜到的资源与大家共享(亲测可用):

该网络聊天程序大致分为三个主要部分:客户端、服务器端和用户图形界面。各个部分的初步设计思想、流程及存储结构如下:

1.    程序整体框架:主程序监听一端口,等待客户接入;同时构造一个线程类,准备接管会话。当一个Socket会话产生后,将这个会话交给线程处理,然后主程序继续监听。

 

打开Socket

 

命 名

 

监听端口

 

建立连接

 

收发消息

 

关闭连接

 

打开Socket

 

 

连接服务器

 

收发消息

 

关闭连接

 

服务器端程序

 

客户端程序

 

2.    客户端(Client)

客户端,使用Socket对网络上某一个服务器的某一个端口发出连接请求,一旦连接成功,打开会话;会话完成后,关闭Socket。客户端不需要指定打开的端口,通常临时的、动态的分配一个端口。

3.    服务器端(Server)

服务器端,使用ServerSocket监听指定的端口,端口可以随意指定(由于1024以下的端口通常属于保留端口,在一些操作系统中不可以随意使用,所以建议使用大于1024的端口),等待客户连接请求,客户连接后,会话产生;在完成会话后,关闭连接。

4.    用户图形界面

用户图形界面方便程序与用户的交互,多个用户参加,完成会话功能,具体的设计要方便用户的使用,直观清晰,简洁明了,友好美观。

5.    存储结构

下面列出主要存储结构或变量:

存储结构、变量、对象

类型

说明

post

InetAddress

标识IP地址

Port

int

标识端口

Server [ ]

ServerThread

服务器端连接数

Client [ ]

Socket

客户端连接数

Client(String ip,int p,Face chat)

public

Client类成员函数

Public void run()

Void

Client、Server类成员函数

Server(int port,Face chat)

public

Server类成员函数

Face()

Public

Face类成员函数

1.服务器端

服务器端主要是使用ServerSocket类,相当于服务器Socket,用来监听试图进入的连接,当新的连接建立后,该类为他们实例化一个Socket对象,同时得到输入输出流,调用相应方法完成会话。

具体代码如下:

[java] view plaincopyprint?
  1. package nupt.java.socket;  
  2. import java.awt.*;  
  3. import java.net.*;  
  4. import java.io.*;  
  5. public class Server extends Thread {  
  6. ServerSocket skt;   // ServerSocket类监听进入的连接,为每个新的连接产生一个Socket对象         
  7.     Socket Client[ ]=new Socket[10];  
  8.     Socket Client1=null;  
  9.     int i = 0;  
  10.     TextArea in;  
  11.     int port,k=0,l=0;  
  12.     PrintStream theOutputStream;  
  13.     Face chat;  
  14.     public Server(int port, Face chat) {  
  15.         try {  
  16.             this.port = port;  
  17.             skt = new ServerSocket(port);  
  18.             this.chat = chat;  
  19.         } catch (IOException e) {  
  20.             chat.ta.append(e.toString());  
  21.         }  
  22.     }  
  23.     public void run() {  
  24.         chat.ta.append("等待连线......");  
  25.         while (true) {  
  26.             try {  
  27.             Client[k] = skt.accept();  
  28.                                               //当有客户端连接时就新建一个子线程   
  29.             if (i < 2) {  
  30.               ServerThread server[] = new ServerThread[10];  
  31.               server[k]= new ServerThread(Client[k], this.chat, i);  
  32.                  l=server.length;  
  33.                  server[k].start();  
  34.                 chat.ta.append(“客户端“+ Client[k].getInetAddress() + "已连线\n");  
  35.                  
  36.                 //for(int j=0;j<server.length;j++)   
  37.                 theOutputStream = new PrintStream(server[k].getClient().getOutputStream());  
  38.                 i = server[k].getI();  
  39.                 k++;  
  40.             } else {  
  41.                 //theOutputStream = new PrintStream(null);   
  42.             }  
  43.         } catch (SocketException e) {  
  44.             } catch (IOException e) {  
  45.                 chat.ta.append(e.toString());  
  46.             }  
  47.         }  
  48.     }  
  49.     public void dataout(String data) {  
  50.         //for(int j=0;j<l;j++)   
  51.         theOutputStream.println(data);  
  52.     }  
  53. }  
  54. class ServerThread extends Thread {  
  55.     ServerSocket skt;  
  56.     Socket Client;  
  57.     TextArea in;  
  58.     int port,i;  
  59.     BufferedReader theInputStream;  
  60.     PrintStream theOutputStream;  
  61.     String readin;  
  62.     Face chat;  
  63. //服务端子线程   
  64.     public ServerThread(Socket s, Face chat, int i) {  
  65.         this.i = ++i;  
  66.         Client = s;  
  67.         this.chat = chat;  
  68.     }  
  69.     public int getI() {  
  70.         return this.i;  
  71.     }  
  72.     public Socket getClient() {  
  73.         return this.Client;  
  74.     }  
  75.     public void run() {  
  76.           try {  
  77.             theInputStream = new BufferedReader(new InputStreamReader(Client  
  78.                     .getInputStream()));  
  79.             theOutputStream = new PrintStream(Client.getOutputStream());  
  80.             while (true) {  
  81.                 readin = theInputStream.readLine();  
  82.                 chat.ta.append(readin + "\n");  
  83.             }  
  84.         } catch (SocketException e) {  
  85.             chat.ta.append("连线中断!\n");  
  86.                            // 设置组件可用性   
  87.             chat.clientBtn.setEnabled(true);  
  88.             chat.serverBtn.setEnabled(true);  
  89.             chat.tfaddress.setEnabled(true);  
  90.             chat.tfport.setEnabled(true);  
  91.             try {  
  92.                 i - -;  
  93.                 skt.close();  
  94.                 Client.close();  
  95.             } catch (IOException err) {  
  96.                 chat.ta.append(err.toString());  
  97.             }  
  98.         } catch (IOException e) {  
  99.             chat.ta.append(e.toString());  
  100.         }  
  101.     }  
  102.     public void dataout(String data) {  
  103.         theOutputStream.println(data);  
  104.     }  
  105. }  

2.客户端

客户端主要是使用Socket类,该类是JAVA实现网络编程重要的基础类,实现程序间双向的面向连接的通信。调用public Socket(String host,int port)方法设定IP和端口。建好连接后,用户通过得到Socket的输入输出流对象后,利用流的方法实现数据的传输。调用public InputStream getInputStream()和public OutputStream getOutputStream()方法,分别得到Socket对象的输入输出流;

具体实现代码如下:

[java] view plaincopyprint?
  1. package nupt.java.socket;  
  2. import java.net.*;  
  3. import java.io.*;  
  4. import javax.swing.Timer;  
  5. public class Client extends Thread {  
  6.       Socket skt;                                  // 用于客户端的连接   
  7.     InetAddress host;                        // 主机地址   
  8.     int port;                                     // 端口号   
  9.     BufferedReader theInputStream;  
  10.     PrintStream theOutputStream;  
  11.     String readin;  
  12.     Face chat;  
  13.     public Client(String ip, int p, Face chat) {  
  14.         try {  
  15.             host = InetAddress.getByName(ip);            // 获取IP地址   
  16.             port = p;                                                  // 获取端口号   
  17.             this.chat = chat;  
  18.         } catch (IOException e) {  
  19.             chat.ta.append(e.toString());  
  20.         }  
  21.     }  
  22.     public void run() {  
  23.         try {  
  24.             chat.ta.append("准备连线,稍后!");  
  25.             skt = new Socket(host, port);                     // 新建Socket对象   
  26.             chat.ta.append("成功\n");                   // 缓冲区末尾添加字符串   
  27.             theInputStream = new BufferedReader(new InputStreamReader(skt.getInputStream()));  
  28.             theOutputStream = new PrintStream(skt.getOutputStream());  
  29.             while (true) {  
  30.                 readin = theInputStream.readLine();  
  31.                 chat.ta.append(readin + "\n");  
  32.             }  
  33.         } catch (SocketException e) {  
  34.             chat.ta.append("未连上!\n");  
  35.             chat.clientBtn.setEnabled(true);  
  36.             chat.serverBtn.setEnabled(true);  
  37.             chat.tfaddress.setEnabled(true);  
  38.             chat.tfport.setEnabled(true);  
  39.             try {  
  40.                 skt.close();  
  41.             } catch (IOException err) {  
  42.                 chat.ta.append(err.toString());  
  43.             }  
  44.         } catch (IOException e) {  
  45.             chat.ta.append(e.toString());  
  46.         }  
  47.     }  
  48.     public void dataout(String data) {  
  49.         theOutputStream.println(data);  
  50.     }  
  51. }  

3.用户图形界面

该部分主要是完成界面的初始化,合理布局组件,方便用户交互。主要是JAVA按钮,文本域,标签,布局管理器的使用。主要处理了键盘Enter消息接受,下面是实现代码:

[java] view plaincopyprint?
  1. package nupt.java.socket;  
  2. import java.awt.*;  
  3. import java.awt.event.*;  
  4. public class Face extends Frame {  
  5.        
  6.       private static final long serialVersionUID = 1L;  
  7.     Button clientBtn, serverBtn;  
  8.     TextArea ta;  
  9.     TextField tfaddress, tfport, tftype;  
  10.     Label lbl1,lbl2,lbl3;  
  11.     int port;  
  12.     Client client;  
  13.     Server server;  
  14.     boolean iamserver;  
  15.     static Face frm;  
  16.     public Face() {  
  17.           // 实例化组件   
  18.         clientBtn = new Button("客户端");  
  19.         serverBtn = new Button("服务器");  
  20.         ta = new TextArea(""1050, TextArea.SCROLLBARS_BOTH);  
  21.         lbl1 = new Label("IP地址:");  
  22.         tfaddress = new TextField("192.168.1.104"10);  
  23.         lbl2 = new Label("端口:");  
  24.         tfport = new TextField("8080");  
  25.         lbl3 = new Label("发送内容:");  
  26.         tftype = new TextField(40);  
  27.         tftype.addKeyListener(new TFListener());  
  28.         ta.setEditable(false);  
  29.         //向容器中加入以上组件   
  30.         setLayout(new   FlowLayout());  
  31.         add(lbl1);  
  32.         add(tfaddress);  
  33.         add(lbl2);  
  34.         add(tfport);  
  35.         add(clientBtn);  
  36.         add(serverBtn);  
  37.         add(ta);  
  38.         add(lbl3);  
  39.         add(tftype);  
  40.         //设置格式   
  41.         setLocation(400250);                //窗口显示再屏幕的位置坐标   
  42.         setSize(400300);                      //设置窗体大小   
  43.         setTitle("基于Socket和多线程编程的聊天程序");  
  44.         this.setVisible(true);                   //设置窗体可见   
  45.         //事件响应   
  46.         clientBtn.addActionListener(new ActionListener() {  
  47.             public void actionPerformed(ActionEvent e) {  
  48.                 port = Integer.parseInt(tfport.getText());  
  49.                 client = new Client(tfaddress.getText(), port, frm);  
  50.                 client.start();  
  51.                 tfaddress.setEnabled(false);  
  52.                 tfport.setEnabled(false);  
  53.                 serverBtn.setEnabled(false);  
  54.                 clientBtn.setEnabled(false);  
  55.             }  
  56.         });  
  57.         serverBtn.addActionListener(new ActionListener() {  
  58.             public void actionPerformed(ActionEvent e) {  
  59.                 port = Integer.parseInt(tfport.getText());  
  60.                 server = new Server(port, frm);  
  61.                 server.start();  
  62.                 iamserver = true;  
  63.                 tfaddress.setText("成为服务器");  
  64.                 tfaddress.setEnabled(false);  
  65.                 tfport.setEnabled(false);  
  66.                 serverBtn.setEnabled(false);  
  67.                 clientBtn.setEnabled(false);  
  68.             }  
  69.         });  
  70.         addWindowListener(new WindowAdapter() {  
  71.             public void windowClosing(WindowEvent e) {  
  72.                 System.exit(0);  
  73.             }  
  74.         });  
  75.     }  
  76.       public static void main(String[] args) {        //主方法   
  77.              // TODO Auto-generated method stub   
  78.               
  79.              frm = new Face();  
  80.       }  
  81.       private class TFListener implements KeyListener {  
  82.         public void keyPressed(KeyEvent e) {  
  83.           if (e.getKeyCode() == KeyEvent.VK_ENTER) { //按Enter输出显示聊天内容   
  84.                 ta.append(">" + tftype.getText() + "\n");  
  85.                 if (iamserver)  
  86.                     server.dataout(tftype.getText());  
  87.                 else  
  88.                     client.dataout(tftype.getText());  
  89.                 tftype.setText("");  
  90.             }  
  91.         }  
  92.         public void keyTyped(KeyEvent e) {  
  93.         }  
  94.         public void keyReleased(KeyEvent e) {  
  95.         }  
  96.     }  
  97. }  

如有不对的地方或者更好的方法,欢迎大家批评指正,交流学习。