简单练习3( Day13 JAVA复习)

练习3:简单聊天室的实现

网络编程确实是学的比较差,线程也只能说学的一般,但是和网络编程写到一块就也搞不定了,只能先跟着老师完成,周末再去查资料一点一点复习

1. 需求及实现方法

实现聊天室
第一个任务:调整代码
1.客户端的socket声明为成员变量
2.在构造方法中初始化socket对象
3.定义start方法:完成读写功能
如果readLine读入的内容不为空,控制台输出
在main方法中创建对象,调用start方法
4.在服务器端定义ServerSocket对象
5.在构造方法中初始化
6.定义start方法,接受客户端请求,实现读写操作
如果readLine读入的内容不为空,控制台输出
在main方法中创建对象,调用start方法
第二个任务:实现控制台的一对一对话
1.客户端接受控制台输入
第三个任务:实现一个客户端和服务器端多次对话
1.服务器的if变while
2.添加while循环
第四个任务:实现多线程的服务器端
1.定义内部类,由线程实现读写
2.在start方法中,创建线程
第五个任务:实现群聊
在客户端使用线程接收服务器端的数据,是客户端的读写分开
服务器端
1.在服务器端定义数组,存放广播的输出流
private PrintWriter[] allOut;
构造方法初始化
2.在run方法中进行数组扩容,并群发信息
//将该客户端的输出流存入allOut

		allOut = Arrays.copyOf(allOut, allOut.length+1);
		allOut[allOut.length-1] = pw;
		
		while((line = br.readLine())!=null){
			System.out.println("客户端说:"+line);
			//回复所有客户端
			for(int i=0;i<allOut.length;i++){
				allOut[i].println("服务器说:"+line);
			}
			}

客户端
1.定义内部类
2.在start方法中调用
第六个任务:
通过窗口实现群聊
第七个任务:
1)把客户端和服务器端的输入输出流改为对象的输入输出流
ObjectInputStream和ObjectOutputStream
2)定义工具类

public class Info{
	//0表示群聊;1表示上线,2表示下线,3表示私聊
	private int type;
	private String name;
	private String sendInfo;

}

3)客户端发送信息为Info对象,服务器响应信息为Info对象
第八个任务:上线
添加登录的窗口,根据登录名称显示用户姓名
第九个任务:显示在线列表
1.在Info类中定义存放登录名的数组变量,添加set/get方法

2.在服务器类中定义存放登录名的数组,构造方法中初始化,
run方法中进行数组的扩容
3.客户端发送的type=1,响应对象把登录人的数组传递给响应对象放回给客户端

4.客户端登录成功,发送type=1的请求

5.控制台测试。。。。。

6.在窗口中添加在线列表组件,显示在线人的姓名

第十个任务:下线
1)添加窗口的关闭处理事件,在关闭窗口的事件处理方法中发送
type= 2的下线请求
2)服务器端代码调整,重新给数组赋值,数组中不包含下线的姓名
把数组返回给客户端
3)客户端的响应信息代码调整,参照上线处理

第十一个任务:单聊
1)在右侧选择单聊的对象,使用list的selected事件处理
//列表的选中事件,(此代码写下窗口事件的后边

 list.addListSelectionListener(new ListSelectionListener() {
	 public void valueChanged(ListSelectionEvent e) {
		//name表示单聊的那个对象的名字(name声明为成员变量)
		name = list.getSelectedValue();
	}
 });

2)在发送按钮的事件处理方法中进行处理(源代码需要重新调整)
判断name!=null,那么发送type=3的单聊请求
Info info = new Info(3,name,tfName.getText());
否则发送的是群聊。
参考代码如下:

@Override
public void actionPerformed(ActionEvent e) {
	Info info = null;
	if(name!=null){
		//私聊
		info = new Info(3,name,t.getText());
		//如果选择的是自己,则不做处理
		if(name.equals(title)){
			return;
		}
		//如果选择的是  在线列表,则表示群聊
		if(name.equals("在线列表                 ")){
			info = new Info(0,title,t.getText());
		}
	}else{
		//群聊
		info = new Info(0,title,t.getText());
	}
		
	try {
		oos.writeObject(info);
	} catch (IOException e1) {
		e1.printStackTrace();
	}
	System.out.println("写出完毕!");
	
}

3)在服务器端处理type=3的的请求处理
通过name找到单聊的客户端的输出流;
通过oos找到当前客户的名字
响应请求对象Info:
参考代码如下:

case 3:
	//找到对方的坐标,为了找到oos
	index = -1;
	for(int i = 0;i<setNames.length;i++){
		if(setNames[i].equals(inf.getName())){
			index = i;
			break;
		}
	}
	//通过oos找到我
	String name=null;
	for(int i = 0;i<allOut.length;i++){
		if(allOut[i]==oos){
 			name = setNames[i];
			break;
		}
	}
	Info inf3 = new Info(inf.getType(),name,inf.getSendInfo());
	
	ObjectOutputStream os[] = {allOut[index],oos};
	for(int i=0;i<os.length;i++){
		System.out.println(inf3);
		os[i].writeObject(inf3);
		os[i].flush();
	}
break;

4)客户端响应信息处理
同群聊处理
参考代码如下:

case 3:
	if(info.getName().equals(title)){
		area.append("我:"+info.getSendInfo()+"\n");
	}else{
		area.append(info.getName()+":"+info.getSendInfo()+"\n");
	}
break;

2. 逐步实现

先实现第一个任务也就是模板,之后逐渐往上修改添加功能模块

2.1 第一个任务:调整代码

1.客户端的socket声明为成员变量
2.在构造方法中初始化socket对象
3.定义start方法:完成读写功能
如果readLine读入的内容不为空,控制台输出
在main方法中创建对象,调用start方法
4.在服务器端定义ServerSocket对象
5.在构造方法中初始化
6.定义start方法,接受客户端请求,实现读写操作
如果readLine读入的内容不为空,控制台输出
在main方法中创建对象,调用start方法

public class Client {
	private Socket socket;
	public Client(){
		try {
			socket = new Socket("127.0.0.1",8888);
		}catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void start(){
		try {
			InputStream in = socket.getInputStream();
			OutputStream out = socket.getOutputStream();
			//写数据到服务器
			String str = "hello";
			PrintWriter pw = new PrintWriter(
					socket.getOutputStream());
			pw.println(str);
			pw.flush();
			
			//读服务器响应的信息
			BufferedReader br = 
					new BufferedReader(
							new InputStreamReader(
									socket.getInputStream()));
			
			String msg = br.readLine();
			if(msg!=null){
				System.out.println(msg);
			}
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		Client client = new Client();
		client.start();
	}
}
public class Server {
	private ServerSocket serverSocket;
	public Server(){
		try {
			serverSocket  = 
					new ServerSocket(8888);
			System.out.println("服务器初始化成功...");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void start(){
		try {
			System.out.println("等待连接....");
			Socket socket = serverSocket.accept();
			System.out.println("一个客户端连接了....");
			InputStream in = socket.getInputStream();
			OutputStream out = socket.getOutputStream();
			
			//读服务器响应的信息
			BufferedReader br = 
					new BufferedReader(
							new InputStreamReader(
									socket.getInputStream()));
			
			String msg = br.readLine();
			if(msg!=null){
				System.out.println("客户端:"+msg);
			}
			
			//写数据到服务器
			String str = "服务器:"+msg;
			PrintWriter pw = new PrintWriter(
					socket.getOutputStream());
			pw.println(str);
			pw.flush();
			
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		Server server = new Server();
		server.start();
	}
}

2.2 第二个任务:实现控制台的一对一对话

1.客户端接受控制台输入

public class Client {
	private Socket socket;
	public Client(){
		try {
			socket = new Socket("127.0.0.1",8888);
		}catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void start(){
		try {
			InputStream in = socket.getInputStream();
			OutputStream out = socket.getOutputStream();
			//写数据到服务器
			Scanner sc = new Scanner(System.in);
			System.out.println("请输入:");
			String str = sc.next();
			PrintWriter pw = new PrintWriter(
					socket.getOutputStream());
			pw.println(str);
			pw.flush();
			
			//读服务器响应的信息
			BufferedReader br = 
					new BufferedReader(
							new InputStreamReader(
									socket.getInputStream()));
			
			String msg = br.readLine();
			if(msg!=null){
				System.out.println(msg);
			}
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		Client client = new Client();
		client.start();
	}
}
public class Server {
	private ServerSocket serverSocket;
	
	public Server(){
		try {
			serverSocket  = 
					new ServerSocket(8888);
			System.out.println("服务器初始化成功...");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void start(){
		try {
			System.out.println("等待连接....");
			Socket socket = serverSocket.accept();
			System.out.println("一个客户端连接了....");
			InputStream in = socket.getInputStream();
			OutputStream out = socket.getOutputStream();
			
			//读服务器响应的信息
			BufferedReader br = 
					new BufferedReader(
							new InputStreamReader(
									socket.getInputStream()));
			
			String msg = br.readLine();
			if(msg!=null){
				System.out.println("客户端:"+msg);
			}
			
			//写数据到服务器
			String str = "服务器:"+msg;
			PrintWriter pw = new PrintWriter(
					socket.getOutputStream());
			pw.println(str);
			pw.flush();
			
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		Server server = new Server();
		server.start();
	}
}

2.3 第三个任务:实现一个客户端和服务器端多次对话

1.服务器的if变while
2.添加while循环

public class Client {
	private Socket socket;
	public Client(){
		try {
			socket = new Socket("127.0.0.1",8888);
		}catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void start(){
		try {
			InputStream in = socket.getInputStream();
			OutputStream out = socket.getOutputStream();
			//读服务器响应的信息
			BufferedReader br = 
					new BufferedReader(
							new InputStreamReader(
									socket.getInputStream()));
			
			PrintWriter pw = new PrintWriter(
					socket.getOutputStream());
			
			
			
			//写数据到服务器
			while(true){
				Scanner sc = new Scanner(System.in);
				System.out.println("请输入:");
				String str = sc.next();
				
				pw.println(str);
				pw.flush();
				
				String msg = br.readLine();
				if(msg!=null){
					System.out.println(msg);
				}
			}
			
			
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		Client client = new Client();
		client.start();
	}
}
public class Server {
	private ServerSocket serverSocket;
	public Server(){
		try {
			serverSocket  = 
					new ServerSocket(8888);
			System.out.println("服务器初始化成功...");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void start(){
		try {
			System.out.println("等待连接....");
			Socket socket = serverSocket.accept();
			System.out.println("一个客户端连接了....");
			InputStream in = socket.getInputStream();
			OutputStream out = socket.getOutputStream();
			
			//读服务器响应的信息
			BufferedReader br = 
					new BufferedReader(
							new InputStreamReader(
									socket.getInputStream()));
			PrintWriter pw = new PrintWriter(
					socket.getOutputStream());
			while(true){
				String msg = br.readLine();
				if(msg!=null){
					System.out.println("客户端:"+msg);
					//写数据到服务器
					String str = "服务器:"+msg;
					
					pw.println(str);
					pw.flush();
				}
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		Server server = new Server();
		server.start();
	}
}

2.4 第四个任务:实现多线程的服务器端

1.定义内部类,由线程实现读写
2.在start方法中,创建线程

public class Client {
	private Socket socket;
	public Client(){
		try {
			socket=new Socket("localhost",8888);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void start() throws Exception{
		InputStream in=socket.getInputStream();
		OutputStream out=socket.getOutputStream();
		//读服务器响应的消息
		BufferedReader br=new BufferedReader(
				new InputStreamReader(
						socket.getInputStream()));
		PrintWriter pw=new PrintWriter(
				socket.getOutputStream());
		//写数据到服务器
		while(true){
		Scanner sc=new Scanner(System.in);
		System.out.println("请输入:");
		String str=sc.next();
		
		pw.println(str);
		pw.flush();
		
		String msg=br.readLine();
		if(msg!=null){
			System.out.println(msg);
		}
		}
	} 
	public static void main(String[] args) throws Exception {
		Client client=new Client();
		client.start();
	}
}
public class Server {
	private ServerSocket serverSocket;
	public Server(){
		try {
			serverSocket  = 
					new ServerSocket(8888);
			System.out.println("服务器初始化成功...");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void start() throws IOException{
		//实现多个线程的服务器,和多个客户端对话
		while(true){
			System.out.println("等待连接....");
			Socket socket = serverSocket.accept();
			System.out.println("一个客户端连接了....");
			new Thread(new ClientHandler(socket)).start();
			}
	}
	private class ClientHandler implements Runnable{
		Socket socket;
		public ClientHandler(Socket socket){
			this.socket=socket;
		}
		@Override
		public void run() {
			try{
			InputStream in = socket.getInputStream();
			OutputStream out = socket.getOutputStream();
			//读服务器响应的信息
			BufferedReader br = 
					new BufferedReader(
							new InputStreamReader(
									socket.getInputStream()));
			PrintWriter pw = new PrintWriter(
					socket.getOutputStream());
			while(true){
				String msg = br.readLine();
				if(msg!=null){
					System.out.println("客户端:"+msg);
					//写数据到服务器
					String str = "服务器:"+msg;
					
					pw.println(str);
					pw.flush();
				}
			}
		}catch(Exception e){
			e.printStackTrace();} 
		}		
	}
	public static void main(String[] args) throws Exception {
		Server server = new Server();
		server.start();
	}
}

2.5 第五个任务:实现群聊

在客户端使用线程接收服务器端的数据,是客户端的读写分开
服务器端
1.在服务器端定义数组,存放广播的输出流
private PrintWriter[] allOut;
构造方法初始化
2.在run方法中进行数组扩容,并群发信息
//将该客户端的输出流存入allOut
allOut = Arrays.copyOf(allOut, allOut.length+1);
allOut[allOut.length-1] = pw;

		while((line = br.readLine())!=null){
			System.out.println("客户端说:"+line);
			//回复所有客户端
			for(int i=0;i<allOut.length;i++){
				allOut[i].println("服务器说:"+line);
			}
			}

客户端
1.定义内部类
2.在start方法中调用

public class Client {
	private Socket socket;
	public Client(){
		try {
			socket=new Socket(/*"10.8.41.112"*/"localhost",8888);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void start() throws Exception{
		new Thread(new ServerHandler()).start();
		/*InputStream in=socket.getInputStream();
		OutputStream out=socket.getOutputStream();
		//读服务器响应的消息
		BufferedReader br=new BufferedReader(
				new InputStreamReader(
						socket.getInputStream()));*/
		PrintWriter pw=new PrintWriter(
				socket.getOutputStream());
		//写数据到服务器
		while(true){
		Scanner sc=new Scanner(System.in);
		System.out.println("请输入:");
		String str=sc.next();
		
		pw.println(str);
		pw.flush();
		
	/*	String msg=br.readLine();
		if(msg!=null){
			System.out.println(msg);
		}*/
		}
	} 
	private class ServerHandler implements Runnable{

		@Override
		public void run() {
			try{
			//读服务器响应的信息
			BufferedReader br=new BufferedReader(
					new InputStreamReader(
							socket.getInputStream()));

			String msg="";
			while((msg=br.readLine())!=null){
				System.out.println(msg);
				}
		}catch(Exception e){
			e.printStackTrace();
			}
		}}
	public static void main(String[] args) throws Exception {
		Client client=new Client();
		client.start();
	}
}
public class Server {
	private ServerSocket serverSocket;
	//存放所有的客户端的输出流对象
	private PrintWriter[] allOut;
	
	public Server(){
		try {
			serverSocket  = 
					new ServerSocket(8888);
			//初始化,一开始客户端数量为0
			allOut=new PrintWriter[0];
			System.out.println("服务器初始化成功...");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void start() throws IOException{
		//实现多个线程的服务器,和多个客户端对话
		while(true){
			System.out.println("等待连接....");
			Socket socket = serverSocket.accept();
			System.out.println("一个客户端连接了....");
			new Thread(new ClientHandler(socket)).start();
			}
	}
	private class ClientHandler implements Runnable{
		Socket socket;
		public ClientHandler(Socket socket){
			this.socket=socket;
		}
		@Override
		public void run() {
			try{
			InputStream in = socket.getInputStream();
			OutputStream out = socket.getOutputStream();
			//读服务器响应的信息
			BufferedReader br = 
					new BufferedReader(
							new InputStreamReader(
									socket.getInputStream()));
			PrintWriter pw = new PrintWriter(
					socket.getOutputStream());
			//扩容数组
			allOut=Arrays.copyOf(allOut, allOut.length+1);
			allOut[allOut.length-1] = pw;
			
			while(true){
				String msg = br.readLine();
				if(msg!=null){
					System.out.println("客户端说:"+msg);
					//写数据到服务器
					String str = "服务器说:"+msg;
					//群聊,遍历数组输出
					for(PrintWriter p:allOut){
					p.println(str);
					p.flush();
					}
				}
			}
		}catch(Exception e){
			e.printStackTrace();} 
		}		
	}
	public static void main(String[] args) throws Exception {
		Server server = new Server();
		server.start();
	}
}

2.6 第六个任务:通过窗口实现群聊

//JFrame窗口
public class Client extends JFrame{
	//声明对象并创建组件
	private JTextArea ta=new JTextArea();
	private JTextField tf=new JTextField(18);
	private JButton send=new JButton("发送");
	
	
	
	private Socket socket;
	PrintWriter pw;
	public Client(){
		//把组件添加到容器,默认是边界布局
		this.add(ta,BorderLayout.CENTER);
		//默认布局流式布局
		JPanel ps=new JPanel();
		ps.add(tf);
		ps.add(send);
		
		this.add(ps, BorderLayout.SOUTH);
		
		//设置窗口的属性
		this.setTitle("聊天室");
		this.setSize(300,400);//设置窗口大小
		this.setLocationRelativeTo(null);//窗口在屏幕中央显示
		
		this.setVisible(true);//显示窗口
		
		
		try {
			socket=new Socket(/*"10.8.41.112"*/"localhost",8888);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		
		
		//发送按钮事件
		send.addActionListener(
				new ActionListener(){
					@Override
					public void actionPerformed(ActionEvent e) {
						//获取文本框的内容
						pw.println(tf.getText());
						pw.flush();
						
					}
					
				});
	}
	public void start() throws Exception{
		new Thread(new ServerHandler()).start();
		/*InputStream in=socket.getInputStream();
		OutputStream out=socket.getOutputStream();
		//读服务器响应的消息
		BufferedReader br=new BufferedReader(
				new InputStreamReader(
						socket.getInputStream()));*/
		 pw=new PrintWriter(
				socket.getOutputStream());
		//写数据到服务器
		/*while(true){
		Scanner sc=new Scanner(System.in);
		System.out.println("请输入:");
		String str=sc.next();
		
		pw.println(str);
		pw.flush();
		
	/*	String msg=br.readLine();
		if(msg!=null){
			System.out.println(msg);
		}*/
		//}
	} 
	private class ServerHandler implements Runnable{

		@Override
		public void run() {
			try{
			//读服务器响应的信息
			BufferedReader br=new BufferedReader(
					new InputStreamReader(
							socket.getInputStream()));

			String msg="";
			while((msg=br.readLine())!=null){
				//System.out.println(msg);
				ta.append(msg+"\n");
				}
		}catch(Exception e){
			e.printStackTrace();
			}
		}}
	public static void main(String[] args) throws Exception {
		Client client=new Client();
		client.start();
	}
}
public class Server {
	private ServerSocket serverSocket;
	//存放所有的客户端的输出流对象
	private PrintWriter[] allOut;
	
	public Server(){
		try {
			serverSocket  = 
					new ServerSocket(8888);
			//初始化,一开始客户端数量为0
			allOut=new PrintWriter[0];
			System.out.println("服务器初始化成功...");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void start() throws IOException{
		//实现多个线程的服务器,和多个客户端对话
		while(true){
			System.out.println("等待连接....");
			Socket socket = serverSocket.accept();
			System.out.println("一个客户端连接了....");
			new Thread(new ClientHandler(socket)).start();
			}
	}
	private class ClientHandler implements Runnable{
		Socket socket;
		public ClientHandler(Socket socket){
			this.socket=socket;
		}
		@Override
		public void run() {
			try{
			InputStream in = socket.getInputStream();
			OutputStream out = socket.getOutputStream();
			//读服务器响应的信息
			BufferedReader br = 
					new BufferedReader(
							new InputStreamReader(
									socket.getInputStream()));
			PrintWriter pw = new PrintWriter(
					socket.getOutputStream());
			//扩容数组
			allOut=Arrays.copyOf(allOut, allOut.length+1);
			allOut[allOut.length-1] = pw;
			
			while(true){
				String msg = br.readLine();
				if(msg!=null){
					System.out.println("客户端说:"+msg);
					//写数据到服务器
					String str = "服务器说:"+msg;
					//群聊,遍历数组输出
					for(PrintWriter p:allOut){
					p.println(str);
					p.flush();
					}
				}
			}
		}catch(Exception e){
			e.printStackTrace();} 
		}		
	}
	public static void main(String[] args) throws Exception {
		Server server = new Server();
		server.start();
	}
}

2.7 第七个任务:

1)把客户端和服务器端的输入输出流改为对象的输入输出流
ObjectInputStream和ObjectOutputStream
2)定义工具类
public class Info{
//0表示群聊;1表示上线,2表示下线,3表示私聊
private int type;
private String name;
private String sendInfo;
}
3)客户端发送信息为Info对象,服务器响应信息为Info对象

public class Info implements Serializable{
	private static final long serialVersionUID = -6493271283072240239L;
	//0表示群聊;1表示上线;2表示下线;3表示私聊
	private int type;
	private String name;
	private String sendInfo;
	public Info() {
		
	}
	
	public Info(int type,String name, String sendInfo ) {
		super();
		this.type = type;
		
		this.name = name;
		this.sendInfo = sendInfo;
	}
	
	public int getType() {
		return type;
	}
	public void setType(int type) {
		this.type = type;
	}
	public String getSendInfo() {
		return sendInfo;
	}
	public void setSendInfo(String sendInfo) {
		this.sendInfo = sendInfo;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "Info [type=" + type + ", name=" + name + ", sendInfo=" + sendInfo + "]";
	}
}
/JFrame窗口
public class Client extends JFrame{
	//声明对象并创建组件
	private JTextArea ta=new JTextArea();
	private JTextField tf=new JTextField(18);
	private JButton send=new JButton("发送");
	private Socket socket;
	//PrintWriter pw;
	ObjectOutputStream oos;
	public Client(){
		//把组件添加到容器,默认是边界布局
		this.add(ta,BorderLayout.CENTER);
		//默认布局流式布局
		JPanel ps=new JPanel();
		ps.add(tf);
		ps.add(send);
		
		this.add(ps, BorderLayout.SOUTH);
		
		//设置窗口的属性
		this.setTitle("聊天室");
		this.setSize(300,400);//设置窗口大小
		this.setLocationRelativeTo(null);//窗口在屏幕中央显示
		
		this.setVisible(true);//显示窗口
		
		
		try {
			socket=new Socket(/*"10.8.41.112"*/"localhost",8888);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		
		
		//发送按钮事件
		send.addActionListener(
				new ActionListener(){
					@Override
					public void actionPerformed(ActionEvent e) {
						//获取文本框的内容
						Info info=new Info(0,"xx",tf.getText());
						try {
							oos.writeObject(info);
							oos.flush();
						} catch (IOException e1) {
							// TODO Auto-generated catch block
							e1.printStackTrace();
						}						
					}					
				});
	}
	public void start() throws Exception{
		new Thread(new ServerHandler()).start();
		//创建对象输出流对象
		 oos=new ObjectOutputStream(
				socket.getOutputStream());
	} 
	private class ServerHandler implements Runnable{

		@Override
		public void run() {
			try{
			//读服务器响应的信息
			ObjectInputStream ois=new ObjectInputStream(
							socket.getInputStream());
			Object obj="";
			while((obj=ois.readObject())!=null){
				//System.out.println(msg);
				Info info=(Info) obj;
				switch(info.getType()){
				case 0://显示群聊
					ta.append(info.getName()+":"+info.getSendInfo()+"\n");
					break;
				}
			}
		}catch(Exception e){
			e.printStackTrace();
			}
		}}
	public static void main(String[] args) throws Exception {
		Client client=new Client();
		client.start();
	}
}
public class Server {
	private ServerSocket serverSocket;
	//存放所有的客户端的输出流对象
	private ObjectOutputStream[] allOut;
	
	public Server(){
		try {
			serverSocket  = 
					new ServerSocket(8888);
			//初始化,一开始客户端数量为0
			allOut=new ObjectOutputStream[0];
			System.out.println("服务器初始化成功...");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void start() throws IOException{
		//实现多个线程的服务器,和多个客户端对话
		while(true){
			System.out.println("等待连接....");
			Socket socket = serverSocket.accept();
			System.out.println("一个客户端连接了....");
			new Thread(new ClientHandler(socket)).start();
			}
	}
	private class ClientHandler implements Runnable{
		Socket socket;
		public ClientHandler(Socket socket){
			this.socket=socket;
		}
		@Override
		public void run() {
			try{
			InputStream in = socket.getInputStream();
			OutputStream out = socket.getOutputStream();
			//读服务器响应的信息
			ObjectInputStream br = 
					new ObjectInputStream(
									socket.getInputStream());
			ObjectOutputStream oos = new ObjectOutputStream(
					socket.getOutputStream());
			//扩容数组
			allOut=Arrays.copyOf(allOut, allOut.length+1);
			allOut[allOut.length-1] = oos;
			
			while(true){
				Object obj = br.readObject();
				if(obj!=null){
					System.out.println("客户端说:"+obj);
					Info info=(Info) obj;
					switch(info.getType()){
					case 0:	
						//群聊
						for(ObjectOutputStream p:allOut){
						p.writeObject(info);
						p.flush();
						}
					break;
					}
				}
			}
		}catch(Exception e){
			e.printStackTrace();} 
		}		
	}
	public static void main(String[] args) throws Exception {
		Server server = new Server();
		server.start();
	}
}

2.8 第八个任务:上线

添加登录的窗口,根据登录名称显示用户姓名

public class Info implements Serializable{
	private static final long serialVersionUID = -6493271283072240239L;
	//0表示群聊;1表示上线;2表示下线;3表示私聊
	private int type;
	private String name;
	private String sendInfo;
	public Info() {
		
	}
	
	public Info(int type,String name, String sendInfo ) {
		super();
		this.type = type;
		
		this.name = name;
		this.sendInfo = sendInfo;
	}
	
	public int getType() {
		return type;
	}
	public void setType(int type) {
		this.type = type;
	}
	public String getSendInfo() {
		return sendInfo;
	}
	public void setSendInfo(String sendInfo) {
		this.sendInfo = sendInfo;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	@Override
	public String toString() {
		return "Info [type=" + type + ", name=" + name + ", sendInfo=" + sendInfo + "]";
	}	
}
public class Login extends JFrame{
	//声明创建对象
	private JLabel labelName=new JLabel("姓名:");
	private JLabel labelPwd=new JLabel("密码:");
	
	private JTextField tfName=new JTextField(16);
	private JTextField tfPwd=new JTextField(16);
	
	private JButton btn=new JButton("登录");
	public Login(){
		//设置窗口布局为流式布局
		this.setLayout(new FlowLayout());
		//添加组件到窗口
		this.add(labelName);
		this.add(tfName);
		this.add(labelPwd);
		this.add(tfPwd);
		this.add(btn);
		//设置窗口属性
		this.setTitle("登录窗口");
		this.setSize(260,150);//设置窗口大小
		this.setLocationRelativeTo(null);//窗口在屏幕中央显示
		this.setVisible(true);//显示窗口
		
		btn.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent e) {
				//当前窗口关闭
				dispose();
				//显示聊天窗口,窗口的标题为登录人的名称
				Client client=new Client(tfName.getText());
				try {
					client.start();
				} catch (Exception e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		});
	}
	public static void main(String[] args) {
		new Login();
	}
}
//JFrame窗口
public class Client extends JFrame{
	//声明对象并创建组件
	private JTextArea ta=new JTextArea();
	private JTextField tf=new JTextField(18);
	private JButton send=new JButton("发送");
	
	
	
	private Socket socket;
	//PrintWriter pw;
	ObjectOutputStream oos;
	String title;
	public Client(String title){
		this.title=title;
		//把组件添加到容器,默认是边界布局
		this.add(ta,BorderLayout.CENTER);
		//默认布局流式布局
		JPanel ps=new JPanel();
		ps.add(tf);
		ps.add(send);
		
		this.add(ps, BorderLayout.SOUTH);
		
		//设置窗口的属性
		this.setTitle(title);
		this.setSize(300,400);//设置窗口大小
		this.setLocationRelativeTo(null);//窗口在屏幕中央显示
		this.setVisible(true);//显示窗口
		try {
			socket=new Socket(/*"10.8.41.112"*/"localhost",8888);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
		//发送按钮事件
		send.addActionListener(
				new ActionListener(){
					@Override
					public void actionPerformed(ActionEvent e) {
						//获取文本框的内容
						Info info=new Info(0,title,tf.getText());
						try {
							oos.writeObject(info);
							oos.flush();
						} catch (IOException e1) {
							// TODO Auto-generated catch block
							e1.printStackTrace();
						}						
					}					
				});
	}
	public void start() throws Exception{
		new Thread(new ServerHandler()).start();
		//创建对象输出流对象
		 oos=new ObjectOutputStream(
				socket.getOutputStream());
	} 
	private class ServerHandler implements Runnable{

		@Override
		public void run() {
			try{
			//读服务器响应的信息
			ObjectInputStream ois=new ObjectInputStream(
							socket.getInputStream());
			Object obj="";
			while((obj=ois.readObject())!=null){
				//System.out.println(msg);
				Info info=(Info) obj;
				switch(info.getType()){
				case 0://显示群聊
					if(info.getName().equals(title)){
						ta.append("我:"+info.getSendInfo()+"\n");
					}else{
						ta.append(info.getName()+":"+info.getSendInfo()+"\n");
					}
					
					break;
				}
			}
		}catch(Exception e){
			e.printStackTrace();
			}
		}}
}
public class Server {
	private ServerSocket serverSocket;
	//存放所有的客户端的输出流对象
	private ObjectOutputStream[] allOut;
	
	public Server(){
		try {
			serverSocket  = 
					new ServerSocket(8888);
			//初始化,一开始客户端数量为0
			allOut=new ObjectOutputStream[0];
			System.out.println("服务器初始化成功...");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void start() throws IOException{
		//实现多个线程的服务器,和多个客户端对话
		while(true){
			System.out.println("等待连接....");
			Socket socket = serverSocket.accept();
			System.out.println("一个客户端连接了....");
			new Thread(new ClientHandler(socket)).start();
			}
	}
	private class ClientHandler implements Runnable{
		Socket socket;
		public ClientHandler(Socket socket){
			this.socket=socket;
		}
		@Override
		public void run() {
			try{
			InputStream in = socket.getInputStream();
			OutputStream out = socket.getOutputStream();
			//读服务器响应的信息
			ObjectInputStream br = 
					new ObjectInputStream(
									socket.getInputStream());
			ObjectOutputStream oos = new ObjectOutputStream(
					socket.getOutputStream());
			//扩容数组
			allOut=Arrays.copyOf(allOut, allOut.length+1);
			allOut[allOut.length-1] = oos;
			
			while(true){
				Object obj = br.readObject();
				if(obj!=null){
					System.out.println("客户端说:"+obj);
					Info info=(Info) obj;
					switch(info.getType()){
					case 0:	
						//群聊
						for(ObjectOutputStream p:allOut){
						p.writeObject(info);
						p.flush();
						}
					break;
					}
				}
			}
		}catch(Exception e){
			e.printStackTrace();} 
		}		
	}
	public static void main(String[] args) throws Exception {
		Server server = new Server();
		server.start();
	}
}

2.9 第九个任务:显示在线列表

1.在Info类中定义存放登录名的数组变量,添加set/get方法
2.在服务器类中定义存放登录名的数组,构造方法中初始化,
run方法中进行数组的扩容
3.客户端发送的type=1,响应对象把登录人的数组传递给响应对象放回给客户端
4.客户端登录成功,发送type=1的请求
5.控制台测试。。。。。
6.在窗口中添加在线列表组件,显示在线人的姓名

public class Info implements Serializable{
	private static final long serialVersionUID = -6493271283072240239L;
	//0表示群聊;1表示上线;2表示下线;3表示私聊
	private int type;
	private String name;
	private String sendInfo;
	//在线列表的数组
	private String[] onlines;
	public Info() {
	}
	
	public Info(int type,String name, String sendInfo ) {
		super();
		this.type = type;
		
		this.name = name;
		this.sendInfo = sendInfo;
	}
	
	public int getType() {
		return type;
	}
	public void setType(int type) {
		this.type = type;
	}
	public String getSendInfo() {
		return sendInfo;
	}
	public void setSendInfo(String sendInfo) {
		this.sendInfo = sendInfo;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String[] getOnlines() {
		return onlines;
	}

	public void setOnlines(String[] onlines) {
		this.onlines = onlines;
	}
	@Override
	public String toString() {
		return "Info [type=" + type + ", name=" + name + ", sendInfo=" + sendInfo + ", onlines="
				+ Arrays.toString(onlines) + "]";
	}
}
public class Login extends JFrame{
	//声明创建对象
	private JLabel labelName=new JLabel("姓名:");
	private JLabel labelPwd=new JLabel("密码:");
	
	private JTextField tfName=new JTextField(16);
	private JTextField tfPwd=new JTextField(16);
	
	private JButton btn=new JButton("登录");
	public Login(){
		//设置窗口布局为流式布局
		this.setLayout(new FlowLayout());
		//添加组件到窗口
		this.add(labelName);
		this.add(tfName);
		this.add(labelPwd);
		this.add(tfPwd);
		this.add(btn);
		//设置窗口属性
		this.setTitle("登录窗口");
		this.setSize(260,150);//设置窗口大小
		this.setLocationRelativeTo(null);//窗口在屏幕中央显示
		this.setVisible(true);//显示窗口
		
		btn.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent e) {
				//当前窗口关闭
				dispose();
				//显示聊天窗口,窗口的标题为登录人的名称
				Client client=new Client(tfName.getText());
				try {
					client.start();
				} catch (Exception e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				
			}
			
		});
	}

	public static void main(String[] args) {
		new Login();
	}

}
//JFrame窗口
public class Client extends JFrame{
	//声明对象并创建组件
	private JTextArea ta=new JTextArea();
	private JTextField tf=new JTextField(18);
	private JButton send=new JButton("发送");
	
	//显示在线列表的组件
	private JList<String> list=new JList<String>();
	private DefaultListModel<String> model=new DefaultListModel<>();
	
	private Socket socket;
	//PrintWriter pw;
	ObjectOutputStream oos;
	String title;
	public Client(String title){
		this.title=title;
		//把组件添加到容器,默认是边界布局
		this.add(ta,BorderLayout.CENTER);
		//默认布局流式布局
		JPanel ps=new JPanel();
		ps.add(tf);
		ps.add(send);
		
		this.add(ps, BorderLayout.SOUTH);
		
		//添加在线列表的组件
		list.setModel(model);
		this.add(list,BorderLayout.EAST);
		
		//设置窗口的属性
		this.setTitle(title);
		this.setSize(300,400);//设置窗口大小
		this.setLocationRelativeTo(null);//窗口在屏幕中央显示
		this.setVisible(true);//显示窗口
		try {
			socket=new Socket(/*"10.8.41.112"*/"localhost",8888);
			//创建对象输出流对象
			 oos=new ObjectOutputStream(
					socket.getOutputStream());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
		//发送上线的请求
		Info info=new Info(1,title,"上线了");
		try {
			oos.writeObject(info);
			oos.flush();
		} catch (IOException e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
		}
	
		//发送按钮事件
		send.addActionListener(
				new ActionListener(){
					@Override
					public void actionPerformed(ActionEvent e) {
						//获取文本框的内容
						Info info=new Info(0,title,tf.getText());
						try {
							oos.writeObject(info);
							oos.flush();
						} catch (IOException e1) {
							// TODO Auto-generated catch block
							e1.printStackTrace();
						}						
					}					
				});
	}
	public void start(){
		new Thread(new ServerHandler()).start();
	} 
	private class ServerHandler implements Runnable{

		@Override
		public void run() {
			try{
			//读服务器响应的信息
			ObjectInputStream ois=new ObjectInputStream(
							socket.getInputStream());
			Object obj="";
			while((obj=ois.readObject())!=null){
				//System.out.println(msg);
				Info info=(Info) obj;
				switch(info.getType()){
				case 0://显示群聊
					if(info.getName().equals(title)){
						ta.append("我:"+info.getSendInfo()+"\n");
					}else{
						ta.append(info.getName()+":"+info.getSendInfo()+"\n");
					}
					break;
				case 1:
					if(info.getName().equals(title)){
						ta.append("我:"+info.getSendInfo()+"\n");
					}else{
						ta.append(info.getName()+":"+info.getSendInfo()+"\n");
					}
					model.clear();
					model.addElement("在线列表                        ");
					String[] onlines=info.getOnlines();
					for(String name:onlines){
						model.addElement(name);
					}
					break;
				}
			}
		}catch(Exception e){
			e.printStackTrace();
			}
		}}
}
public class Server {
	private ServerSocket serverSocket;
	//存放所有的客户端的输出流对象
	private ObjectOutputStream[] allOut;
	//定义在线的列表数组
	private String[] onlines;
	
	public Server(){
		try {
			serverSocket  = 
					new ServerSocket(8888);
			//初始化,一开始客户端数量为0
			allOut=new ObjectOutputStream[0];
			onlines=new String[0];
			System.out.println("服务器初始化成功...");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void start() throws IOException{
		//实现多个线程的服务器,和多个客户端对话
		while(true){
			System.out.println("等待连接....");
			Socket socket = serverSocket.accept();
			System.out.println("一个客户端连接了....");
			new Thread(new ClientHandler(socket)).start();
			}
	}
	private class ClientHandler implements Runnable{
		Socket socket;
		public ClientHandler(Socket socket){
			this.socket=socket;
		}
		@Override
		public void run() {
			try{
			InputStream in = socket.getInputStream();
			OutputStream out = socket.getOutputStream();
			//读服务器响应的信息
			ObjectInputStream br = 
					new ObjectInputStream(
									socket.getInputStream());
			ObjectOutputStream oos = new ObjectOutputStream(
					socket.getOutputStream());
			//扩容数组
			allOut=Arrays.copyOf(allOut, allOut.length+1);
			allOut[allOut.length-1] = oos;
			
			
			
			while(true){
				Object obj = br.readObject();
				if(obj!=null){
					System.out.println("客户端说:"+obj);
					Info info=(Info) obj;
					onlines =Arrays.copyOf(onlines, onlines.length+1);
					onlines[onlines.length-1]=info.getName();
					switch(info.getType()){
					case 0:
						//群聊
						sendAllMsg(info);
						break;
					case 1:
						//info对象中包含信息
						info.setOnlines(onlines);
						sendAllMsg(info);
						break;
					}
				}
			}
		}catch(Exception e){
			e.printStackTrace();} 
		}		
	}
	private void sendAllMsg(Info info) throws Exception{
		//群聊
		for(ObjectOutputStream p:allOut){
		p.writeObject(info);
		p.flush();}
	}
	public static void main(String[] args) throws Exception {
		Server server = new Server();
		server.start();
	}
}

2.10 第十个任务:下线

1)添加窗口的关闭处理事件,在关闭窗口的事件处理方法中发送
type= 2的下线请求
2)服务器端代码调整,重新给数组赋值,数组中不包含下线的姓名
把数组返回给客户端
3)客户端的响应信息代码调整,参照上线处理

public class Info implements Serializable{
	private static final long serialVersionUID = -6493271283072240239L;
	//0表示群聊;1表示上线;2表示下线;3表示私聊
	private int type;
	private String name;
	private String sendInfo;
	//在线列表的数组
	private String[] onlines;
	public Info() {
	}
	
	public Info(int type,String name, String sendInfo ) {
		super();
		this.type = type;
		
		this.name = name;
		this.sendInfo = sendInfo;
	}
	
	public int getType() {
		return type;
	}
	public void setType(int type) {
		this.type = type;
	}
	public String getSendInfo() {
		return sendInfo;
	}
	public void setSendInfo(String sendInfo) {
		this.sendInfo = sendInfo;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String[] getOnlines() {
		return onlines;
	}

	public void setOnlines(String[] onlines) {
		this.onlines = onlines;
	}
	@Override
	public String toString() {
		return "Info [type=" + type + ", name=" + name + ", sendInfo=" + sendInfo + ", onlines="
				+ Arrays.toString(onlines) + "]";
	}	
}
public class Login extends JFrame{
	//声明创建对象
	private JLabel labelName=new JLabel("姓名:");
	private JLabel labelPwd=new JLabel("密码:");
	
	private JTextField tfName=new JTextField(16);
	private JTextField tfPwd=new JTextField(16);
	
	private JButton btn=new JButton("登录");
	public Login(){
		//设置窗口布局为流式布局
		this.setLayout(new FlowLayout());
		//添加组件到窗口
		this.add(labelName);
		this.add(tfName);
		this.add(labelPwd);
		this.add(tfPwd);
		this.add(btn);
		//设置窗口属性
		this.setTitle("登录窗口");
		this.setSize(260,150);//设置窗口大小
		this.setLocationRelativeTo(null);//窗口在屏幕中央显示
		this.setVisible(true);//显示窗口
		
		btn.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent e) {
				//当前窗口关闭
				dispose();
				//显示聊天窗口,窗口的标题为登录人的名称
				Client client=new Client(tfName.getText());
				try {
					client.start();
				} catch (Exception e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		});
	}
	public static void main(String[] args) {
		new Login();
	}
}
//JFrame窗口
public class Client extends JFrame{
	//声明对象并创建组件
	private JTextArea ta=new JTextArea();
	private JTextField tf=new JTextField(18);
	private JButton send=new JButton("发送");
	
	//显示在线列表的组件
	private JList<String> list=new JList<String>();
	private DefaultListModel<String> model=new DefaultListModel<>();
	
	private Socket socket;
	//PrintWriter pw;
	ObjectOutputStream oos;
	String title;
	public Client(String title){
		this.title=title;
		//把组件添加到容器,默认是边界布局
		this.add(ta,BorderLayout.CENTER);
		//默认布局流式布局
		JPanel ps=new JPanel();
		ps.add(tf);
		ps.add(send);
		
		this.add(ps, BorderLayout.SOUTH);
		
		//添加在线列表的组件
		list.setModel(model);
		this.add(list,BorderLayout.EAST);
		
		//设置窗口的属性
		this.setTitle(title);
		this.setSize(300,400);//设置窗口大小
		this.setLocationRelativeTo(null);//窗口在屏幕中央显示
		this.setVisible(true);//显示窗口
		try {
			socket=new Socket(/*"10.8.41.112"*/"localhost",8888);
			//创建对象输出流对象
			 oos=new ObjectOutputStream(
					socket.getOutputStream());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
		//发送上线的请求
		Info info=new Info(1,title,"上线了");
		try {
			oos.writeObject(info);
			oos.flush();
		} catch (IOException e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
		}
	
		//发送按钮事件
		send.addActionListener(
				new ActionListener(){
					@Override
					public void actionPerformed(ActionEvent e) {
						//获取文本框的内容
						Info info=new Info(0,title,tf.getText());
						try {
							oos.writeObject(info);
							oos.flush();
						} catch (IOException e1) {
							// TODO Auto-generated catch block
							e1.printStackTrace();
						}						
					}					
				});
		//关闭窗口事件
		this.addWindowListener(new WindowAdapter(){
			@Override
			public void windowClosing(WindowEvent e) {
				//1.向服务器发送type=2的请求(下线)
				Info info=new Info(2,title,"下线了");
				try {
					oos.writeObject(info);
					oos.flush();
				} catch (IOException e2) {
					// TODO Auto-generated catch block
					e2.printStackTrace();
				}
				//2.关闭当前窗口
				dispose();
			}
		});
	}
	public void start(){
		new Thread(new ServerHandler()).start();
	} 
	private class ServerHandler implements Runnable{

		@Override
		public void run() {
			try{
			//读服务器响应的信息
			ObjectInputStream ois=new ObjectInputStream(
							socket.getInputStream());
			Object obj="";
			while((obj=ois.readObject())!=null){
				//System.out.println(msg);
				Info info=(Info) obj;
				switch(info.getType()){
				case 0://显示群聊
					if(info.getName().equals(title)){
						ta.append("我:"+info.getSendInfo()+"\n");
					}else{
						ta.append(info.getName()+":"+info.getSendInfo()+"\n");
					}
					break;
				case 1:
					if(info.getName().equals(title)){
						ta.append("我:"+info.getSendInfo()+"\n");
					}else{
						ta.append(info.getName()+":"+info.getSendInfo()+"\n");
					}
					model.clear();
					model.addElement("在线列表                        ");
					String[] onlines=info.getOnlines();
					for(String name:onlines){
						//System.out.println(name+",");
						model.addElement(name);
					}
					break;
				case 2:
					ta.append(info.getName()+":"+info.getSendInfo()+"\n");
					model.clear();
					model.addElement("在线列表                        ");
							onlines=info.getOnlines();
					String[] onlines2=info.getOnlines();
					for(String name:onlines2){
						//System.out.println(name+",");
						model.addElement(name);
					}
					break;
				}
			}
		}catch(Exception e){
			e.printStackTrace();
			}
		}}
}
public class Server {
	private ServerSocket serverSocket;
	//存放所有的客户端的输出流对象
	private ObjectOutputStream[] allOut;
	//定义在线的列表数组
	private String[] onlines;
	
	public Server(){
		try {
			serverSocket  = 
					new ServerSocket(8888);
			//初始化,一开始客户端数量为0
			allOut=new ObjectOutputStream[0];
			onlines=new String[0];
			System.out.println("服务器初始化成功...");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void start() throws IOException{
		//实现多个线程的服务器,和多个客户端对话
		while(true){
			System.out.println("等待连接....");
			Socket socket = serverSocket.accept();
			System.out.println("一个客户端连接了....");
			new Thread(new ClientHandler(socket)).start();
			}
	}
	private class ClientHandler implements Runnable{
		Socket socket;
		public ClientHandler(Socket socket){
			this.socket=socket;
		}
		@Override
		public void run() {
			try{
			InputStream in = socket.getInputStream();
			OutputStream out = socket.getOutputStream();
			//读服务器响应的信息
			ObjectInputStream br = 
					new ObjectInputStream(
									socket.getInputStream());
			ObjectOutputStream oos = new ObjectOutputStream(
					socket.getOutputStream());
			//扩容数组
			allOut=Arrays.copyOf(allOut, allOut.length+1);
			allOut[allOut.length-1] = oos;
			
			
			
			while(true){
				Object obj = br.readObject();
				if(obj!=null){
					System.out.println("客户端说:"+obj);
					Info info=(Info) obj;
					switch(info.getType()){
					case 0:
						//群聊
						sendAllMsg(info);
						break;
					case 1:
						onlines =Arrays.copyOf(onlines, onlines.length+1);
						onlines[onlines.length-1]=info.getName();
						//info对象中包含信息
						info.setOnlines(onlines);
						sendAllMsg(info);
						break;
					case 2:
						//缩容 onlines  allOut
						String[] newOnlines=new String[onlines.length-1];
						int index=0;
						for(String str:onlines){
							if(!str.equals(info.getName())){
								newOnlines[index++]=str;
							}
						}
						onlines=newOnlines;
						ObjectOutputStream[] newAllOut=new ObjectOutputStream[allOut.length-1];
						index=0;
						for(ObjectOutputStream output:allOut){
							if(output!=oos){
							newAllOut[index++]=output;}
						}
						allOut=newAllOut;
						//info对象中包含信息
						info.setOnlines(onlines);
						sendAllMsg(info);
						break;
					}
				}
			}
		}catch(Exception e){
			e.printStackTrace();} 
		}		
	}
	private void sendAllMsg(Info info) throws Exception{
		//群聊
		for(ObjectOutputStream p:allOut){
		p.writeObject(info);
		p.flush();}
	}
	public static void main(String[] args) throws Exception {
		Server server = new Server();
		server.start();
	}
}

2.11 第十一个任务:单聊

1)在右侧选择单聊的对象,使用list的selected事件处理
//列表的选中事件,(此代码写下窗口事件的后边)
2)在发送按钮的事件处理方法中进行处理(源代码需要重新调整)
判断name!=null,那么发送type=3的单聊请求
Info info = new Info(3,name,tfName.getText());
否则发送的是群聊。
3)在服务器端处理type=3的的请求处理
通过name找到单聊的客户端的输出流;
通过oos找到当前客户的名字
响应请求对象Info:
4)客户端响应信息处理同群聊处理

public class Info implements Serializable{
	private static final long serialVersionUID = -6493271283072240239L;
	//0表示群聊;1表示上线;2表示下线;3表示私聊
	private int type;
	private String name;
	private String sendInfo;
	//在线列表的数组
	private String[] onlines;
	public Info() {
	}
	
	public Info(int type,String name, String sendInfo ) {
		super();
		this.type = type;
		
		this.name = name;
		this.sendInfo = sendInfo;
	}
	
	public int getType() {
		return type;
	}
	public void setType(int type) {
		this.type = type;
	}
	public String getSendInfo() {
		return sendInfo;
	}
	public void setSendInfo(String sendInfo) {
		this.sendInfo = sendInfo;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String[] getOnlines() {
		return onlines;
	}

	public void setOnlines(String[] onlines) {
		this.onlines = onlines;
	}
	@Override
	public String toString() {
		return "Info [type=" + type + ", name=" + name + ", sendInfo=" + sendInfo + ", onlines="
				+ Arrays.toString(onlines) + "]";
	}
}
public class Login extends JFrame{
	//声明创建对象
	private JLabel labelName=new JLabel("姓名:");
	private JLabel labelPwd=new JLabel("密码:");
	
	private JTextField tfName=new JTextField(16);
	private JTextField tfPwd=new JTextField(16);
	
	private JButton btn=new JButton("登录");
	public Login(){
		//设置窗口布局为流式布局
		this.setLayout(new FlowLayout());
		//添加组件到窗口
		this.add(labelName);
		this.add(tfName);
		this.add(labelPwd);
		this.add(tfPwd);
		this.add(btn);
		//设置窗口属性
		this.setTitle("登录窗口");
		this.setSize(260,150);//设置窗口大小
		this.setLocationRelativeTo(null);//窗口在屏幕中央显示
		this.setVisible(true);//显示窗口
		
		btn.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent e) {
				//当前窗口关闭
				dispose();
				//显示聊天窗口,窗口的标题为登录人的名称
				Client client=new Client(tfName.getText());
				try {
					client.start();
				} catch (Exception e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
			}
		});
	}
	public static void main(String[] args) {
		new Login();
	}
}
//JFrame窗口
public class Client extends JFrame{
	//声明对象并创建组件
	private JTextArea ta=new JTextArea();
	private JTextField tf=new JTextField(18);
	private JButton send=new JButton("发送");
	
	//显示在线列表的组件
	private JList<String> list=new JList<String>();
	private DefaultListModel<String> model=new DefaultListModel<>();
	
	private Socket socket;
	//PrintWriter pw;
	private ObjectOutputStream oos;
	private String title;
	private String name;
	public Client(String title){
		this.title=title;
		//把组件添加到容器,默认是边界布局
		this.add(ta,BorderLayout.CENTER);
		//默认布局流式布局
		JPanel ps=new JPanel();
		ps.add(tf);
		ps.add(send);
		
		this.add(ps, BorderLayout.SOUTH);
		
		//添加在线列表的组件
		list.setModel(model);
		this.add(list,BorderLayout.EAST);
		
		//设置窗口的属性
		this.setTitle(title);
		this.setSize(300,400);//设置窗口大小
		this.setLocationRelativeTo(null);//窗口在屏幕中央显示
		this.setVisible(true);//显示窗口
		try {
			socket=new Socket(/*"10.8.41.112"*/"localhost",8888);
			//创建对象输出流对象
			 oos=new ObjectOutputStream(
					socket.getOutputStream());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}		
		//发送上线的请求
		Info info=new Info(1,title,"上线了");
		try {
			oos.writeObject(info);
			oos.flush();
		} catch (IOException e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
		}
	
		//发送按钮事件
		send.addActionListener(
				new ActionListener(){
					@Override
					public void actionPerformed(ActionEvent e) {
						Info info=null;
						//表示私聊
						if(name!=null){
							if(name.equals(title)){
								return;
							}else if(name.equals("在线列表                        ")){
								 info=new Info(0,title,tf.getText());
							}else{
							 info=new Info(3,name,tf.getText());
							}
							}else{
							//表示群聊
							//获取文本框的内容
							 info=new Info(0,title,tf.getText());
						}
						try {
							oos.writeObject(info);
							oos.flush();
						} catch (IOException e1) {
							// TODO Auto-generated catch block
							e1.printStackTrace();
						}						
					}					
				});
		//关闭窗口事件
		this.addWindowListener(new WindowAdapter(){
			@Override
			public void windowClosing(WindowEvent e) {
				//1.向服务器发送type=2的请求(下线)
				Info info=new Info(2,title,"下线了");
				try {
					oos.writeObject(info);
					oos.flush();
				} catch (IOException e2) {
					// TODO Auto-generated catch block
					e2.printStackTrace();
				}
				//2.关闭当前窗口
				dispose();
			}
		});
		list.addListSelectionListener(new ListSelectionListener(){

			@Override
			public void valueChanged(ListSelectionEvent e) {
				//获取list选中的值
				name=list.getSelectedValue();
			}
			
		});
	}
	public void start(){
		new Thread(new ServerHandler()).start();
	} 
	private class ServerHandler implements Runnable{

		@Override
		public void run() {
			try{
			//读服务器响应的信息
			ObjectInputStream ois=new ObjectInputStream(
							socket.getInputStream());
			Object obj="";
			while((obj=ois.readObject())!=null){
				//System.out.println(msg);
				Info info=(Info) obj;
				switch(info.getType()){
				case 0://显示群聊
					if(info.getName().equals(title)){
						ta.append("我:"+info.getSendInfo()+"\n");
					}else{
						ta.append(info.getName()+":"+info.getSendInfo()+"\n");
					}
					break;
				case 1:
					if(info.getName().equals(title)){
						ta.append("我:"+info.getSendInfo()+"\n");
					}else{
						ta.append(info.getName()+":"+info.getSendInfo()+"\n");
					}
					model.clear();
					model.addElement("在线列表                        ");
					String[] onlines=info.getOnlines();
					for(String name:onlines){
						//System.out.println(name+",");
						model.addElement(name);
					}
					break;
				case 2:
					ta.append(info.getName()+":"+info.getSendInfo()+"\n");
					model.clear();
					model.addElement("在线列表                        ");
							onlines=info.getOnlines();
					String[] onlines2=info.getOnlines();
					for(String name:onlines2){
						//System.out.println(name+",");
						model.addElement(name);
					}
					break;
				case 3:
					if(info.getName().equals(title)){
						ta.append("我:"+info.getSendInfo()+"\n");
					}else{
						ta.append(info.getName()+":"+info.getSendInfo()+"\n");
					}
					break;
				}
			}
		}catch(Exception e){
			e.printStackTrace();
			}
		}}
}
public class Server {
	private ServerSocket serverSocket;
	//存放所有的客户端的输出流对象
	private ObjectOutputStream[] allOut;
	//定义在线的列表数组
	private String[] onlines;
	
	public Server(){
		try {
			serverSocket  = 
					new ServerSocket(8888);
			//初始化,一开始客户端数量为0
			allOut=new ObjectOutputStream[0];
			onlines=new String[0];
			System.out.println("服务器初始化成功...");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void start() throws IOException{
		//实现多个线程的服务器,和多个客户端对话
		while(true){
			System.out.println("等待连接....");
			Socket socket = serverSocket.accept();
			System.out.println("一个客户端连接了....");
			new Thread(new ClientHandler(socket)).start();
			}
	}
	private class ClientHandler implements Runnable{
		Socket socket;
		ObjectInputStream br;
		ObjectOutputStream oos;
		public ClientHandler(Socket socket){
			this.socket=socket;
		}
		@Override
		public void run() {
			try{
			InputStream in = socket.getInputStream();
			OutputStream out = socket.getOutputStream();
			
			//读服务器响应的信息
			 br = new ObjectInputStream(
									socket.getInputStream());
			 oos = new ObjectOutputStream(
					socket.getOutputStream());
			
			
			
			
			while(true){
				Object obj = br.readObject();
				if(obj!=null){
					System.out.println("客户端说:"+obj);
					Info info=(Info) obj;
					switch(info.getType()){
					case 0:
						//群聊
						sendAllMsg(info);
						break;
					case 1:
						//扩容数组
						allOut=Arrays.copyOf(allOut, allOut.length+1);
						allOut[allOut.length-1] = oos;
						onlines =Arrays.copyOf(onlines, onlines.length+1);
						onlines[onlines.length-1]=info.getName();
						//info对象中包含信息
						info.setOnlines(onlines);
						sendAllMsg(info);
						break;
					case 2:
						//缩容 onlines  allOut
						String[] newOnlines=new String[onlines.length-1];
						int index=0;
						for(String str:onlines){
							if(!str.equals(info.getName())){
								newOnlines[index++]=str;
							}
						}
						onlines=newOnlines;
						ObjectOutputStream[] newAllOut=new ObjectOutputStream[allOut.length-1];
						index=0;
						for(ObjectOutputStream output:allOut){
							if(output!=oos){
							newAllOut[index++]=output;}
						}
						allOut=newAllOut;
						//info对象中包含信息
						info.setOnlines(onlines);
						sendAllMsg(info);
						break;
					case 3:
						//找到oos的索引
						int oosIndex=-1;
						for(int i=0;i<onlines.length;i++){
							if(info.getName().equals(onlines[i])){
								oosIndex=i;
								break;
							}
						}
						//找到发射请求的人的姓名
						String name="";
						for(int i=0;i<allOut.length;i++){
							if(allOut[i]==oos){
								name=onlines[i];
								break;
							}
						}
						info.setName(name);
						
						ObjectOutputStream[] allOut1={oos,allOut[oosIndex]};
						for(ObjectOutputStream p:allOut1){
							p.writeObject(info);
							p.flush();
							}
						break;
					}
				}
			}
		}catch(Exception e){
			e.printStackTrace();} 
		}		
	}
	private void sendAllMsg(Info info) throws Exception{
		//群聊
		for(ObjectOutputStream p:allOut){
		p.writeObject(info);
		p.flush();}
	}
	public static void main(String[] args) throws Exception {
		Server server = new Server();
		server.start();
	}
}

3. 一点闲话

项目和平时练习最大的区别就是平时的练习太有针对性了,虽然可以很好地让人快速的去了解去熟悉知识,但局限性就在于只针对某一方面的知识,没办法和其他相关知识串联起来,而且项目除了可以一次深入练习到不同知识之外,还可以锻炼代码逻辑性,一个项目可以分为几个模块,几个功能点,几个类,几个方法,应该从何写起,先实现哪些基础框架,所以个人认为每学习一段时间最好是找一个项目来进行复习,一个项目重复练习也没关系,毕竟直到你可以自己完全独立流畅的写出一个项目来说它对你都是有用的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值