命令行界面的C/S聊天室应用 (Socket多线程实现)

命令行界面即在Eclipe控制台输入数据。

服务器端包含多个线程,每个Socket对应一条线程,该线程负责读取对应输入流的数据(从客户端发送过来的数据),并将读到的数据向每个Socket输出流发送一遍(将一个客户端发送的数据"广播给其他客户端"),因此需要在服务器端使用List来保存所有的Socket.

 

下面是服务器端的实现代码,程序为服务器提供了两个类,一个是创建ServerSocket监听的主类,一个是负责处理每个Socket通信的线程类.

package com.net.scday1;

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

public class MyServer {
	//定义保存所有Socket的ArrayList
	public static ArrayList<Socket> scoketList=new ArrayList<Socket>();
	public static void main(String[] args) {
		
		try {
			ServerSocket ss=new ServerSocket(30000);
			while (true) {
				//此代码会阻塞,将一直等待别人的连接
				Socket s=ss.accept();
				scoketList.add(s);
				//每当客户端连接后启动一条ServerThread线程为该客户端服务
				new Thread(new ServerThread(s)).start();
			} 
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		
	}

}


 

package com.net.scday1;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
/**
 * 负责处理每个线程通信的线程类
 * @author yulei
 *
 */
public class ServerThread  implements Runnable{
	//定义当前线程所处理的Socket
	Socket s=null;
	//该线程所处理Socket所对应的输入流
	BufferedReader br=null;
	public ServerThread(Socket s) throws IOException{
		this.s=s;	
		//初始化该Socket对应的输入流
		br=new BufferedReader(new InputStreamReader(s.getInputStream()));
	}
	public  void run() {
	try {
		String content=null;
		//采用循环不断从Socket中读取客户端发送过来的数据
		while((content=readFromClient())!=null){
			//遍历SocketList中的每个Socket
			//将读到的内容向每个Socket发送一次
			for(Socket s:MyServer.scoketList){
				PrintStream ps=new PrintStream(s.getOutputStream());
				ps.println(content);
			}
		}
		} catch (Exception e) {
		}
	}
	
	//定义读取客户端数据的方法
	private String readFromClient(){
		try {
			return br.readLine();
			//如果捕获到异常,表明该Socket对应的客户端已经关闭
		} catch (IOException e) {
			//删除该Socket
			MyServer.scoketList.remove(s);
		}
		return null;
	}
}


每个客户端应包含两条线程:一条负责读取用户的键盘输入,并将用户的输入数据写入Socket对应的输出流中;一条负责读取Socket对应输入流中的数据(从服务器发送过来的数据),并将这些数据打印输出。其中,负责读取用户键盘输入的线程由MyClinet负责,也就是由程序的主线程负责。客户端程序代码如下:

package com.net.scday1;

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

public class MyClient {
	
	public static void main(String[] args) throws Exception {
		Socket s=new Socket("127.0.0.1",30000);
		//客户端启动ClientThread线程不断读取来自服务器的数据
		new Thread(new ClientThread(s)).start();
		//获取该Socket对应的输出流
		PrintStream ps=new PrintStream(s.getOutputStream());
		String line=null;
		//不断读取键盘输入
		BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
		while((line=br.readLine())!=null){
			if(""==line||"".equals(line.trim())){
				System.out.println("不允许发送空数据!");
			}else{
			//将用户的键盘输入内容写入Socket对应的输出流
			ps.println(line);
		}
	 }
	}
}

package com.net.scday1;

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

public class ClientThread  implements Runnable{
	//该线程负责处理的Socket
	private Socket s;
	//该线程所处理的Socket所对应的输入流
	BufferedReader br=null;
	public  ClientThread(Socket s) throws IOException{
		this.s=s;
		br=new BufferedReader(new InputStreamReader(s.getInputStream()));
	}
	@Override
	public void run() {
	try {
		String content=null;
		//不断读取Socket输入流中的内容,并将这些内容打印输出
		while((content=br.readLine())!=null){
			System.out.println(content);
		}
		} catch (Exception e) {
		}
	}
}


 

 


 

 

 

posted on 2015-02-08 17:20  吴一达  阅读(237)  评论(0编辑  收藏  举报

导航