基于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对象,同时得到输入输出流,调用相应方法完成会话。

具体代码如下:

package nupt.java.socket;
import java.awt.*;
import java.net.*;
import java.io.*;
public class Server extends Thread {
ServerSocket skt;   // ServerSocket类监听进入的连接,为每个新的连接产生一个Socket对象      
    Socket Client[ ]=new Socket[10];
    Socket Client1=null;
    int i = 0;
    TextArea in;
    int port,k=0,l=0;
    PrintStream theOutputStream;
    Face chat;
    public Server(int port, Face chat) {
        try {
            this.port = port;
            skt = new ServerSocket(port);
            this.chat = chat;
        } catch (IOException e) {
            chat.ta.append(e.toString());
        }
    }
    public void run() {
        chat.ta.append("等待连线......");
        while (true) {
            try {
            Client[k] = skt.accept();
                                              //当有客户端连接时就新建一个子线程
            if (i < 2) {
              ServerThread server[] = new ServerThread[10];
              server[k]= new ServerThread(Client[k], this.chat, i);
                 l=server.length;
                 server[k].start();
                chat.ta.append(“客户端“+ Client[k].getInetAddress() + "已连线\n");
               
                //for(int j=0;j<server.length;j++)
                theOutputStream = new PrintStream(server[k].getClient().getOutputStream());
                i = server[k].getI();
                k++;
            } else {
                //theOutputStream = new PrintStream(null);
            }
        } catch (SocketException e) {
            } catch (IOException e) {
                chat.ta.append(e.toString());
            }
        }
    }
    public void dataout(String data) {
        //for(int j=0;j<l;j++)
        theOutputStream.println(data);
    }
}
class ServerThread extends Thread {
    ServerSocket skt;
    Socket Client;
    TextArea in;
    int port,i;
    BufferedReader theInputStream;
    PrintStream theOutputStream;
    String readin;
    Face chat;
//服务端子线程
    public ServerThread(Socket s, Face chat, int i) {
        this.i = ++i;
        Client = s;
        this.chat = chat;
    }
    public int getI() {
        return this.i;
    }
    public Socket getClient() {
        return this.Client;
    }
    public void run() {
          try {
            theInputStream = new BufferedReader(new InputStreamReader(Client
                    .getInputStream()));
            theOutputStream = new PrintStream(Client.getOutputStream());
            while (true) {
                readin = theInputStream.readLine();
                chat.ta.append(readin + "\n");
            }
        } catch (SocketException e) {
            chat.ta.append("连线中断!\n");
                           // 设置组件可用性
            chat.clientBtn.setEnabled(true);
            chat.serverBtn.setEnabled(true);
            chat.tfaddress.setEnabled(true);
            chat.tfport.setEnabled(true);
            try {
                i - -;
                skt.close();
                Client.close();
            } catch (IOException err) {
                chat.ta.append(err.toString());
            }
        } catch (IOException e) {
            chat.ta.append(e.toString());
        }
    }
    public void dataout(String data) {
        theOutputStream.println(data);
    }
}

2.客户端

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

具体实现代码如下:

package nupt.java.socket;
import java.net.*;
import java.io.*;
import javax.swing.Timer;
public class Client extends Thread {
      Socket skt;                                  // 用于客户端的连接
    InetAddress host;                        // 主机地址
    int port;                                     // 端口号
    BufferedReader theInputStream;
    PrintStream theOutputStream;
    String readin;
    Face chat;
    public Client(String ip, int p, Face chat) {
        try {
            host = InetAddress.getByName(ip);            // 获取IP地址
            port = p;                                                  // 获取端口号
            this.chat = chat;
        } catch (IOException e) {
            chat.ta.append(e.toString());
        }
    }
    public void run() {
        try {
            chat.ta.append("准备连线,稍后!");
            skt = new Socket(host, port);                     // 新建Socket对象
            chat.ta.append("成功\n");                   // 缓冲区末尾添加字符串
            theInputStream = new BufferedReader(new InputStreamReader(skt.getInputStream()));
            theOutputStream = new PrintStream(skt.getOutputStream());
            while (true) {
                readin = theInputStream.readLine();
                chat.ta.append(readin + "\n");
            }
        } catch (SocketException e) {
            chat.ta.append("未连上!\n");
            chat.clientBtn.setEnabled(true);
            chat.serverBtn.setEnabled(true);
            chat.tfaddress.setEnabled(true);
            chat.tfport.setEnabled(true);
            try {
                skt.close();
            } catch (IOException err) {
                chat.ta.append(err.toString());
            }
        } catch (IOException e) {
            chat.ta.append(e.toString());
        }
    }
    public void dataout(String data) {
        theOutputStream.println(data);
    }
}

3.用户图形界面

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

package nupt.java.socket;
import java.awt.*;
import java.awt.event.*;
public class Face extends Frame {
     
      private static final long serialVersionUID = 1L;
    Button clientBtn, serverBtn;
    TextArea ta;
    TextField tfaddress, tfport, tftype;
    Label lbl1,lbl2,lbl3;
    int port;
    Client client;
    Server server;
    boolean iamserver;
    static Face frm;
    public Face() {
          // 实例化组件
        clientBtn = new Button("客户端");
        serverBtn = new Button("服务器");
        ta = new TextArea("", 10, 50, TextArea.SCROLLBARS_BOTH);
        lbl1 = new Label("IP地址:");
        tfaddress = new TextField("192.168.1.104", 10);
        lbl2 = new Label("端口:");
        tfport = new TextField("8080");
        lbl3 = new Label("发送内容:");
        tftype = new TextField(40);
        tftype.addKeyListener(new TFListener());
        ta.setEditable(false);
        //向容器中加入以上组件
        setLayout(new   FlowLayout());
        add(lbl1);
        add(tfaddress);
        add(lbl2);
        add(tfport);
        add(clientBtn);
        add(serverBtn);
        add(ta);
        add(lbl3);
        add(tftype);
        //设置格式
        setLocation(400, 250);                //窗口显示再屏幕的位置坐标
        setSize(400, 300);                      //设置窗体大小
        setTitle("基于Socket和多线程编程的聊天程序");
        this.setVisible(true);                   //设置窗体可见
        //事件响应
        clientBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                port = Integer.parseInt(tfport.getText());
                client = new Client(tfaddress.getText(), port, frm);
                client.start();
                tfaddress.setEnabled(false);
                tfport.setEnabled(false);
                serverBtn.setEnabled(false);
                clientBtn.setEnabled(false);
            }
        });
        serverBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                port = Integer.parseInt(tfport.getText());
                server = new Server(port, frm);
                server.start();
                iamserver = true;
                tfaddress.setText("成为服务器");
                tfaddress.setEnabled(false);
                tfport.setEnabled(false);
                serverBtn.setEnabled(false);
                clientBtn.setEnabled(false);
            }
        });
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
    }
      public static void main(String[] args) {        //主方法
             // TODO Auto-generated method stub
            
             frm = new Face();
      }
      private class TFListener implements KeyListener {
        public void keyPressed(KeyEvent e) {
          if (e.getKeyCode() == KeyEvent.VK_ENTER) { //按Enter输出显示聊天内容
                ta.append(">" + tftype.getText() + "\n");
                if (iamserver)
                    server.dataout(tftype.getText());
                else
                    client.dataout(tftype.getText());
                tftype.setText("");
            }
        }
        public void keyTyped(KeyEvent e) {
        }
        public void keyReleased(KeyEvent e) {
        }
    }
}

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

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值