客户端:
public class Client {
/*
* java.net.Socket
* 套接字,封装了TCP协议,使用它可以与远端计算机通讯
*/
private Socket socket;
/*
* 定义构造方法,用来初始化客户端
*/
/*
* 实例化Socket时需要传入两个参数:
* 1:服务端计算机的地址信息(IP地址)
* 2:服务端计算机上运行的服务端应用程序申请的服务端口
*
*
* 通过IP可以找到服务端的计算机,通过端口可以连接到运行在服务端计算机上的服务端应用程序
*
* 实例化Socket的过程就是连接服务端的过程,若服务端无响应,实例化过程会抛出异常
*
* 端口号是一个整数,2字节内的整数0-65536
* 但3000以内的端口号不要使用,因为紧密的绑定着系统程序,和世界上流行的应用程序。
* 10000以上也很少被使用
*/
public Client() throws Exception{
try {
Scanner scanner=new Scanner(System.in);
String str=scanner.nextLine();
socket=new Socket(str,8088);//"176.195.107.86"
System.out.println(str);
System.out.println("连接上服务端!");
} catch (Exception e) {
//记录日志
throw e;
}
}
public void start(){
try{
//用来获取用户输入
Scanner scanner=new Scanner(System.in);
/**
* Socket提供方法:
* OutputStream getOutputStream()
* 通过获取的输出流写出的数据就可以通过
* 网络发送给远端计算机,对于客户端而言远端就是服务器。
*/
OutputStream out=socket.getOutputStream();
//String message="你好服务端";
//byte[] data=message.getBytes("UTF-8");
OutputStreamWriter osw=new OutputStreamWriter(out,"UTF-8");
PrintWriter pw=new PrintWriter(osw,true);
//接收客户端发送过来的消息的线程启动
ServerHandler handler=new ServerHandler();
Thread t=new Thread(handler);
t.start();
System.out.println("请开始聊天吧!");
String message=null;
long time=System.currentTimeMillis()-500;
while(true){
message=scanner.nextLine();
if(System.currentTimeMillis()-time>=500){
pw.println(message);
time=System.currentTimeMillis();
}else{
System.out.println("你说话过快!");
time=System.currentTimeMillis();
}
}
//pw.close();
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {//程序入口方法,没有逻辑代码
Client client;
try {
client = new Client();
client.start();
} catch (Exception e) {
e.printStackTrace();
System.out.println("服务端启动失败");
}
}
/**
* 该线程专门用来循环读取服务端发送过来的消息并输出
* 到客户端的控制台上
* @author live
*
*/
private class ServerHandler implements Runnable{
public void run() {
try{
InputStream in=socket.getInputStream();
InputStreamReader isr
=new InputStreamReader(in,"UTF-8");
BufferedReader br
=new BufferedReader(isr);
String message=null;
while((message=br.readLine())!=null){
System.out.println(message);
}
}catch(Exception e){
}
}
}
}
服务端:
public class Server {
/**
* 运行在服务端的ServerSocket主要负责两个工作:
* 1:向系统申请服务端口,客户端就是通过这个端口与服务端应用程序建立连接的。
* 2:监听服务端口,一旦客户端通过该端口尝试连接时,ServerSocket就会实
* 例化一个Socket与该客户端通讯。
*/
private ServerSocket server;
//存放所有客户端的输出流,用于广播消息
private List<PrintWriter> allOut;
public Server() throws Exception{
try{
/**
* 实例化SeverSocket时需要指定服务端口,客户端就是通过这个端口与服务端建立连接的。
*
* 该端口不能与系统其他程序申请的端口冲突,否则会抛出异常。
* address already in use
*/
server=new ServerSocket(8088);
allOut=new ArrayList<PrintWriter>();
}catch(Exception e){
throw e;
}
}
public void start(){
try{
/*
* ServerSocket提供方法:
* Socket accept()
* 该方法是一个阻塞方法,调用后会一致等待客户端的连接,一旦一个客户端
* 通过ServerSocket申请的端口建立连接,那么accept方法会返回一个
* Socket实例,通过该Socket实例可以与建立连接的客户端进行通讯
*/
while(true){
System.out.println("等待客户端连接。。。。。。。。。");
Socket socket=server.accept();
System.out.println("一个客户端连接了");
//启动一个线程来处理该客户端交互
ClientHandler handler=
new ClientHandler(socket);
Thread t=new Thread(handler);
t.start();
}
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args) {
try{
Server server=new Server();
server.start();
}catch(Exception e){
e.printStackTrace();
System.out.println("服务端启动失败");
}
}
/*
* 该线程任务是用于处理与指定客户端的交互工作。
*/
private class ClientHandler implements Runnable{
//当前线程通过这个Socket与指定客户端交互
private Socket socket;
//该客户端的地址信息
private String host;
public ClientHandler(Socket socket){
this.socket=socket;
//通过Socket获取远端计算机地址信息
InetAddress address=socket.getInetAddress();
//获取IP地址的字符串形式
host=address.getHostAddress();
}
public void run() {
PrintWriter pw = null;
try{
/*
* Socket提供方法:
* InputStream getInputStream()
* 通过获取的输入流读取的字节就是来自远端发送过来的数据,对于服务端而言,
* 远端指的就是客户端。
*/
InputStream in=socket.getInputStream();
InputStreamReader isr=new InputStreamReader(in,"UTF-8");
BufferedReader br=new BufferedReader(isr);
/*
* 通过Socket获取输出流,用于将消息发送给客户端
*/
OutputStream out=socket.getOutputStream();
OutputStreamWriter osw=new OutputStreamWriter(out,"UTF-8");
pw=new PrintWriter(osw,true);
//将该客户端的输出流存入共享集合
synchronized (allOut) {//给allOut加锁,保证线程安全
allOut.add(pw);
}
String message=null;
/*
* 使用br.readline读取客户端发送过来的一行字符串时,该方法处于阻塞状态
* 直到客户端真实发送过来一行,这里才会返回
*
* 但是当客户端断开连接时,br.readline会根据客户端不同操作系统有不同的反馈
* 当Windows的客户端断开,br.readline方法会抛出异常
* 当Linux的客户端断开,br.readline方法会返回null.
*/
while((message=br.readLine())!=null){
//System.out.println(host+"说:"+message);
/*//将读到的内容再回复给客户端(临时测试用)
pw.println("回复:"+message);
*/
/*
* 将消息转发给所有客户端
*/
synchronized (allOut) {
for(PrintWriter o:allOut){
o.println(host+"说:"+message+"\n连接人数:"+allOut.size());
}
}
}
}catch(Exception e){
} finally {
/*
* 当客户端断线,要将输出流从共享集合中删除
* 需要同步
*
* 处理客户端断开连接的操作
*/
synchronized (allOut) {
allOut.remove(pw);
}
try{
socket.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
}
}