3.服务器端读写消息实现

1.设定服务器顺循环等待:

    服务器不能只连接一个客户机就退出——你可以将ServerSocketaccept()方法放在一个循环中调用:进行一个客户机,当服务器与这个客户机通信完毕后,服务器就再次进入循环中,重新调用accept()方法等待下一客户机连接进入,代码示例如下:

while(true){

       Socket client=server.accept();//让服务器在while中等待:阻塞状态

     //从连接对象上得到输入输出流对象. . .

当然,如果你要控制服务器的启动和停止,硬编码while的条件为true可不是个好主意。

2.读取客户机字符串分析

   至此,我们知道了服务器创建的基本流程,但有一件事还没弄明白,一个字符串是如何通过网络被发送到客户机的?如何读取客户机发来的消息?

   发送字符串分析:

    请注意,发送字符串时,首先调用字符串的getBytes()方法,得到组成这个字符串的字节数组,发送出的实际上是这个字节数组。请测试如下代码,示例了在字符串和和字节数组之间的转换:

String s="Hello!";

       byte[] data=s.getBytes();

       for(int i=0;i<data.length;i++){

     System.out.println(i+"个字节是"+data[i]

     +" 二进制是:"+Integer.toBinaryString(data[i]));

       }

       //再将字节组转为字符串

       String src=new String(data);

System.out.println("生成的字符串是: "+src);

  运行后,输出的结果如下:

0个字节是72 二进制是:1001000

1个字节是101 二进制是:1100101

2个字节是108 二进制是:1101100

3个字节是108 二进制是:1101100

4个字节是111 二进制是:1101111

5个字节是33 二进制是:100001

生成的字符串是: Hello!

   我的目地只是要说明:字符串是由字节组成的,字节是二进制位组成的——在网络上发送的,其实是一个个字节(byte),或者说是组成每一个字节的一个二进制位(bit)。从网络中读取信息时,情况也是一样的。从InputStream对象中,一次只能读到一个字节,然后,再将这些字节组装为一个String对象——前提是对方发送的是一串文本,而不是一张图片,如下代码:

public void setUpServer(int port){

       try{

     //建立绑定在指定端口上的服务器对象

     ServerSocket server=new  ServerSocket(port);

    System.out.println("服务器创建成功!"+port);

   while(true){ //让服务器循环等待

      Socket client=server.accept();

     //从连接对象上得到输入输出流对象

      OutputStream out=client.getOutputStream();

      InputStream ins=client.getInputStream();

      String s="你好,欢迎来javaKe.com\r\n";

        byte[] data=s.getBytes();//取得组成这个字符串的字节

           out.write(data); //用输出对象发送数据

          out.flush();//强制输出

          int in=0; //一个字节一个字节的读取客户机的输入

          while(in!=13){//如果读到不是13,即回车字符

              in=ins.read();

              System.out.println("读到的一个是: "+in);

          }

          System.out.println("客户机按了回车,退出:"+in);

          client.close();//半闭与这个客户机的连接

    }

       }catch(Exception ef){ef.printStackTrace();}

    }

 

   运行如上程序,在命令行通过telnet连接后,输入一些字符,如图1.13所示。

 

可以看到,在telnet输入界面中,每输入一个字符(串),马上会被发送给服务器。在以上代码示例中,我们还改进了让服务能循环等待客户机连接的功能。

3.读取字符串实现

    要让服务器能读取客户机发来的一段字符串,必须设定一个基本规则:客户机可以连续输入字符给服务器,服务器循环读取,如果收到的字符串有一个回车符,则认为是一条字符串——一条消息。如果收到的字符串是“bye”,则认为客户机要结束通信,就断开客户机。

  实现代码如下:

 

/**

 * 简单Echo服务器实现

 * @author  www.NetJava.cn

 */

public class ChatServer {

    /**

     * 在指定端口上启动一个服务器

     * @param port:服务器所以的端口

     */

    public void setUpServer(int port){

       try{

    //建立绑定在指定端口上的服务器对象

    java.net.ServerSocket server=new java.net.ServerSocket(port);

    System.out.println("服务器创建成功!"+port);

      //当有客户机连接上时,等待方法就会返回,返回一个代表与客户机连接的对象

        while(true){ //让服务器进入循环等待状态

          java.net.Socket client=server.accept();

      System.out.println("Incoming clieng: "+client.getRemoteSocketAddress());

             //调用处理连接对象的方法去处理连接对象

               processChat(client);

            }

       }catch(Exception ef){

           ef.printStackTrace();

       }

    }

    /**

     * 处理连接对象:读取客户机发来的字符串,回送给客户机

     * @param client:与客户机的连接对象

     */

    private void processChat(java.net.Socket client)

    throws Exception{

            OutputStream out=client.getOutputStream();

             InputStream ins=client.getInputStream();

             String s="你好,欢迎来到服务器!\r\n";

              byte[] data=s.getBytes();//取得组成这个字符串的字节

               out.write(data); //用输出对象发送!

                 out.flush();//强制输出

       //调用读取字符串的方法,从输入流中读取一个字符串

                 String inputS=readString(ins);

            while(!inputS.equals("bye")){

                 System.out.println("客户机说: "+inputS);

                  s="服务器收到:"+inputS+"\r\n";

                  data=s.getBytes();//取得组成这个字符串的字节数组

                 out.write(data); //用输出流对象发送!

                 out.flush();//强制输出

                 inputS=readString(ins); //读取客户机的下一次输入

                 }

            s="你好,欢迎再来!\r\n";

            data=s.getBytes();

            out.write(data);

            out.flush();

            client.close();//半闭与客户机的连接

    }

   

    /**

     * 从输入流对象中读取字节,拼成一个字符串返加

     * 如果读到一个字节值为13,则认为以前的是一个字符串

     * @param ins:输入流对象

     * @return :从流上(客户机发来的)读到的字符串

     */

    private String readString(InputStream ins)

     throws Exception{

       //创建一个字符串缓冲区

          StringBuffer stb=new StringBuffer();

         char c =0;

          while(c!=13){

          //遇到一个换行,就是一句话

          int i= ins.read();//读取客户机发来的一个字节

          c=(char)i;//将输入的字节转换为一个Char

          stb.append(c);//将读到的一个字符加到字符串缓冲区中

          }

         //将读到的字节组转为字符串,并调用trim去掉尾部的空格

        String inputS=stb.toString().trim();

       return inputS;

    }

     //程序入口

    public static void main(String[] args) {

       ChatServer cs=new ChatServer();

       cs.setUpServer(9090);

    }

}

  运行如上程序,现在,服务器可以和客户机对话了吗?如图1.14所示。

 
 

4.网络数据传送总结。

    在程序中,字符串最终被以“字节”为单位通过网络发送(接收),再根据双方约定的协议 (如本例中,以回车为一条消息的结束)在程序中进行解析。这个过程如图1.15所示。

 

 

1.15通信时,数据在网络上传送格式的分层示意图

 

  但还有许多问题没有介绍清楚:这些字节是如何被发送到对方IP所在的电脑上的?如果中途丢了字节怎么办?这些字节又是如何转化为为电流信息的……建议大家在完成本节练习后,找一本TCP/IP分析方面的书做深入研读。

    在以上代码的基础上,你可以添加上当客户端连接上服务器后,服务项器提示用户输入用户名、输入密码的功能,然后实现服务器与客户机的聊天功能。

 

    现在最大的缺陷是:这个服务器同时只能与一个客户端通信,测试效果如图1.16所示。

 

 

   当用两个telnet客户端同时连接服务器时,只有一个可以通信,前一个退出后,后一个才能通信。这就是我们下一步的任务:多线程服务器实现。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的 Java WebSocket 服务器端的完整实现: ```java import javax.websocket.*; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.concurrent.CopyOnWriteArrayList; @ServerEndpoint("/websocket") public class WebSocketServer { private static CopyOnWriteArrayList<Session> sessions = new CopyOnWriteArrayList<>(); @OnOpen public void onOpen(Session session) { sessions.add(session); System.out.println("WebSocket opened: " + session.getId()); } @OnMessage public void onMessage(String message, Session session) { System.out.println("WebSocket message received: " + message); broadcast(message); } @OnClose public void onClose(Session session) { sessions.remove(session); System.out.println("WebSocket closed: " + session.getId()); } @OnError public void onError(Throwable throwable) { System.out.println("WebSocket error: " + throwable.getMessage()); } private void broadcast(String message) { for (Session session : sessions) { try { session.getBasicRemote().sendText(message); } catch (IOException e) { System.out.println("Failed to send message to session: " + session.getId()); sessions.remove(session); } } } } ``` 该实现使用了 Java WebSocket API,并使用了 `@ServerEndpoint` 注解将其声明为 WebSocket 服务器端点。在 `onOpen()` 方法中,会将新连接的 `Session` 对象添加到 `sessions` 列表中。在 `onMessage()` 方法中,会将接收到的消息进行广播,即发送给所有连接的客户端。在 `onClose()` 方法中,会将关闭的 `Session` 对象从 `sessions` 列表中移除。在 `onError()` 方法中,会输出错误信息。`broadcast()` 方法用于将消息发送给所有连接的客户端。 该实现使用了一个 `CopyOnWriteArrayList` 对象来存储所有连接的 `Session` 对象,这个对象是线程安全的,因此在多个线程中对其进行读写是安全的。在 `broadcast()` 方法中,会遍历 `sessions` 列表,并向每个客户端发送消息。如果发送消息失败,则会将该客户端的 `Session` 对象从 `sessions` 列表中移除。 要在 Tomcat 中运行该 WebSocket 服务器端,需要在 `web.xml` 文件中添加以下配置: ```xml <websocket-endpoint> <endpoint-class>WebSocketServer</endpoint-class> </websocket-endpoint> ``` 然后,在 Tomcat 中启动应用程序即可。现在,您可以使用任何 WebSocket 客户端连接到该服务器端了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值