一個簡單的基於socket的通信處理程序

這幾天看書看得java網絡編程,看到一個不錯的,適合新手的,部分代碼借鑑書上的,可能有地方還不是很成熟,不過能夠借鑑一下,分爲客戶端和服務端,話很少說,貼代碼,不少都在註釋中給出 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{ socket

 //該線程負責處理輸入流
 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類,主要負責客戶端的一些操做 this

 

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException; .net

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,可能也有失誤的地方,本人新手,勿噴
相關文章
相關標籤/搜索