一个简单的基于socket的通讯处理Java程序

jopen 10年前

这几天看书看得java网络编程,看到一个不错的,适合新手的,部分代码借鉴书上的,可能有地方还不是很成熟,不过可以借鉴一下,分为客户端和服务端,话不多说,贴代码,很多都在注释中给出

客户端程序:

YeekuProtocol类,是一个帮助类,用来定义协议字符串   
 /**   *   *<description>用来定义的协议字符</description>   * @author Administrator   * @date 2013-11-26   * @file CrazyitProtocol.java   * @category com.inspur.net.MultiThread.chat.server   * @version 1.0   *   */  public interface YeekuProtocol {     //定义协议字符串长度为2   int PROROCLO_LEN = 2;   //下面是一些协议字符串,服务器和客户端交换的信息   //都应该在前、后添加这种特殊字符串。   String MSG_ROUND = "§γ";   String USER_ROUND = "∏∑";   String LOGIN_SUCCESS = "1";   String NAME_REP = "-1";   String PRIVATE_ROUND = "★【";   String SPLIT_SIGN = "※";  }    clientThread线程类是客户端处理输入流的几个类         import java.io.BufferedReader;  import java.io.IOException;    /**   *<description>客户端增加了让用户输入用户名的代码,并且不允许用户名重复   *除此之外,还可以根据用户的键盘输入来判断用户是否想发送私聊信息</description>   * @author Administrator   * @date 2013-11-26   * @file clientThread.java   * @category com.inspur.net.MultiThread.chat.client   * @version 1.0   *   */  public class clientThread extends Thread{     //该线程负责处理输入流   BufferedReader br = null;   public clientThread(BufferedReader br){    this.br = br;   }   public void run(){    //不断从输入流读取数据,并且打印输出    String line = null;    try {     while((line=br.readLine())!=null){      System.out.println(line);           }    } catch (IOException e) {     // TODO Auto-generated catch block     e.printStackTrace();    }finally{     if(br!=null){      try {       br.close();      } catch (IOException e) {       // TODO Auto-generated catch block       e.printStackTrace();      }     }    }   }  }    client类,主要负责客户端的一些操作         import java.io.BufferedReader;  import java.io.IOException;  import java.io.InputStreamReader;  import java.io.PrintStream;  import java.net.Socket;  import java.net.UnknownHostException;    import javax.swing.JOptionPane;    import com.inspur.net.MultiThread.chat.interfacefolder.YeekuProtocol;    /**   *<description>客户端增加了让用户输入用户名的代码,并且不允许用户名重复   *除此之外,还可以根据用户的键盘输入来判断用户是否想发送私聊信息</description>   * @author Administrator   * @date 2013-11-26   * @file client.java   * @category com.inspur.net.MultiThread.chat.client   * @version 1.0   *   */  public class Client {     private static final  int SERVER_PORT = 40001;   private static final String ip = "192.168.1.123";   private Socket socket;   private PrintStream ps ;   private BufferedReader brServer;   private BufferedReader keyIn;      public void init(){    keyIn = new BufferedReader(new InputStreamReader(System.in));    try {     socket = new Socket(ip,SERVER_PORT);     ps = new PrintStream(socket.getOutputStream());     brServer = new BufferedReader(new InputStreamReader(socket.getInputStream()));     String tip = "";     //采用循环不断弹出对话框要求输入用户名     while(true){      String userName = JOptionPane.showInputDialog(tip+"输入用户名");      //将用户名前后增加协议字符串      ps.println(YeekuProtocol.USER_ROUND+userName+YeekuProtocol.USER_ROUND);      //读取服务器的相应      String read = brServer.readLine();      //如果用户重复,开始下一次循环      if(read.equals(YeekuProtocol.NAME_REP)){       tip = "用户名重复,请刷新";       continue;      }      //如果服务器返回成功,结束循环      if(read.equals(YeekuProtocol.LOGIN_SUCCESS)){       break;      }     }    } catch (UnknownHostException e) {     // TODO Auto-generated catch block     System.out.println("找不到远程服务器,确认服务已经启动");     closeRs();     System.exit(1);     e.printStackTrace();    } catch (IOException e) {     // TODO Auto-generated catch block     System.out.println("网络异常,请重新登录");     e.printStackTrace();    }    new Thread(new clientThread(brServer)).run();   }   //定义一个从键盘读入,并向网络发送的方法   public void readAndSend(){    //不断从键盘读入    String line = null;    try {     while((line=keyIn.readLine())!=null){      if(line.indexOf(":")>0 && line.startsWith("//")){//如果以//开头,则默认为私聊信息,后面必须输入要私聊的对象       line = line.substring(2);       ps.println(YeekuProtocol.PRIVATE_ROUND+line.split(":")[0]+YeekuProtocol.SPLIT_SIGN         +line.split(":")[1]+YeekuProtocol.PRIVATE_ROUND);      }      else{       ps.println(YeekuProtocol.MSG_ROUND+line+YeekuProtocol.MSG_ROUND);      }     }    } catch (IOException e) {     // TODO Auto-generated catch block     e.printStackTrace();    }       }   public void closeRs(){//关闭释放IO资源    try {     if(keyIn!=null){      keyIn.close();     }     if(brServer!=null){      brServer.close();     }     if(ps!=null){      ps.close();     }     if(socket!=null){      socket.close();     }    } catch (IOException e) {     // TODO Auto-generated catch block     e.printStackTrace();    }   }   public static void main(String[] args) {//程序入口    Client client = new Client();    client.init();    client.readAndSend();   }  }    服务器端:服务器端主要有YeekuMap类和server,serverThred类    YeekuMap类是服务端用来存储用户名和输入输出流之间的映射关系,这样服务端就可以根据用户名来寻找相对应的socket,实现私密通信,利用map这一数据结构存储         import java.util.HashMap;  import java.util.HashSet;  import java.util.Set;  /**   *   *<description>服务器端用来存储用户名和输入输出流之间的映射关系,这样服务器就可以根据用户名来   *寻找相应的socket,实现客户端之间的通信,服务器只要获取gia用户名对应的输出流即可,提供的这个HashMap类   *可以根据value获取key值,或者是通过value删除key值,而且不允许value的重复</description>   * @author Administrator   * @date 2013-11-26   * @file YeekuMap.java   * @category com.inspur.net.MultiThread.chat.server   * @version 1.0   *   */  public class YeekuMap<K,V> extends HashMap<K,V>{     /**    *    */   private static final long serialVersionUID = 1L;   //根据value值来删除指定项   public void remonveByValue(Object value){    for(Object key : keySet()){     if(get(key)==value){      remove(key);      break;     }    }   }   //获取所有value组成的集合   public Set<V> valueSet(){    Set<V> result = new HashSet<V>();    for(K key : keySet()){     //将每个key对应的value添加到result集合中     result.add(get(key));    }    return result;   }   //通过value查找key   public K getKeyByValue(V val){    //便利所有key组成的集合    for(K key : keySet()){     //如果指定key对应的value与被搜索的value相同     //则返回该key的值     if(get(key).equals(val) &&get(key)==val){      return key;     }         }    return null;   }   //重写HashMap的put方法,该方法允许value的重复   public V put(K key , V value){    for(V val : valueSet()){     //如果指定的value与试图放入集合的value相同     //则抛出一个RuntimeException     if(val == get(key) && val.equals(get(key))){      throw new RuntimeException("MyMap实例中不允许重复");     }    }    return super.put(key, value);   }     }    server类,    import java.io.IOException;  import java.io.PrintStream;  import java.net.ServerSocket;  import java.net.Socket;    /**   *   *<description>服务器端程序,建立ServerSocket监听客户端,增加异常处理</description>   * @author Administrator   * @date 2013-11-26   * @file Server.java   * @category com.inspur.net.MultiThread.chat.server   * @version 1.0   *   */  public class Server {      //监听端口   private static final int SERVER_PORT = 40001;   //使用MyMap对象来存储每个客户名字和对应的输出流之间的关系   public static YeekuMap<String ,PrintStream> clients = new YeekuMap<String,PrintStream>();   private Socket socket;   public void init(){    try {     ServerSocket serverSocket = new ServerSocket(SERVER_PORT);     while(true){       socket = serverSocket.accept();      new Thread(new ServerThread(socket)).start();     }    } catch (IOException e) {     // TODO Auto-generated catch block     System.out.println("服务器已经启动,是否端口"+SERVER_PORT+"被占用");     e.printStackTrace();    }finally{     if(socket!=null){      try {       socket.close();      } catch (IOException e) {       // TODO Auto-generated catch block       e.printStackTrace();      }      System.exit(1);     }    }   }   public static void main(String[] args) {    Server server = new Server();    server.init();   }  }    serverThraead类         import java.io.BufferedReader;  import java.io.IOException;  import java.io.InputStreamReader;  import java.io.PrintStream;  import java.net.Socket;    import com.inspur.net.MultiThread.chat.interfacefolder.YeekuProtocol;  /**   *   *<description>服务器端程序,处理客户端请求的实体类</description>   * @author Administrator   * @date 2013-11-26   * @file ServerThread.java   * @category com.inspur.net.MultiThread.chat.server   * @version 1.0   *   */  public class ServerThread implements Runnable{     private Socket socket;   private BufferedReader br = null;   private PrintStream ps = null;   public ServerThread(Socket socket){    this.socket = socket;    try {     br = new BufferedReader(new InputStreamReader(socket.getInputStream()));     ps = new PrintStream(socket.getOutputStream());    } catch (IOException e) {     // TODO Auto-generated catch block     e.printStackTrace();    }   }   public void run() {    // TODO Auto-generated method stub    String line = null;    try {     while((line=br.readLine())!=null){      //如果读到以MyProtocol.USER ROUND开始和结束      //可以确定读到的是用户的用户名      if(line.startsWith(YeekuProtocol.USER_ROUND) && line.endsWith(YeekuProtocol.USER_ROUND)){              String userName = getRealMsg(line);       if(Server.clients.containsKey(userName)){        System.out.println("重复");        ps.println(YeekuProtocol.NAME_REP);       }else{        System.out.println("成功");        ps.println(YeekuProtocol.LOGIN_SUCCESS);        Server.clients.put(userName, ps);       }      }      //如果读到的行以YeekuProtocol.PRIVATE_ROUND开始结束      //可以确定是私聊信息,私聊信息都以特定的流输出      else if(line.startsWith(YeekuProtocol.PRIVATE_ROUND) && line.endsWith(YeekuProtocol.PRIVATE_ROUND)){       //得到真实信息       String userAndMsg = getRealMsg(line);       String User = userAndMsg.split(YeekuProtocol.SPLIT_SIGN)[0];       String Msg = userAndMsg.split(YeekuProtocol.SPLIT_SIGN)[1];       //获取私聊用户的输出流,并发送私聊信息       Server.clients.get(User).println(         Server.clients.getKeyByValue(ps)+"悄悄对你说:"+Msg);      }      //公聊的话向每个socket都发送      else{       //得到真实信息       String msg = getRealMsg(line);       //便利clients的每个输出流       for(PrintStream clientsPs : Server.clients.valueSet()){        clientsPs.println(Server.clients.getKeyByValue(ps)+"说"+msg);       }      }     }    } catch (IOException e) {     // TODO Auto-generated catch block     Server.clients.remonveByValue(ps);     System.out.println(Server.clients.size());     //关闭io资源     try {      if(br != null){       br.close();      }      if(ps != null){       ps.close();      }      if(socket !=null){       socket.close();      }     } catch (IOException e1) {      // TODO Auto-generated catch block      e1.printStackTrace();     }     e.printStackTrace();    }   }   //将读到的内容去掉前后的字符串,恢复成真实数据   public String getRealMsg(String line){    return line.substring(YeekuProtocol.PROROCLO_LEN, line.length()-YeekuProtocol.PROROCLO_LEN);   }  } 

以上就是这个简单实现,我测试的时候时将他们导出成jar文件,然后再cmd窗口运行jar文件,此处在

MANIFEST.MF文件中应该加上一句Main-Class: 包名+类名,注意:后面必须有个空格,而且在结束之后要以换行结束,shift+enter