java实现hello/hi聊天程序

1.使用java实现服务器与客户端之间的对话,客户端与服务器java

服务器端代码linux

import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Chat { // 运行在服务端的Socket
    private ServerSocket server; // 线程池,用于管理客户端链接的交互线程
    private ExecutorService threadPool; // 保存全部客户端输出流的集合
    private HashMap<String, PrintWriter> allout; /** * 构造方法,用于初始化服务端 * * @throws IOException */
    public Chat() throws IOException { try { /* * 建立ServerSocket时须要指定服务端口 */ System.out.println("初始化服务端"); server = new ServerSocket(8088); // 初始化线程池
            threadPool = Executors.newFixedThreadPool(50); // 初始化存放全部客户端输出流的集合
            allout = new HashMap<String, PrintWriter>(); System.out.println("服务端初始化完毕"); } catch (IOException e) { e.printStackTrace(); throw e; } } /** * 服务端开始工做的方法 */
    public void start() { try { /* * ServerSocket的accept方法 用于监听8088端口,等待客户端的链接 该方法是一个阻塞方法,直到一个 * 客户端链接,不然该方法一直阻塞。 若一个客户端链接了,会返回该客户端的 Socket */
            while (true) { System.out.println("等待客户端链接..."); Socket socket = server.accept(); /* * 当一个客户端链接后,启动启动一个线程 ClientHandler,将该客户端的Socket 传入,使得该线程与该 * 客户端交互 这样咱们能再次进入循环,接收下一个客户端的链接 */ Runnable handler = new ClientHandler(socket); // Thread t = new Thread(handler) ; // t.start();
 threadPool.execute(handler); } } catch (Exception e) { e.printStackTrace(); } } /** * 将给定的输出流共享集合 * * @param p */
    public synchronized void addout(String nickName, PrintWriter p) { allout.put(nickName, p); } /** * 将给定的输出流从共享集合中删除 * * @param p */
    public synchronized void removeOut(String nickName) { allout.remove(nickName); } /** * 将给定的消息转发给客户端 * * @param message */
    public synchronized void sendMessage(String message) { Set<Entry<String, PrintWriter>> set = allout.entrySet(); for (Entry<String, PrintWriter> e : set) { e.getValue().println(message); } } public static void main(String[] args) { Chat server; try { server = new Chat(); server.start(); } catch (IOException e) { e.printStackTrace(); System.out.println("服务端初始化失败"); } } /* * 服务器中的一个线程,用于与某个客户端 交互 使用线程的目的是使得服务端能够处理多客户端了。 */
 
    class ClientHandler implements Runnable { // 当前线程处理的客户端的Socket、
        private Socket socket; // 当前客户端的ip
        private String ip; // 当前客户端的昵称
        private String nickname; public ClientHandler(Socket socket) { this.socket = socket; InetAddress address = socket.getInetAddress(); // 获取远端计算机的IP地址
            ip = address.getHostAddress(); // address.getCanonicalHostName() // 获取客户端的端口号
            int port = socket.getPort(); System.out.println(ip + ":" + port + " 客户端链接了"); // 改成了使用昵称了,因此不在这里通知了 // //通知其余用户,该用户上线了 // sendMessage("["+ip+"]上线了");
 } /* * 该线程将当前Socket中的输入流获取 用来循环读取客户端发送过来的消息 (non-Javadoc) * * @see java.lang.Runnable#run() */
        public void run() { /* * 定义在try语句外的目的是,为了在finally中也能够引用到 */ PrintWriter pw = null; try { /* * 为了让服务端与客户端发送信息 咱们须要经过socket 获取输出流。 */ OutputStream out = socket.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(out, "UTF-8"); pw = new PrintWriter(osw, true); /* * 将客户端的输出流存入共享集合 以即是的客户端也能接收服务端转发的消息 */
                // addout(pw); // Scanner sc = new Scanner(System.in) ; //
                // System.out.println(sc.nextLine());
                /* * 经过socket获取远端的地址信息 对于服务端而言,远端就是客户端了 */
 
                /* * 经过刚刚连上的客户端的Socket获取 输入流,来读取客户端发送过来的信息 */ InputStream in = socket.getInputStream(); /* * 将字节输入流包装为字符输出流,这样 能够指定编码集来读取每个字符 */ InputStreamReader isr = new InputStreamReader(in, "UTF-8"); /* * 将字符流转换为缓冲字符输入流 这样就能够以行为单位读取字符串了 */ BufferedReader br = new BufferedReader(isr); /* * 当建立好当前客户端的输入流后 读取的第一个字符串,应当是昵称 */ nickname = br.readLine(); addout(nickname, pw); // 通知全部客户端,当前用户上线了
                sendMessage("[" + nickname + "]上线了"); String message = null; // 读取客户端发送过来的一行字符串
                /* * 读取客户端发送过来的信息这里 windows与linux存在必定的差别: linux:当客户端与服务端断开链接后 * 咱们经过输入流会读取到null 但这是合乎逻辑的,由于缓冲流的 readLine()方法若返回null就 * 表示没法经过该留再读取到信息。 参考以前服务文本文件的判断。 * * windows:当客户端与服务端断开链接后 readLine()方法会抛出异常。 */
                while ((message = br.readLine()) != null) { message = hexiestr(message); // System.out.println( // "客户端说:" + message); // pw.println("服务端说:"+message) ;
                    /* * 当读取客户端发送过来的一条消息后,将该消息转发给全部客户端 */
                    // for (PrintWriter o : allout) { // o.println(message); // }
                    String siliao = siliaostr(message); if (allout.containsKey(siliao)) { message = message.substring(message.indexOf(":") + 1); allout.get(siliao).println(nickname + "对你说:" + message); } else { sendMessage(nickname + "说:" + message); } } } catch (Exception e) { // 在windows中的客户顿, // 报错一般是由于客户端断开了链接
            } finally { /* * 首先将该客户端的输出流从共享集合中删除 */ removeOut(nickname); // 输出当前在线人数
                System.out.println("当前在线人数为:" + allout.size()); // 通知其余用户,该用户下线了
                sendMessage("[" + nickname + "]下线了"); /* * 不管是Linux 用户仍是windows用户,当予服务端断开链接后、、、 咱们都应该在服务端也与客户端断开链接 */
                try { socket.close(); } catch (IOException e) { e.printStackTrace(); System.out.println("一个客户端下线了.."); } } } } public String siliaostr(String str) { int fir = str.indexOf("\\") + 1; int las = str.indexOf(":"); if (fir <= 0 || las <= 0) { return str; } else { String sub = str.substring(fir, las); return sub; } } public String hexiestr(String str) { String s = str.replaceAll("(sb|cao|ca)", "**"); return s; } }

客户端代码c++

import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; public class Client { //Socket,用于链接服务端的ServerSocket
    private Socket socket; private Scanner scanner; /* * 客户端构造方法,用于初始化客户端 */
    public Client() throws IOException{ try { /* * 建立Socket对象时, * 就会尝试根据给定的地址与端口链接服务端。 * 因此,若该对象建立成功,说明与服务端链接正常。 */ System.out.println("正在链接服务端。。"); socket=new Socket("127.0.0.1",8088); System.out.println("成功链接服务端"); }catch (IOException e) { throw e; } } /* * 客户端启动方法 */
    public void start(){ try { //建立并启动线程,来接收服务端的消息
            Runnable runn=new GetServerInfoHandler(); Thread t=new Thread(runn); t.start(); /* * 能够经过Socket的getOutputStream() * 方法得到一条输出流,用于将信息发送至服务端 */ OutputStream out=socket.getOutputStream(); /* * 使用字符流来根据指定的编码集将字符串转换为字节后, * 在经过out发送给服务端 */ OutputStreamWriter osw=new OutputStreamWriter(out,"UTF-8"); /* * 将字符流包装为缓冲字符流,就能够按行为单位写出字符串 */ PrintWriter pw=new PrintWriter(osw,true); /* * 建立一个Scanner,用于接收用户输入的字符串 */ scanner = new Scanner(System.in); //输出欢迎用语
            System.out.println("欢迎来到Linus聊天室"); while(true){ //首先输入昵称
            System.out.println("请输入昵称"); String nickname=scanner.nextLine(); if(nickname.trim().length()>0){ pw.println(nickname); break; } System.out.println("昵称不能为空"); } while(true){ String str=scanner.nextLine(); pw.println(str); } } catch (Exception e) { e.printStackTrace(); } } public static void main(String [] args){ try { Client client=new Client(); client.start(); } catch (Exception e) { e.printStackTrace(); System.out.println("客户端初始化失败"); } } /* * 该线程的做用是循环接受服务端发送过来的信息,并输出待控制台 */
    class GetServerInfoHandler implements Runnable{ public void run() { try { /* * 经过Socket获取输入流 */ 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) { } } } }

程序执行结果以下:windows

2.分析socket大体系统调用流程api

  (1)创建一个服务器ServerSocket,并同时定义好ServerSocket的监听端口;
  (2)ServerSocket 调用accept()方法,使之处于阻塞。
  (3)建立一个客户机Socket,并设置好服务器的IP和端口。
  (4)客户机发出链接请求,创建链接。
  (5)分别取得服务器和客户端ServerSocket 和Socket的InputStream和OutputStream.
  (6)  利用Socket和ServerSocket进行数据通讯。服务器

其运行抽象图和运行流程图以下:socket

3.java中socket中api与linux中socket的api的对比this

new SeverSocket()与Linux中socket()bind()listen()的功能同样编码

SeverSocket.accept()与 Linux中accept()的功能同样spa

in.readLine()与 Linux中recv()的功能同样

java中经过调用本地方法来使用一些操做系统底层的功能,一般使用的都是c或c++代码来实现的,具备跨平台做用;linux中的socket API则只能在其系统上使用

相关文章
相关标签/搜索