import java.net.*;
import java.io.*;

public class Client {
  
    /////////////Control + C 时发生NullPointerException,在写线程处
    public static void main(String[] args) {
  // 获取客户端名字
  Client.cname = args[0];

  // 获取服务器IP地址
  Client.hostIP = args[1];

  // 启动客户端
  new Client().start();
    }

    /*
  * 客户端启动函数
  */

    public void start() {
  // 客户端与服务器建立连接
  connect();

  // 为已连接的Socket准备好输入输出流
  prepareIO();

  // 客户端向服务器注册自己的身份
  login();

  // 启动客户端的消息接受线程
  cr = new Thread(new ClientReadMessage());
  cr.start();

  // 启动客户端的消息输出线程
  cw = new Thread(new ClientWriteMessage());
  cw.start();
    }

    /*
  * 连接到指定的服务器
  */

    public void connect() {
  try {
      client = new Socket(hostIP, 8888);
      connected = true;
  } catch (UnknownHostException e) {
      System.out.println("服务器不存在或者已经关闭!");
      System.exit(-1);
  } catch (IOException e) {
      System.out.println("服务器已经关闭");
      System.exit(-1);
  }
    }

    /*
  * 断开与服务器的连接
  */

    public void disconnect() {
  try {
      connected = false;
      sendMessage("?bye");//通知服务器客户端要关闭,让服务器自己先把IO流关闭
      if (din != null)
    din.close();
      if (dout != null)
    dout.close();
      if (br != null)
    dout.close();
      if (client != null)
    client.close();
  } catch (IOException e) {
      e.printStackTrace();
  }
  System.out.println("You 退出了!");
    }

    /*
  * 客户端注册函数
  */

    public void login() {
  sendMessage(cname);
    }

    /*
  * 输入输出流生成函数 包括一个标准输入流、一个Socket输出流和一个Socket输入流
  */

    public void prepareIO() {
  br = new BufferedReader(new InputStreamReader(System.in));
  try {
      dout = new DataOutputStream(client.getOutputStream());
      din = new DataInputStream(client.getInputStream());
  } catch (IOException e) {
      e.printStackTrace();
  }
    }

    /*
  * 客户端发送信息线程类 从控制台输入信息,并发送到服务器
  */

    class ClientWriteMessage implements Runnable {
  @Override
  public void run() {
      String str = null;
      while (connected) {
    str = receiveConsole();
    if (str.equals("?bye")) {
        disconnect();
    } else {
        sendMessage(str);
    }
      }
  }
    }

    /*
  * 客户端接受信息线程类 从服务器接受信息,并在控制台显示
  */

    class ClientReadMessage implements Runnable {
  @Override
  public void run() {
      while (connected) {  
    String str = receiveMessage();
    System.out.println(str);  
      }
  }
    }

    /*
  * 消息输出函数 向服务器发送消息
  */

    public void sendMessage(String message) {
  try {
      dout.writeUTF(message);
      dout.flush();
  } catch (IOException e) {
      e.printStackTrace();
  }  
    }

    /*
  * 消息输入函数 从服务器接受消息
  */

    public String receiveMessage() {
  String message = null;
  try {
      message = din.readUTF();
  } catch (EOFException e) {//////////第二条开始发送?bye时连续发生异常,其实也是服务器的socket关闭
      message = "";
      System.exit(0);
  } catch (SocketException e) {///////第一条发送?bye发生异常,因为服务器的socket已经关闭
      message = "";
      System.exit(0);
  } catch (IOException e) {
      e.printStackTrace();
  }
  return message;
    }

    /*
  * 消息输入函数 从控制台接受输入
  */

    public String receiveConsole() {
  String message = null;
  try {
      message = br.readLine();
  } catch (IOException e) {
      e.printStackTrace();
  }  
  return message;
    }

    Socket client = null;
    static String cname = null;
    static String hostIP = null;
    volatile boolean connected = false;
    Thread cr = null;
    Thread cw = null;

    BufferedReader br = null;
    DataOutputStream dout = null;
    DataInputStream din = null;
}