你好,博客园!!第一弹~局域网下的简易聊天室,socket与多线程简结

Posted 枫林晚月

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了你好,博客园!!第一弹~局域网下的简易聊天室,socket与多线程简结相关的知识,希望对你有一定的参考价值。

  发觉博客园里面关于这些基本知识点的详细内容真是应有尽有,所以这里的随笔就不再重复了,就积累一下简单的用法——

  1.Socket

  最近学网络编程,也就是Socket,套接字,一个用来建立链接传输数据的工具。

  数据传输发生在“客户端”与“服务端”之间,下面是一种建立连接传输数据的简单方法:

  (1)客户端

 1 try{
 2     //服务端ip
 3   String ip = "127.0.0.1";
 4   //服务器端口
 5   int port = 5000;
 6 
 7   //1.通过服务器ip和端口,创建Socket对象,连接服务器
 8   Socket socket = new Socket(ip, port);
 9   //2.获取Socket输出流,并包装为DataOutputStream
10   DataOutputStream output = new DataOutputStream(socket.getOutputStream());
11   //传输的内容
12   String send = "halo";
13   //3.加密输出,发送到服务端
14     //将一个字符串转换成字符数组
15     char[] char_arr = send.toCharArray();
16     //对每个字符·进行加密操作
17     for(int i=0; i<char_arr.length; i++){
18         output.writeChar(char_arr[i] + 12);
19     }
20     //在输出内容后加一个特殊符号作为结束符
21     output.writeChar(33333);
22     //刷写内容
23     output.flush();
24 } catch (IOException e) {
25     e.printStackTrace();
26 }

  以上为客户端创建连接,发送数据的简单方法。下面是接收数据的方法,注意如果服务端不发送数据过来,代码会停留在字符读取那里。

 1 try{
 2     //4.获取Socket输入流,并包装为DataInputStream
 3     DataInputStream input = new DataInputStream(socket.getInputStream());
 4     //5.获取并解密,接收返回结果
 5     String rtn = "";
 6     while(true) {
 7         //从输出流中读取一个字符,直到结束符
 8         int char_src = input.readChar();
 9         if(char_src != 33333){
10             rtn = rtn + (char)(char_src - 12);
11         }else{
12             break;
13         }
14     }
15     //读取结束,rtn即是服务端发送过来的信息
16 } catch (IOException e) {
17     e.printStackTrace();
18 }

  使用完Socket,一定要关闭Socket及相应的输入输出流。

1 try {2     if(output != null){output.close();}
3     if(input != null){input.close();}
4     if(socket != null){socket.close();}
5 } catch (IOException e) {
6     e.printStackTrace();
7 }

  (1)服务端

  服务端创建连接的方法——

1 //1.创建ServerSocket对象,监听端口5000
2 ServerSocket server_socket = new ServerSocket(5000);
3 //2.调用ServerSocket的accept方法,等待客户端连接(若没有连接,代码停留在这里),成功连接之后,返回Socket对象
4 Socket socket = server_socket.accept();

  创建连接后,数据传输方法跟客户端是同样的步骤,获取Socket的输出输入流并包装,然后加密输出,获取解密,这里不重复了。

  注意这里用了加密传输,所以服务端客户端的加密方法、解密方法一定要一致。用完ServerSocket,也要关闭。

 

  ******************************************************

 

  2.多线程

  多线程简单地说,就是为程序建立分支。例如,在聊天室的服务端中,每有一个用户连接进来,就为其建立一个用于接收的分支,这样可以做到同时接受多个用户的数据。

  建立多线程的方法

  

 1 //这是主线程的类,包含主方法
 2 public class text{
 3     public static void main(String [] args){
 4         //new一个thread类,并调用它的start()方法,就建立了一个线程。这步可以重复,不断地建立分支线程
 5         //分支线程建立后,程序一边执行下面的代码,一边执行thread类中的run()方法(分支)
 6         new thread().start();
 7         //继续主线程的其他代码
 8         System.out.println("主线程");
 9     }
10 }
11 
12 //thread类必需继承Thread类,这是建立分支线程的关键
13 class thread extends Thread{
14     //需要在分支线程中执行的代码放到run()中
15     public void run(){
16         System.out.println("分支线程");
17     }
18 }

  多线程中各个线程同时运行,其运行没有确定的顺序,并不是先启动的线程一定先执行,当前一刻,谁抢占了 CUP 资源,谁就执行。

  所以上面的程序在cmd中会有不同的执行结果,可能是"主线程"在前面,也可能是"分支线程"在前面。

  多线程还可以通过实现 Runnable 接口来实现,详见聊天室的代码。

 

************************************************

  

  3.聊天室

  客户端——

  1 import java.awt.*;
  2 import java.awt.event.*;
  3 import javax.swing.*;
  4 import java.io.*;
  5 import java.net.*;
  6 
  7 public class chat {
  8 
  9     //主方法
 10     public static void main(String[] args) {
 11         chat ch = new chat();
 12         ch.loginframe();
 13         
 14     }
 15 
 16     //登录窗口
 17     public JFrame lf;
 18     public JTextField lmi;
 19     public JTextField lii;
 20     public int ltz = 1;
 21     public String y;
 22     public void loginframe(){
 23         lf = new JFrame("创建用户"); 
 24         lf.setVisible(true);
 25         lf.setSize(400, 280);
 26         lf.setLocationRelativeTo(null);
 27         lf.addWindowListener(new WindowAdapter(){
 28             public void windowClosing(WindowEvent e){
 29                 closes();
 30                 System.exit(0);
 31             }
 32         });
 33         
 34         JPanel op = new JPanel();
 35         lf.add(op);
 36         Box lbox = Box.createVerticalBox();
 37         op.add(lbox);
 38         
 39         lbox.add(Box.createVerticalStrut(40));
 40 
 41         JPanel lfp = new JPanel();
 42         lfp.setOpaque(false);
 43         JLabel lft = new JLabel("服务器ip:");
 44         lft.setFont(new Font("华文中宋", Font.PLAIN, 18));
 45         lii = new JTextField("127.0.0.1" ,9);
 46         lii.setFont(new Font("微软雅黑", Font.PLAIN, 18));
 47         lfp.add(lft);
 48         lfp.add(lii);
 49         lbox.add(lfp);
 50         
 51         JPanel lip = new JPanel();
 52         lip.setOpaque(false);
 53         JLabel lwt = new JLabel("用户名:");
 54         lwt.setFont(new Font("华文中宋", Font.PLAIN, 18));
 55         lmi = new JTextField(10);
 56         lmi.setFont(new Font("微软雅黑", Font.PLAIN, 18));
 57         lmi.requestFocus();
 58         lip.add(lwt);
 59         lip.add(lmi);
 60         lbox.add(lip);
 61         
 62         lbox.add(Box.createVerticalStrut(20));
 63         
 64         JPanel obp = new JPanel();
 65         JButton odb = new JButton("加入聊天");
 66         odb.setFont(new Font("微软雅黑", Font.PLAIN, 15));    
 67         JLabel obt = new JLabel("    ");
 68         obt.setFont(new Font("华文中宋", Font.PLAIN, 15));
 69         JButton oqb = new JButton("清空输入");
 70         oqb.setFont(new Font("微软雅黑", Font.PLAIN, 15));
 71         obp.add(odb);
 72         obp.add(obt);
 73         obp.add(oqb);
 74         lbox.add(obp);
 75         
 76         //按钮监听
 77         odb.addActionListener(new ActionListener(){
 78             public void actionPerformed(ActionEvent e) {
 79                 y = lmi.getText();
 80                 if("".equals(y)){
 81                     JOptionPane.showMessageDialog(null,
 82                             "请输入用户名!", "创建用户", JOptionPane.INFORMATION_MESSAGE);
 83                     lmi.requestFocus();
 84                 }else{
 85                     ip = lii.getText();
 86                     ysend();
 87                     lf.dispose();
 88                     chatframe();
 89                     //开启一条接收信息的线程
 90                     th.start();
 91                 }
 92              }
 93         });
 94         
 95         oqb.addActionListener(new ActionListener(){
 96             public void actionPerformed(ActionEvent e) {
 97                 lmi.setText("");
 98              }
 99         });
100         
101     }
102     
103     //聊天室
104     public JTextField i;
105     public JTextArea l;
106     public String book = "";
107     public void chatframe(){
108         JFrame cf = new JFrame("聊天室 - " + y); 
109         cf.setVisible(true);
110         cf.setSize(600, 500);
111         cf.setLocationRelativeTo(null);
112         cf.addWindowListener(new WindowAdapter(){
113             public void windowClosing(WindowEvent e){
114                 closes();
115                 System.exit(0);
116             }
117         });
118         
119         JPanel sfp = new JPanel();
120         sfp.setLayout(new BorderLayout());
121         i = new JTextField("请在此输入聊天内容");
122         i.setSelectionStart(0);
123         i.setSelectionEnd(9);
124         i.setFont(new Font("微软雅黑", Font.PLAIN, 20));
125         JButton fsb = new JButton("发 送");
126         fsb.setFont(new Font("微软雅黑", Font.PLAIN, 15));
127         sfp.add(i, BorderLayout.CENTER);
128         sfp.add(fsb, BorderLayout.EAST);
129         cf.add(sfp, BorderLayout.SOUTH);
130 
131         l = new JTextArea();
132         l.setFont(new Font("华文中宋", Font.PLAIN, 18));
133         JScrollPane jt = new JScrollPane(l);
134         l.setText(book);
135         cf.add(jt, BorderLayout.CENTER);
136         
137         //按钮监听
138         fsb.addActionListener(new ActionListener(){
139             public void actionPerformed(ActionEvent e) {
140                 if("".equals(i.getText())){
141                     i.setText("不能发送空白信息~");
142                     i.setSelectionStart(0);
143                     i.setSelectionEnd(9);
144                     i.requestFocus();
145                 }else{
146                     nsend(i.getText());
147                     i.setText("");
148                     i.requestFocus();
149                 }
150              }
151         });
152         
153     }
154     //made by feng(^-^)
155     //创建用户
156     public void ysend(){
157         try {
158             String r;
159             connect();
160             //发送到服务端
161             encryptWrite(y, out);
162             r = readDecrypt(in);
163             book = r;
164             String[] str = r.split("进入聊天室~");
165             if(!(str[0].equals(y))){
166                 JOptionPane.showMessageDialog(null,
167                         "用户“"+y+"”已在聊天室中,系统为你改为"+"“"+str[0]+"”", 
168                         "创建用户", JOptionPane.INFORMATION_MESSAGE);
169                 y = str[0];
170             }
171         } catch (IOException e1) {
172             e1.printStackTrace();
173         }
174     }
175     
176     //创建socket及相关流
177     public String ip;
178     public int port = 6000;
179     public Socket so;
180     public DataOutputStream out;
181     public DataInputStream in;
182     Thread th = new Thread(new Runc());
183     //连接到服务器
184     public void connect(){
185         try {
186             so = new Socket(ip, port);
187             out = new DataOutputStream(so.getOutputStream());
188             in = new DataInputStream(so.getInputStream());
189         } catch (IOException e) {
190             e.printStackTrace();
191         }
192     }
193     
194     //内容传输
195     public void nsend(String n){
196         try {
197             //发送到服务端
198             String s = y + ":" + n;
199             encryptWrite(s, out);
200         } catch (IOException e) {
201             e.printStackTrace();
202         }
203     }
204     
205     //关闭流,停止线程运行
206     public boolean bc = true;
207     public void closes(){
208         try {
209             bc = false;
210             //延迟两秒
211             //Thread.sleep(1000 * 2);
212             if(out != null){out.close();}
213             if(in != null){in.close();}
214             if(so != null){so.close();}
215         } catch (IOException e) {
216             e.printStackTrace();
217         } 
218     }
219     
220     //加密输出
221     public void encryptWrite(String src, DataOutputStream output) throws IOException{
222         char[] char_arr = src.toCharArray();
223         for(int i=0; i<char_arr.length; i++){
224             output.writeChar(char_arr[i] + 12);
225         }
226         output.writeChar(33333);
227         output.flush();
228     }
229     
230     //读取解密
231     public String readDecrypt(DataInputStream input) throws IOException{
232         String rtn = "";
233         
234         while(true) {
235             //从输出流中读取一个字符,直到结束符
236             int char_src = input.readChar();
237             if(char_src != 33333){
238                 rtn = rtn + (char)(char_src - 12);
239             }else{
240                 break;
241             }
242         }
243         return rtn;
244     }
245     
246     /**用于接收消息的线程*/
247     class Runc implements Runnable{
248         public void run(){
249                 try {
250                     while(bc){
251                         String re = readDecrypt(in);
252                         book = book + re;
253                         l.setText(book);
254                     }
255                 } catch (SocketException e) {
256                     //System.out.println("socket关闭,停止接受消息");
257                 } catch (EOFException e) {
258                     //System.out.println("与服务器断开连接");
259                 } catch (IOException e) {
260                     e.printStackTrace();
261                 }
262         }
263     }
264     
265 }

  

 

 

  服务端——

  1 import java.awt.BorderLayout;
  2 import java.awt.event.*;
  3 import java.io.*;
  4 import java.net.*;
  5 import javax.swing.*;
  6 
  7 public class chatserver {
  8     //聊天记录
  9     public static String book = "";
 10     //当前消息
 11     public static String nowm;
 12     //用户名数组
 13     public static String[] name;
 14     //Socket数组
 15     public static Socket[] so;
 16     //每个用户配备一个输出流
 17     public static DataOutputStream[] out;
 18     //用户数量
 19     public static int num = 0;
 20     public static ServerSocket ss = null;
 21     public static Socket s = null;
 22     public static void main(String [] args){
 23         chatserver c = new chatserver();
 24         c.frame();
 25         
 26         try {
 27             //监听6000
 28             ss = new ServerSocket(6000);
 29             //初始化用户数组
 30             name = new String[100];
 31             so = new Socket[100];
 32             out = new DataOutputStream[100];
 33             //System.out.println("OK!");
 34             while(true){
 35                 s = ss.accept();
 36                 so[num] = s;
 37                 out[num] = new DataOutputStream(so[num].getOutputStream());
 38                 num++;
 39                 //当有用户接入时,开启一条线程
 40                 new Thread(new Runs(so[num-1])).start();
 41             }
 42         } catch (SocketException e) {
 43             //System.out.println("ss已关闭!");
 44         } catch (IOException e) {
 45             e.printStackTrace();
 46             c.closes();
 47         }
 48     }
 49 
 50     //服务器窗口
 51     public JFrame sf;
 52     public static JLabel sl;
 53     public static JLabel rl;
 54     public void frame() {
 55         sf = new JFrame("服务端");
 56         sf.setVisible(true);
 57         sf.setSize(400, 250);
 58         sf.setLocationRelativeTo(null);
 59         sf.addWindowListener(new WindowAdapter(){
 60             public void windowClosing(WindowEvent e){
 61                 closes();
 62                 System.exit(0);
 63             }
 64         });
 65         
 66         sl = new JLabel("当前在线人数:"+num);
 67         rl = new JLabel("等待用户接入...");
 68         sf.add(sl, BorderLayout.NORTH);
 69         sf.add(rl, BorderLayout.CENTER);
 70     }
 71     //made by feng(+-+)
 72     //更新窗口显示
 73     public void renew(){
 74         sl.setText("当前在线人数:"+num);
 75         if (num == 0) {
 76             rl.setText("等待用户接入...");
 77         }else{
 78             String s = "";
 79             for (int n = 0; n <= (num - 1); n++) {
 80                 s = s + name[n] + ",";
 81             }
 82             rl.setText("在线用户:"+s);
 83         }
 84     }
 85 
 86     //用户接入处理
 87     public synchronized String addh(String y){
 88         int x = 2;
 89         String newy = y;
 90         //检测用户名是否已经存在
 91         for(int n=0; n<(num-1); n++){
 92             if(newy.equals(name[n])){
 93                 //若存在,改为+(n)
 94                 newy = y + " (" +x+ ")";
 95                 x++;
 96                 n = -1;
 97             }
 98         }
 99         name[num-1] = newy;
100         nowm = newy + "进入聊天室~" + "\n";
101         book = book + nowm;
102         renew();
103         sendm();
104         return newy;
105     }
106     
107     //信息处理
108     public synchronized void messageh(String n){
109         nowm = n + "\n";
110         book = book + nowm;
111         sendm();
112     }
113     
114     //用户断开处理
115     public synchronized void reduceh(String y) {
116         int m = 0;
117         //查找断开连接的用户
118         for(int n=0; n<=(num-1); n++){
119             if(y.equals(name[n])){
120                 m = n;
121                 break;
122             }
123         }
124         //关闭用户的输出流
125         try {
126             out[m].close();
127         } catch (IOException e) {
128             e.printStackTrace();
129         }
130         //数组元素前移
131         for(int n=m; n<=(num-1); n++){
132             name[n] = name[n+1];
133             so[n] = so[n+1];
134             out[n] = out[n+1];
135         }
136         //用户数量减1
137         num--;
138         renew();
139         nowm =  y + "离开聊天室~" + "\n";
140         book = book + nowm;
141         sendm();
142     }
143         
144     //消息传输
145     public void sendm(){
146         try {
147             for(int n=0; n<=(num-1); n++){
148                 encryptWrite(nowm, out[n]);
149             }
150         } catch (IOException e) {
151             e.printStackTrace();
152             closes();
153         }
154     }
155     
156     //加密输出
157     public void encryptWrite(String src, DataOutputStream output) throws IOException{
158         char[] char_arr = src.toCharArray();
159         for(int i=0; i<char_arr.length; i++){
160             output.writeChar(char_arr[i] + 12);
161         }
162         output.writeChar(33333);
163         output.flush();
164     }
165     
166     //关闭socket及流
167     public void closes() {
168         try{
169             for(int n=0; n<=(num-1); n++){
170                 out[n].close();
171             }
172             //System.out.println("outputStream关闭");
173             if(s != null){s.close();}
174             if(ss != null){ss.close();}
175             //System.out.println("s,ss关闭");
176         } catch (IOException e1) {
177             e1.printStackTrace();
178         }
179     }
180 }
181 
182 /**接收客户端消息*/
183 class Runs implements Runnable{
184     chatserver cs = new chatserver();
185     
186     //创建流
187     public Socket socket;
188     public DataInputStream input;
189     public Runs(Socket s){
190         socket = s;
191         try {
192             input = new DataInputStream(socket.getInputStream());
193         } catch (IOException e) {
194             e.printStackTrace();
195         }
196     }
197     
198     public void run(){
199         String n = null;
200         try {
201             //将输入传到主线程并返回用户名
202             n = cs.addh(readDecrypt(input));
203             while(true){
204                 cs.messageh(readDecrypt(input));
205             }
206         } catch (SocketException e) {
207             //System.out.println("socket关闭,停止接受消息");
208         } catch (EOFException e) {
209             //System.out.println("客户端断开,关闭线程");
210             //断开连接后移除用户
211             cs.reduceh(n);
212             //System.out.println(n+"退出成功!");
213         } catch (IOException e) {
214             e.printStackTrace();
215         } finally{
216             try {
217                 if(input != null){ input.close();}
218                 if(socket != null){ socket.close();}
219             } catch (IOException e) {
220                 e.printStackTrace();
221             }
222         }
223     }
224     
225     //读取解密
226     public String readDecrypt(DataInputStream input) throws IOException{
227         String rtn = "";
228         
229         while(true) {
230             //从输出流中读取一个字符,直到结束符
231             int char_src = input.readChar();
232             if(char_src != 33333){
233                 rtn = rtn + (char)(char_src - 12);
234             }else{
235                 break;
236             }
237         }
238         return rtn;
239     }
240 }

  

 

 

  支持多用户,可以在局域网下运行。虽然简单,但也是小弟呕心沥血之作,如果有大佬看见,也希望给点意见啦~

  另外,有人知道怎么上传flsah动画吗,好像这个功能不怎么好使。。

  第一弹就到这里啦~

以上是关于你好,博客园!!第一弹~局域网下的简易聊天室,socket与多线程简结的主要内容,如果未能解决你的问题,请参考以下文章

博客园,你好

Hello World! 你好,博客园!

java简易的局域网聊天工具

你好啊,点进来认识一下吧

你好,博客园

博客第一弹