`
Mybeautiful
  • 浏览: 297085 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

使用Server转发的聊天程序 (短小精悍,无重复代码, 支持多客户端)

阅读更多

Server 入口程序,

    仅一个类,其中 hsClientSocket 用来保存所有已经连上的客户端连接,以便实现转发.

    SocketConnection这个类是封装了socket, 以便Client跟Sever都要用到的逻辑实现共用,

   MessageReceivedHandler是个接口,传给SocketConnection, 以便让SocketConnection收到消息时做具体的事情, 且可以add多个Handler.

 

 

package coc.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Hashtable;

import coc.client.MessageReceivedHandler;
import coc.client.SocketConnection;
/**
 * @author Stony Zhang
 * @E-Mail stonyz@live.com
 * @QQ 55279427
 * @Createdate 2009-9-5
 * 
 * @Copyright the author, If you want to use the code, you must keep the author's
 *             information.
 */
public class Server {
	private int listeningPort = 6666; // TODO: the port should be configurable
	ServerSocket myServer = null;

	private Hashtable<String, SocketConnection> hsClientSocket = new Hashtable<String, SocketConnection>();

	public void start() {
		try {
			myServer = new ServerSocket(listeningPort);
			waitForConnect();
		} catch (IOException e) {
			System.out.println(e);
		}
	}

	private void waitForConnect() {
		
		class serverMSGHandler implements MessageReceivedHandler{
			
			private SocketConnection sc;
			
			public serverMSGHandler(SocketConnection sc){
				this.sc=sc;
			}
			

//			@Override
			public void Message_Recived(String message) {

				if(message.startsWith("REGISTER:")){
					//regiest the Client to Server
					String linkNum=message.substring("REGISTER:".length());
					hsClientSocket.put(linkNum, sc);
					sc.setLinkNum(linkNum);
					sc.sentMessage("Register succesfully");
				}
				else if(message.startsWith("MSGTO:")){
					//get the to dest Node socket, which regestered in server.
				    String dest=message.substring("MSGTO:".length(),"MSGTO:".length()+3);
				    String msgContent=message.substring(9);
				    
				    SocketConnection destsc = hsClientSocket.get(dest);
				    if(destsc==null){
				    	sc.sentMessage("[From :Server ] " +"The Node " + dest + " didn't log in.") ;
				    	return;
				    }
						destsc.sentMessage("[From :" + sc.getLinkNum()+ "] " + msgContent);
				}
				else if(message.startsWith("DISCONNECT:")){
					String linkNum=message.substring("DISCONNECT:".length());
					this.sc.disconnect();
					hsClientSocket.remove(linkNum);
				}
				
			
				
			}
			
		}
		
		while (!isStop) {
			String clientIP = "";
			try {
				Socket sock = myServer.accept();
//				clientIP = sock.getInetAddress().getHostAddress();
				// if (checkIP(route, clientIP)) {
//				System.out.println(" ransfer Server : " + "" + "  Incoming:"
//						+ sock.getInetAddress());
//				sock.setSoTimeout(0);
//				connCounter++;
//
//				Transfer myt = new Transfer(sock);
//				connectionQueue.add(myt);
				
				final SocketConnection sc=new SocketConnection(sock);
				
				sc.addMSGReveivedHander(new serverMSGHandler(sc));
				sc.start();
				// } else {
				// SysLog.warning(" ransfer Server : " + route.toString() +
				// "  Refuse :" + sock.getInetAddress());
				// closeSocket(sock);
				// }
			} catch (Exception ef) {
				// SysLog.severe(" Transfer Server : " + route.toString() +
				// " accept error" + ef);
			}
		}
	}

	private boolean isStop = false;
	//


	/**
	 * @author Stony Zhang
	 * @date Feb 22, 2009
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Server s = new Server();
		s.start();
	}

}

 

 

Client 入口程序

       MainGUI .java, 界面类,不多解释,

package coc.client;
import java.io.IOException;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
/**
 * @author Stony Zhang
 * @E-Mail stonyz@live.com
 * @QQ 55279427
 * @Createdate 2009-9-5
 * 
 * @Copyright the author, If you want to use the code, you must keep the author's
 *             information.
 */
public class MainGUI {

	private Text text_1;
	private Text txt_output;
	private Text txt_input;
	private Text txtServerPort;
	private Text txt_server;
	private Text text;
	protected Shell shell;
	
	Client c=null;

	/**
	 * Launch the application
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			MainGUI window = new MainGUI();
			window.open();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Open the window
	 */
	public void open() {
		final Display display = Display.getDefault();
		createContents();
		shell.open();
		shell.layout();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch())
				display.sleep();
		}
		c.disconnect();
	}

	/**
	 * Create contents of the window
	 */
	protected void createContents() {
		shell = new Shell();
		shell.setSize(367, 444);
		shell.setText("My-Connect");

		final Group connectGroup = new Group(shell, SWT.NONE);
		connectGroup.setText("Connect");
		connectGroup.setBounds(10, 10, 319, 66);

		final Label serverIpLabel = new Label(connectGroup, SWT.NONE);
		serverIpLabel.setText("Server IP :");
		serverIpLabel.setBounds(10, 20, 59, 13);

		txt_server = new Text(connectGroup, SWT.BORDER);
		txt_server.setText("192.168.254.124");
		txt_server.setBounds(80, 20, 95, 18);

		final Label serverPortLabel = new Label(connectGroup, SWT.NONE);
		serverPortLabel.setText("Server Port :");
		serverPortLabel.setBounds(181, 20, 71, 18);

		txtServerPort = new Text(connectGroup, SWT.BORDER);
		txtServerPort.setText("6666");
		txtServerPort.setBounds(252, 20, 49, 18);

		final Label linkNumberLabel = new Label(connectGroup, SWT.NONE);
		linkNumberLabel.setBounds(10, 40,69, 18);
		linkNumberLabel.setText("Link Number :");

		text = new Text(connectGroup, SWT.BORDER);
		text.setBounds(80, 41,59, 18);

		final Button connectButton = new Button(connectGroup, SWT.NONE);
		connectButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(final SelectionEvent e) {

				if(connectButton.getText().equalsIgnoreCase("connect")){
					String name=txt_server.getText().trim();
					String port=txtServerPort.getText().trim();
					String linkNum=text.getText().trim();
					
					linkNum="000" + linkNum;
					linkNum=linkNum.substring(linkNum.length()-3);
					text.setText(linkNum);
					
					c=new Client(name,port,linkNum);
					c.connect(new MessageReceivedHandler(){

//						@Override
						public void Message_Recived(String message) {
							updateSwtGui(message);
							
//							//FIXME, auto replay
//							StringBuffer msg=new StringBuffer();
//							msg.append("MSGTO:").append("022");
//							msg.append("Receieved, it's OK");
//							
//							try {
//								c.sentMessage(msg.toString());
//							} catch (IOException e) {
//								// TODO Auto-generated catch block
//								e.printStackTrace();
//							}
							//---------------------------
						}
						
					});
				}else{
					c.disconnect();
					c=null;
				}
				if(connectButton.getText().equalsIgnoreCase("connect")){
					connectButton.setText("disconnect");
				}else{
					connectButton.setText("connect");
				}
			}
		});
		connectButton.setText("Connect");
		connectButton.setBounds(240, 40, 61, 23);

		txt_input = new Text(shell, SWT.BORDER|SWT.MULTI | SWT.WRAP);
		txt_input.setBounds(10, 104, 319, 92);
//		txt_input.set

		txt_output = new Text(shell, SWT.BORDER|SWT.MULTI | SWT.WRAP);
		txt_output.setBounds(10, 224, 319, 164);

		final Label inputLabel = new Label(shell, SWT.NONE);
		inputLabel.setText("Send To :");
		inputLabel.setBounds(10, 85, 46, 13);

		final Label outputLabel = new Label(shell, SWT.NONE);
		outputLabel.setText("Output :");
		outputLabel.setBounds(10, 205, 46, 13);

		final Label lbl_status = new Label(shell, SWT.NONE);
		lbl_status.setBounds(10, 394, 319, 13);

		text_1 = new Text(shell, SWT.BORDER);
		text_1.setBounds(88, 82, 65, 16);

		final Button sendButton = new Button(shell, SWT.NONE);
		sendButton.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(final SelectionEvent e) {
				if (c == null) {
					MessageBox messageBox = new MessageBox(shell, SWT.OK);
					messageBox.setMessage("Please connect to server firstly");
					if (messageBox.open() == SWT.OK) {
						return;
					}

				}
				String to=text_1.getText().trim();
				StringBuffer msg=new StringBuffer();
				msg.append("MSGTO:").append(to);
				msg.append(txt_input.getText().trim());
				try {
					c.sentMessage(msg.toString());
				} catch (IOException e1) {
					e1.printStackTrace();
				}
				txt_input.setText("");
			}
		});
		sendButton.setText("Send");
		sendButton.setBounds(260, 80, 44, 23);
		//
	}
	
	private synchronized void updateSwtGui(final String output) {
		shell.getDisplay().asyncExec( // where "display" represents the swt gui
										// thread accessed asynchronously
				new Runnable() {
					public void run() {
						txt_output.append(output + "\n");
					}
				});
	}


}

 

Client.java, 客户端业务类

 

 

package coc.client;

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
/**
 * @author Stony Zhang
 * @E-Mail stonyz@live.com
 * @QQ 55279427
 * @Createdate 2009-9-5
 * 
 * @Copyright the author, If you want to use the code, you must keep the author's
 *             information.
 */
public class Client {
	String name;
	String ip;
	String port;
	String linkNum;
	
	Socket socket = null;
//	DataOutputStream os = null;
//	DataInputStream is = null;
	
	//TODO; server ip and port should be configurable.
	String serverIP = "192.168.254.124";
	int ServerPort=6666;
	private MessageReceivedHandler defaultHandle = new MessageReceivedHandler(){

//		@Override
		public void Message_Recived(String message) {
			// TODO Auto-generated method stub
			String host="";
			String linkNum="";
			System.out.print("Recieve message from " + host + " linkNum =" + host + " : " + message);
		}
		
	};
	
	public Client(String name,String port,String linkNum){
//		this.name=name;
		this.serverIP=name;
		this.linkNum=linkNum;
		this.port=port;
		try {
			InetAddress IPAddress = InetAddress.getByName("localhost");
			this.ip=IPAddress.getHostAddress();
		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
	}
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getIp() {
		return ip;
	}
	public void setIp(String ip) {
		this.ip = ip;
	}
	public String getPort() {
		return port;
	}
	public void setPort(String port) {
		this.port = port;
	}

	public void sentMessage(String msg) throws IOException{
		sc.sentMessage(msg);
	}
	
	public String recieveMessage(){
		return null;
	}
	
	
	public void connect(MessageReceivedHandler handle){
		try {
			socket = new Socket(this.serverIP, this.ServerPort);
			sc = new SocketConnection(socket);
			sc.addMSGReveivedHander(handle);
			sc.start();
			sc.sentMessage("REGISTER:" + this.linkNum);
//			startListenMessage();
		} catch (UnknownHostException e) {
			System.err.println("Don't know about host: hostname");
		} catch (IOException e) {
			System.err
					.println("Couldn't get I/O for the connection to: hostname");
		}
		
	}
	
	public void disconnect(){
		sc.sentMessage("DISCONNECT:");
		sc.disconnect();
	}
	
	SocketConnection sc;

	public static void main(String[] argv){
		String name=argv[0];
		String port=argv[1];
		String linkNum=argv[2];
		Client c=new Client(name,port,linkNum);
		c.connect(c.defaultHandle);
		try {
			c.sentMessage("MSGTO:002 Are you ok?");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
}

 

MessageReceiverdHandler.java  传给 SocketConnection类的Handler,作用前面已经说了, 具体实现在Client.java, Server.java各有一个。

 

 

package coc.client;
/**
 * @author Stony Zhang
 * @E-Mail stonyz@live.com
 * @QQ 55279427
 * @Createdate 2009-9-5
 * 
 * @Copyright the author, If you want to use the code, you must keep the author's
 *             information.
 */
public interface MessageReceivedHandler {
	void Message_Recived(String message);
}

 

   SocketConnection.java  封装socket, 扩展了一些功能。

 

 

package coc.client;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
/**
 * @author Stony Zhang
 * @E-Mail stonyz@live.com
 * @QQ 55279427
 * @Createdate 2009-9-5
 * 
 * @Copyright the author, If you want to use the code, you must keep the author's
 *             information.
 */
public class SocketConnection {
	Socket socket = null;
	DataOutputStream os = null;
	DataInputStream is = null;
	public SocketConnection(Socket socket){
		this.socket=socket;
		try {
			os = new DataOutputStream(socket.getOutputStream());
			is = new DataInputStream(socket.getInputStream());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public void start(){
		startListenMessage();
	}
	
	class MessageReader{
		List<MessageReceivedHandler> handlers=new ArrayList<MessageReceivedHandler>();
		Thread t=new Thread(){
			public void run(){
				try {
					String responseLine;
					while ((responseLine = is.readLine()) != null) {
//						System.out.println("Server: " + responseLine);
						for (MessageReceivedHandler h : handlers) {
							h.Message_Recived(responseLine);
						}
						if (responseLine.indexOf("Ok") != -1) {
							break;
						}
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
				System.out.print("Message Reader exit");
			}
		};
		
		public void start(){
			t.start();
		}

		public void addHander(MessageReceivedHandler Handle) {
			handlers.add(Handle);
			
		}
	}
	
	MessageReader reader=new MessageReader();
	private void startListenMessage() {
//		reader.addHander(this.defaultHandle);
		reader.start();
	}
	
	public void addMSGReveivedHander(MessageReceivedHandler Handle){
		reader.addHander(Handle);
	}
	
	public void sentMessage(String msg){
		try {
			os.writeBytes(msg + "\n");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	private String linkNum="";
	public void setLinkNum(String linkNum) {
		this.linkNum=linkNum;
		
	}

	public String getLinkNum() {
		return linkNum;
	}

	public void disconnect() {
		try {
			is.close();
			os.close();
			socket.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	
}
 

    客户端运行如图,

       002 说 Are you ok?

       001 说 I'm fine Thank you!


 

    完整 eclipse工程下载,下载..

 

  • My_Chat.zip (7.7 MB)
  • 描述: 完整 eclipse工程下载
  • 下载次数: 147
  • 大小: 18.4 KB
3
2
分享到:
评论
2 楼 Mybeautiful 2010-08-09  
1楼的朋友,MessageReceivedHandle就是想把Server, Client共用的逻辑抽出来。

抽出来的部分就是 SocketConnection, 但是由于server,跟client收到数据时,做不一样的事情,所以必须要把 各自做的事情再提取出来,就是MessageReceivedHandle了。

当然可能还有更加精简的办法(不使用第三方的框架的前提下),但是我觉得MessageReceivedHandle的实现是不是一定要用内部类实现,其实,是不一定的,只是我这里是用的内部类。如果不用内部类也行,只是要把相关信息传到你的这个新类,比如构造函数传入一个socket等。
1 楼 pcenshao 2010-07-30  
有一个疑问是 MessageReceivedHandler抽象的实用性不好 只能用内部类

相关推荐

    程序精灵——短小精悍的实用代码

    - **用户输入**:程序通过`cin`接收用户输入的高度值`s`,并根据这个值来确定每行需要打印的空格数和星号数。 - **空格计算**:通过变量`g`存储距离左端的空格数,这里设置为`s + 25`,使得小一点的树也能居中显示。...

    二十个C++经典程序附源代码

    在本资源中,我们收集了二十个经典的C++程序,并附带了源代码,涵盖了从小应用到小游戏的各种项目。这些程序对于学习C++语言、理解面向对象编程思想以及实际编程技巧有着极大的帮助。下面,我们将详细探讨其中涉及的...

    一个短小精悍的MS SQL Server数据库日志清除工具程序。.rar

    标题中的“一个短小精悍的MS SQL Server数据库日志清除工具程序”指的是针对Microsoft SQL Server数据库的日志清理工具。MS SQL Server是一个广泛使用的商业关系型数据库管理系统,它在处理大量数据时会产生大量的...

    完全免费的自动升级程序,短小精悍、功能强大!

    2. 根据版本号比较结果,从服务器中下载相应程序或数据进行升级,然后使用本地与服务器版本号一致。版本号可以分级,用“.”隔开,级数不限,如10或10.01或100.001.002等等。 3. 可以指定单个文件的存放目录,指定的...

    简单FTP功能软件 绿色短小精悍版

    5. **多用户支持**:FTP服务器可以同时处理多个客户端连接,使得多人可以并发地进行文件传输。 6. **传输模式选择**:FTP支持主动模式和被动模式。主动模式中,服务器主动发起数据连接,而被动模式下,客户端建立...

    ASP.NET短小精悍Ajax留言本(无广告)

    【ASP.NET短小精悍Ajax留言本(无广告)】是一个基于ASP.NET技术构建的Web应用程序,它利用Ajax(Asynchronous JavaScript and XML)技术实现了页面的动态无刷新更新,为用户提供流畅、高效的交互体验。这个留言本...

    短小精悍快速定时关机程序

    【短小精悍快速定时关机程序】是一种实用的小型应用程序,专为用户设计,以实现高效的电脑自动关机功能。这种程序的核心价值在于其简洁、快速和易用的特性,使得用户能够在设定的时间内方便地让计算机自动关闭,从而...

    vc下ftp协议实现多线程断点续传源代码

    4.分服务端和客户端,服务器支持多客户. 5.在传送过程中可实实对话,有聊天功能. 6.为了提高效率,本软件做得短小精悍,两文件总共只有100K,直截运行,不用安装. 7.麻雀虽小,但五脏俱全,功能多,操作方便. 使用方法: 在...

    多线程断点续传软件《传圣》源代码

    4.分服务端和客户端,服务器支持多客户. 5.在传送过程中可实实对话,有聊天功能. 6.为了提高效率,本软件做得短小精悍,两文件总共只有100K,直截运行,不用安装. 7.麻雀虽小,但五脏俱全,功能多,操作方便. 使用方法: ...

    IP/TCP协议下的文件传输源代码(VC)

    4.分服务端和客户端,服务器支持多客户. 5.在传送过程中可实实对话,有聊天功能. 6.为了提高效率,本软件做得短小精悍,两文件总共只有100K,直截运行,不用安装. 7.麻雀虽小,但五脏俱全,功能多,操作方便.

    vc++ 开发实例源码包

    内部包含:mp3播放器Lrc歌词同步源程序代码分析、mp3播放器+支持歌词同步显示哦、简单音乐播放器。 mfc 解码 视频音频解码部分。 MFC_MultiSender_OVER 文件传送,多文件(超大文件)传送功能的实现,含文档。 ...

    C# 实现Scoket心跳机制的方法

    服务器在一个Timer事件中定时向客户端发送一个短小精悍的数据包,然后启动一个低级别的线程,在该线程中不断检测客户端的回应,如果在一定时间内没有收到客户端的回应,即认为客户端已经掉线。 TCP的KeepAlive保活...

    短小精悍 ftp服务器软件 免安装

    "短小精悍 FTP服务器软件 免安装" 指的是一款轻量级、无需复杂安装过程的FTP服务器应用程序,它简化了FTP服务器的配置和管理,使得用户能够快速地在自己的计算机上搭建一个FTP服务。 FTP服务器软件的核心功能是提供...

    SQLyog-10.0.0-0客户端.zip

    3、支持客户端挑选、过滤数据; 4、批量执行很大的SQL脚本文件; 5、快速执行多重查询并能够返回每页超过1000条的记录集,而这种操作是直接生成在内存中的; 6、程序本身非常短小精悍!压缩后只有348 KB ; 7、完全...

    怎么样写好安全代码

    1. **程序和方法的短小精悍**:保持函数和模块的规模适中,使得每个部分的功能单一且易于管理。 2. **使用编程语言的惯用法**:熟悉并运用特定编程语言的常用编程模式和最佳实践,这样可以使代码更加规范和易于理解...

    短小精悍的图书馆新书自动编目程序.caj

    短小精悍的图书馆新书自动编目程序.caj

    4-04 子程序设计.zip

    9. **子程序库和模块**:在许多编程语言中,可以将子程序组织成库或模块,方便在不同项目中重复使用。 10. **子程序调用的性能影响**:虽然子程序提供了诸多好处,但每次调用都会带来一定的开销。因此,对于性能...

    自己开发的WebSocket 调试工具

    自己开发的WebSocket 调试工具,基于aardio 短小精悍,支持服务器、客户端,十六进制等,支持多客户端连接,支持对指定的客户发送消息,支持多URL监听,用英文";" 分隔即可, 支持发送消息保存,下次启动可快速选择...

    msnippet:记录一些短小精悍的功能代码片段,如一些小工具程序

    【描述】MSnippet是一个汇集了各种实用、短小精悍的Python代码片段的资源库,旨在帮助开发者快速解决日常编程中的常见问题。这个集合包括了诸如微信好友检查、图片爬虫以及豆瓣爬虫等应用示例,覆盖了Python编程中的...

Global site tag (gtag.js) - Google Analytics