功能:
客户端:从控制台发到服务器端,再接收服务器端返回的信息,打印到控制台
服务器端:从一个控制台读消息,发给所有连接到服务器的客户端上,实际上实现了群聊的机制,即服务器广播一个客户端发送的消息。
FirstServer.java
package com.InetAddress;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class FirstServer {
//线程安全Set集合
public static Set<Socket> clients = Collections.synchronizedSet(
new HashSet<Socket>());
public static void main(String[] args) throws IOException {
//ServerSocket只负责接收连接
ServerSocket ss = new ServerSocket(9999);
System.out.println("服务器正在等待连接:");
while(true){
//Socket会阻塞线程
Socket s = ss.accept();
//每当有一个客户端连接就把该客户端的socket加入到set集合中
clients.add(s);
System.out.println("已连接");
System.out.println(clients);
System.out.println("客户端的数量:"+clients.size());
new ServerThread(s).start();
}
}
}
class ServerThread extends Thread{
private Socket socket;
public ServerThread(Socket socket){
this.socket = socket;
}
public void run(){
/*
* 从socket里面读,然后打印到控制台
*/
try{
//使用BufferedReader的好处是可以一次读一行
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String line = null;
while((line = br.readLine())!=null){
//for(Socket s : FirstServer.clients){
//用迭代器来遍历HashSet集合
for(Iterator<Socket> it = FirstServer.clients.iterator();it.hasNext(); ){
Socket s = it.next();
try{
//往socket里面写,println()一次就会清空一次
PrintStream ps = new PrintStream(s.getOutputStream());
System.out.println(line);//打印到控制台
ps.println(line);//输出到各客户端的socket,就相当于打印到客户端控制台上
}catch(SocketException sex){
//此时捕获到的异常即是客户端断开连接时会产生的SocketException,要把这个客户端从set集合中删除
//此处可以不做任何处理,只要在最后捕获SocketException就可以了
it.remove();
//sex.printStackTrace();
}catch(Exception ex){
ex.printStackTrace();
}
}
}//while
}catch(SocketException sex){
//把退出的客户端的socket从集合中删除
FirstServer.clients.remove(socket);
}catch(Exception ex){
ex.printStackTrace();
}
}//run()
}//Thread
FirstClient.java
package com.InetAddress;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
public class FirstClient {
public static void main (String[] args) throws IOException {
Socket s = new Socket("127.0.0.1",9999);
new ClientThread(s).start();
/*
*从控制台读,往socket里写
*/
//把字节流转化成字符流,从控制台读
InputStreamReader reader = new InputStreamReader(System.in);
//使用BufferedReader的好处是可以一次读一行
BufferedReader br = new BufferedReader(reader);
//往socket里面写,println()一次就会清空一次
PrintStream ps = new PrintStream(s.getOutputStream());
String keyline = null;
while((keyline = br.readLine())!=null){
//打印到控制台
//System.out.println(keyline);
//发送到socket里
ps.println(keyline);
}
}
}
class ClientThread extends Thread{
private Socket socket;
public ClientThread(Socket socket){
this.socket = socket;
}
public void run(){
try {
/*
*从socket读,打印到控制台
*/
//把字节流转化成字符流,从socket读
InputStreamReader reader1 = new InputStreamReader(socket.getInputStream());
//使用BufferedReader的好处是可以一次读一行
BufferedReader br1 = new BufferedReader(reader1);
//往socket里面写,println()一次就会清空一次
//PrintStream ps = new PrintStream(s.getOutputStream());
String keyline1 = null;
while((keyline1 = br1.readLine())!=null){
//打印到控制台
System.out.println(keyline1);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
此处客户端加线程是因为
1.从控制台读,往socket里写是一个死循环
2,从socket里读,往控制台写也是一个死循环
如果client端不起线程,两个死循环(都是阻塞式)不可能同时执行