一个简单的基于socket的通讯处理程序
2019獨角獸企業(yè)重金招聘Python工程師標準>>>
這幾天看書看得java網(wǎng)絡編程,看到一個不錯的,適合新手的,部分代碼借鑒書上的,可能有地方還不是很成熟,不過可以借鑒一下,分為客戶端和服務端,話不多說,貼代碼,很多都在注釋中給出
客戶端程序:
YeekuProtocol類,是一個幫助類,用來定義協(xié)議字符串???? /**
?*
?*<description>用來定義的協(xié)議字符</description>
?* @author Administrator
?* @date 2013-11-26
?* @file CrazyitProtocol.java
?* @category com.inspur.net.MultiThread.chat.server
?* @version 1.0
?*
?*/
public interface YeekuProtocol {
?//定義協(xié)議字符串長度為2
?int PROROCLO_LEN = 2;
?//下面是一些協(xié)議字符串,服務器和客戶端交換的信息
?//都應該在前、后添加這種特殊字符串。
?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>客戶端增加了讓用戶輸入用戶名的代碼,并且不允許用戶名重復
?*除此之外,還可以根據(jù)用戶的鍵盤輸入來判斷用戶是否想發(fā)送私聊信息</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(){
??//不斷從輸入流讀取數(shù)據(jù),并且打印輸出
??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>客戶端增加了讓用戶輸入用戶名的代碼,并且不允許用戶名重復
?*除此之外,還可以根據(jù)用戶的鍵盤輸入來判斷用戶是否想發(fā)送私聊信息</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 = "";
???//采用循環(huán)不斷彈出對話框要求輸入用戶名
???while(true){
????String userName = JOptionPane.showInputDialog(tip+"輸入用戶名");
????//將用戶名前后增加協(xié)議字符串
????ps.println(YeekuProtocol.USER_ROUND+userName+YeekuProtocol.USER_ROUND);
????//讀取服務器的相應
????String read = brServer.readLine();
????//如果用戶重復,開始下一次循環(huán)
????if(read.equals(YeekuProtocol.NAME_REP)){
?????tip = "用戶名重復,請刷新";
?????continue;
????}
????//如果服務器返回成功,結(jié)束循環(huán)
????if(read.equals(YeekuProtocol.LOGIN_SUCCESS)){
?????break;
????}
???}
??} catch (UnknownHostException e) {
???// TODO Auto-generated catch block
???System.out.println("找不到遠程服務器,確認服務已經(jīng)啟動");
???closeRs();
???System.exit(1);
???e.printStackTrace();
??} catch (IOException e) {
???// TODO Auto-generated catch block
???System.out.println("網(wǎng)絡異常,請重新登錄");
???e.printStackTrace();
??}
??new Thread(new clientThread(brServer)).run();
?}
?//定義一個從鍵盤讀入,并向網(wǎng)絡發(fā)送的方法
?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類是服務端用來存儲用戶名和輸入輸出流之間的映射關系,這樣服務端就可以根據(jù)用戶名來尋找相對應的socket,實現(xiàn)私密通信,利用map這一數(shù)據(jù)結(jié)構存儲
?
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
/**
?*
?*<description>服務器端用來存儲用戶名和輸入輸出流之間的映射關系,這樣服務器就可以根據(jù)用戶名來
?*尋找相應的socket,實現(xiàn)客戶端之間的通信,服務器只要獲取gia用戶名對應的輸出流即可,提供的這個HashMap類
?*可以根據(jù)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;
?//根據(jù)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監(jiān)聽客戶端,增加異常處理</description>
?* @author Administrator
?* @date 2013-11-26
?* @file Server.java
?* @category com.inspur.net.MultiThread.chat.server
?* @version 1.0
?*
?*/
public class Server {
?
?//監(jiān)聽端口
?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("服務器已經(jīng)啟動,是否端口"+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開始和結(jié)束
????//可以確定讀到的是用戶的用戶名
????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開始結(jié)束
????//可以確定是私聊信息,私聊信息都以特定的流輸出
????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];
?????//獲取私聊用戶的輸出流,并發(fā)送私聊信息
?????Server.clients.get(User).println(
???????Server.clients.getKeyByValue(ps)+"悄悄對你說:"+Msg);
????}
????//公聊的話向每個socket都發(fā)送
????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();
??}
?}
?//將讀到的內(nèi)容去掉前后的字符串,恢復成真實數(shù)據(jù)
?public String getRealMsg(String line){
??return line.substring(YeekuProtocol.PROROCLO_LEN, line.length()-YeekuProtocol.PROROCLO_LEN);
?}
}
以上就是這個簡單實現(xiàn),我測試的時候時將他們導出成jar文件,然后再cmd窗口運行jar文件,此處在
MANIFEST.MF文件中應該加上一句Main-Class: 包名+類名,注意:后面必須有個空格,而且在結(jié)束之后要以換行結(jié)束,shift+enter,可能也有失誤的地方,本人新手,勿噴?
轉(zhuǎn)載于:https://my.oschina.net/u/1034481/blog/179457
總結(jié)
以上是生活随笔為你收集整理的一个简单的基于socket的通讯处理程序的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WPF 正確理解ContentPrese
- 下一篇: 更改消息存储的位置