-------------siwuxie095

   

   

   

   

   

   

工程名:TestMyServerSocket

包名:com.siwuxie095.socket

类名:MyServerSocket.java(主类)、ServerListener.java、ChatSocket.java、ChatManager.java

   

   

工程结构目录如下:

   

自建聊天系统docker 利用socket创建聊天服务器_服务器

   

   

   

   

MyServerSocket.java(主类):

   

package com.siwuxie095.socket;
  
/**
* 聊天服务器,不仅能向客户端发送数据,也能从客户端读取数据								
* 
* @author siwux
*
*/
  
public class MyServerSocket {
     
/**
     * 这是主类(主线程),启动线程ServerListener进行监听,								
     * 当有Socket对象进行连接时,在线程ServerListener中
     * 启动线程ChatSocket,同时将该ChatSocket线程添加到聊天
     * 管理器的集合Vector中
     * 
     * @param args
     */
  
public staticvoid main(String[] args) {
         
//运行线程ServerListener,使用匿名对象								
new ServerListener().start();
    }
  
}

   

   

   

ServerListener.java:

   

package com.siwuxie095.socket;
  
import java.io.IOException;
import .ServerSocket;
import .Socket;
  
import javax.swing.JOptionPane;
  
//创建线程 ServerListener,将有阻塞的代码放到这个独立的线程中								
//将新建立的ChatSocket线程添加到集合Vector中
public classextends Thread {
  
// 复写run()								
@SuppressWarnings("resource")
@Override
publicvoid run() {
try {
  
// 创建一个ServerSocket对象,并指定端口:12345								
// 端口的范围:1~65535,通常都指定较大的数字,
// 这样和较小的或系统预留的端口分开
// 有异常抛出,用 try catch 捕获
new12345);
  
             
// ServerSocket创建完成后需要侦听客户端的连接								
// 调用accept()方法,这是一个阻塞的方法,
// 会阻塞当前的线程,对于有阻塞的代码,应该放到独立的线程中
//(ServerListener 就是一个独立的线程)
// 返回值是Socket类型,创建以接收返回值
// 当accept()被执行且socket被赋值,说明有客户端连接
//每当有一个客户端连接到ServerSocket,accept()都会返回一个新的Socket对象
//如果有多个客户端来连接当前的服务器ServerSocket,就会有多个Socket对象出现
//需要一个while循环来循环监听
while (true) {
                 
                Socket socket = serverSocket.accept();
  
// 建立连接时								
// 弹出提示框:有客户端连接到本机的 12345 端口
"有客户端连接到本机的 12345 端口");
                 
//由于每一个socket要与一个独立的客户端进行通信								
//所以要将socket传递给新的线程:ChatSocket(用于Socket通信)
//每一个socket都有一个独立的ChatSocket线程
//每一个ChatSocket线程之间是相互独立的,它们不能相互沟通数据
//新建一个类ChatManager,将这些新建的ChatSocket线程管理起来,
//实现它们之间的相互通信
//运行该线程
new ChatSocket(socket);
                cs.start();
                 
//通过静态方法将cs添加到聊天管理器中								
                ChatManager.getChatManager().add(cs);
                 
            }
             
  
catch (IOException e) {
            e.printStackTrace();
        }
  
    }
  
}

   

   

   

ChatSocket.java:

   

package com.siwuxie095.socket;
  
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import .Socket;
  
/**
* run()循环执行的读取的工作,即当前的服务器会不断的从客户端读取内容								
* 将读取到的内容发送到集合Vector中的所有客户端(除了自身)
* 
* @author siwux
*
*/
  
//创建用于Socket通信的线程:ChatSocket								
public classextends Thread {
     
    Socket socket;
     
//创建构造方法,传入Socket对象								
public ChatSocket(Socket socket) {
this.socket=socket;
    }
     
publicvoid output(String out) {
         
try {
  
//对当前的Socket执行									数据输出相关功能的包装								
//使用getOutputStream()获取输出流,通过输出流向外输出数据
//返回值是OutputStream类型,创建以接收返回值
            OutputStream os=socket.getOutputStream();
  
//创建一个BufferedWriter作为数据的输出,传入匿名对象,指定编码,层层包装								
newnew"UTF-8"));
  
//让BufferedWriter输出字符串								
            bw.write(out);
//因为带缓冲,所以需要强制输出,不然无法输出								
            bw.flush();
  
catch (UnsupportedEncodingException e) {
            e.printStackTrace();
catch (IOException e) {
            e.printStackTrace();
        }
  
    }
     
//复写run()方法								
@Override
publicvoid run() {
         
try {
             
//对Socket的输入流进行包装								
//在指定InputStreamReader时,指定编码的字符集
//有异常抛出,用 try catch 捕获
new BufferedReader(
new InputStreamReader(
"UTF-8"));
             
            String line=null;
//读取从客户端发送给服务器的数据								
while ((line=br.readLine())!=null) {
//通过静态方法,将自己传入,同时传入line								
this, line);
            }
             
//关闭流								
            br.close();
catch (IOException e) {
            e.printStackTrace();
        }
         
         
         
    }
}

   

   

   

ChatManager.java:

   

package com.siwuxie095.socket;
  
import java.util.Vector;
  
  
/**
* ChatManager 将不同的socket所新建的ChatSocket线程管理起来								
* 由于一个聊天服务器只能有一个聊天的管理器:ChatManager
* 所以要把这个类作单例化处理
* 单例化的第一步就是让这个类的构造方法变成 private 类型
* 
* @author siwux
*
*/
public class ChatManager {
     
//让构造方法变成private类型,完成单例化的第一步								
private ChatManager(){}
      
//为当前类创建一个实例								
private static finalnew ChatManager();
     
//创建方法 getChatManager(),返回ChatManager类型								
public static ChatManager getChatManager() {
return cm;
    }
     
     
     
//至此,完成了这个类(ChatManager)的单例化								
//接下来就是对ChatSocket线程进行管理
     
     
//创建一个Vector,指定泛型为ChatSocket								
new Vector<>();
     
//创建add()方法,为当前集合添加一个新的ChatSocket对象								
//在创建ChatSocket时使用,即ServerListener.java中使用
publicvoid add(ChatSocket cs) {
//调用Vector的add()方法,传入cs即可								
        vector.add(cs);
    }
     
     
//创建publish()方法,其中的某个ChatSocket线程可以调用publish()								
//向其他的客户端(其他的ChatSocket线程)发送信息
//传入线程本身和需要发送的信息
publicvoid publish(ChatSocket cs,String msg) {
         
//因为要发送给集合Vector中的其他所有线程,要使用遍历								
forint0; i < vector.size(); i++) {
//获取循环中的第 i 个对象								
            ChatSocket csx=vector.get(i);
             
//当前发送信息的线程就不用再接收这条信息,								
//判断发送消息的对象是不是当前对象
if (!cs.equals(csx)) {
                csx.output(msg);
            }
        }
    }
             
     
     
     
     
     
  
}

   

   

运行后,终止按钮(Terminate)长亮,即 当前程序正在运行 且 没有停止

   

自建聊天系统docker 利用socket创建聊天服务器_自建聊天系统docker_02

   

而且,此时也没有任何提示框,即当前程序被阻塞在 ServerListener.java 的:

Socket socket=serverSocket.accept();

   

   

打开 CMD 窗口,输入:telnet 127.0.0.1 12345或telnet localhost 12345

(即 本机地址+端口),回车。此时,会弹框提示:

   

自建聊天系统docker 利用socket创建聊天服务器_客户端_03

   

   

   

点击确定

   

打开多个 CMD 窗口,重复操作,然后在其中一个 CMD 窗口任意输入,

其他窗口就会显示该输入

   

自建聊天系统docker 利用socket创建聊天服务器_java_04

   

   

   

点击 终止按钮(Terminate),可结束运行

   

 

【made by siwuxie095】