异步处理多客户端连接服务端

上篇讲到的是服务端接收一个客户端的请求之后就结束了,不能再接收其他客户端的请求了,实际情况上我们希望服务端能够处理来自不同用户的请求。

想到这里,或许我们可以用一个死循环,在循环体里面ServerSocket调用其accept方法试图接收来自客户端的连接请求。当没有接收到请求的时候,程序会在这里阻塞直到接收到来自客户端的连接请求,之后会跟当前建立好连接的客户端进行通信,完了后会接着执行循环体再次尝试接收新的连接请求。这样我们的ServerSocket就能接收来自所有客户端的连接请求了,并且与它们进行通信了。这就实现了一个简单的一个服务端与多个客户端进行通信的模式。

这样似乎实现了多客户端的请求,但又存在了一个问题:这种服务端处理客户端的连接请求是同步进行的,每次接收到来自客户端的连接请求后,都要先跟当前的客户端通信完之后才能再处理下一个连接请求。这在并发比较多的情况下会严重影响程序的性能,想象下你打开一个网页肯定希望立即得到反馈,而不是一直等着。

因此我们用异步处理与客户端通信的方式。

代码:

public class Server {
public static void main(String args[]) throws IOException {
// 定义一个ServerSocket监听在端口8888上
int port = 8888;
int i = 1; // 连接计数
// server尝试接收其他Socket的连接请求,
ServerSocket server = new ServerSocket(port);
while (true) {
// server的accept方法是阻塞式的 ,即等待着客户端的请求
Socket socket = server.accept();
// 每接收到一个Socket就建立一个新的线程来处理它
new Thread (new Link(socket)).start();
System.out.println("连接" + i++);
}
}
/**
* 处理客户端Socket连接请求
*/
static class Link implements Runnable { private Socket socket; public Link(Socket socket) {
this.socket = socket;
} public void run() { try { handleSocket();
} catch (Exception e) {
e.printStackTrace();
}
}
//跟客户端Socket进行通信
private void handleSocket() throws Exception {
// 跟客户端建立好连接,我们就可以获取socket的InputStream,从中读取客户端发过来的信息。
Reader reader = new InputStreamReader(socket.getInputStream());
char chars[] = new char[64];
int len;
StringBuilder sb = new StringBuilder();
String temp;
int index;
while ((len = reader.read(chars)) != -1) {
temp = new String(chars, 0, len);
if ((index = temp.indexOf("eof")) != -1) { // 遇到eof时就结束接收
sb.append(temp.substring(0, index));
break;
}
sb.append(temp);
}
System.out.println("from client: " + sb);
// 读完后写数据
Writer writer = new OutputStreamWriter(socket.getOutputStream());
writer.write("Hello Client:我是服务端输入数据");
// 释放资源
writer.flush();
writer.close();
reader.close();
socket.close();
}
}
}

输出

上述代码中,每次ServerSocket接收到一个新的Socket连接请求后都会新起一个线程来跟当前Socket进行通信,这样就达到了异步处理与客户端Socket进行通信的情况。

BurfferReader读取

对哦,还有一点,Socket的InputStream中接收数据时,一个一个字符的读有点太复杂了,有时候我们就会换成使用BufferedReader来一次读一行。

读取数据时用BufferedReader的readLine 方法

BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
char chars[] = new char[64];
int len;
StringBuilder sb = new StringBuilder();
String temp;
int index;
while ((temp=br.readLine()) != null) {
System.out.println(temp);
if ((index = temp.indexOf("eof")) != -1) {//遇到eof时就结束接收
sb.append(temp.substring(0, index));
break;
}
sb.append(temp);
}

需要注意的是,BufferedReader的readLine方法是一次读一行的,这个方法是阻塞的,直到它读到了一行数据为止程序才会继续往下执行,即直到程序遇到了换行符或者是对应流的结束符readLine方法才会认为读到了一行,才会结束其阻塞,让程序继续往下执行。

所以我们在使用BufferedReader的readLine读取数据的时候一定要记得在对应的输出流里面一定要写入换行符(流结束之后会自动标记为结束,readLine可以识别),写入换行符之后一定记得如果输出流不是马上关闭的情况下记得flush一下,这样数据才会真正的从缓冲区里面写入。

即在输入数据后记得在加入换行符,例

   writer.write("Hello ,我是客户端输入数据1111");
writer.write("eof\n"); `

如果客户端中读取来自服务端数据也用bufferedReader的readLine时,记得也要在服务端write写入数据后添加换行符。

设置时间超时

这个应该我们都遇到过,客户端需要通过Socket从服务端获取到信息,然后给用户展示在页面上。Socket在读数据的时候是阻塞式的,如果没有读到数据程序会一直阻塞在那里。在同步请求的时候我们肯定是不能允许这样的情况发生的,这就需要我们在请求达到一定的时间后控制阻塞的中断,让程序得以继续运行。Socket为我们提供了一个setSoTimeout()方法来设置接收数据的超时时间,单位是毫秒。当设置的超时时间大于0,并且超过了这一时间Socket还没有接收到返回的数据的话,Socket就会抛出一个SocketTimeoutException。

例:设置6秒超时

 client.setSoTimeout(6000);
try{
while ((len=reader.read(chars)) != -1) {
temp = new String(chars, 0, len);
if ((index = temp.indexOf("eof")) != -1) {
sb.append(temp.substring(0, index));
break;
}
sb.append(new String(chars, 0, len));
}
} catch (SocketTimeoutException e) {
System.out.println("数据读取超时。");
}

java网络编程之socket(2)的更多相关文章

  1. java网络编程之Socket编程

    概念 网络编程分为BIO(传统IO).NIO.AIO.Socket编程属于BIO这种传统IO. InetAddress java.net.InetAddress是JAVA中管理IP地址的类,常用 pu ...

  2. Java 网络编程之 Socket

    ========================UDP============================= UDP---用户数据报协议,是一个简单的面向数据报的运输层协议. UDP不提供可靠性, ...

  3. java网络编程之socket

    网络编程是什么 网络编程的本质是两个设备之间的数据交换,当然,在计算机网络中,设备主要指计算机.数据传递本身没有多大的难度,不就是把一个设备中的数据发送给两外一个设备,然后接受另外一个设备反馈的数据. ...

  4. java网络编程之socket(1)

    网络编程是什么 网络编程的本质是两个设备之间的数据交换,当然,在计算机网络中,设备主要指计算机.数据传递本身没有多大的难度,不就是把一个设备中的数据发送给两外一个设备,然后接受另外一个设备反馈的数据. ...

  5. java 网络编程之Socket编程

      1.客户端代码 1 package com.gylhaut.socket; 2 3 import java.io.BufferedReader; 4 import java.io.IOExcept ...

  6. 网络编程之Socket & ServerSocket

    网络编程之Socket & ServerSocket Socket:网络套接字,网络插座,建立网络通信连接至少要一对端口号(socket).socket本质是编程接口(API),对TCP/IP ...

  7. GO语言的进阶之路-网络编程之socket

    GO语言的进阶之路-网络编程之socket 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是socket; 在说socket之前,我们要对两个概念要有所了解,就是IP和端口 ...

  8. Java网络编程之TCP、UDP

    Java网络编程之TCP.UDP 2014-11-25 15:23 513人阅读 评论(0) 收藏 举报 分类: java基础及多线程(28) 版权声明:本文为博主原创文章,未经博主允许不得转载.   ...

  9. Java网络编程之UDP

    Java网络编程之UDP 一.C/S架构中UDP网络通信流程 ①创建DatagramSocket与DatagramPacket对象 ②建立发送端,接收端 ③建立数据包 ④调用Socket的发送.接收方 ...

随机推荐

  1. 一个使用openGL渲染的炫丽Android动画库二(碎片化曲面动画)

    续一个使用openGL渲染的炫丽Android动画库 MagicSurfaceView v1.1.0发布, 新增碎片化曲面动画 地址:https://github.com/gplibs/android ...

  2. 2、Java应用中常见的JDBC连接字符串(SQLite、MySQL、Oracle、Sybase、SQLServer、DB2)

    2.Java应用中常见的JDBC连接字符串 Java应用中连接数据库是不可或缺的,于是便整理一些可能用到的JDBC的jar包及其相匹配的URL,以备日后查阅. 1)SQLite Class.forNa ...

  3. opencv探索之路(十二):感兴趣区域ROI和logo添加技术

    在图像处理领域,有一个非常重要的名词ROI. 什么是ROI? 它的英文全称是Region Of Interest,对应的中文解释就是感兴趣区域. 感兴趣区域,就是我们从图像中选择一个图像区域,这个区域 ...

  4. SharePoint 服务器端对象迁移文件夹

    最近有个需求,写个定时迁移文件夹的小程序,因为计时器任务比较繁琐,所以选择了控制台程序.然后,用windows的计划任务执行,也许,会有广大朋友需要类似的功能,简单的分享一下代码逻辑,功能非常简单,希 ...

  5. iOS面试题及答案

    设计模式是什么? 你知道哪些设计模式,并简要叙述? 设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型的事情. 1). MVC模式:Model View Control,把模型 视图 控制器 ...

  6. python爬虫从入门到放弃(五)之 正则的基本使用

    什么是正则表达式 正则表达式是对字符串操作的一种逻辑公式,就是 事先定义好的一些特定字符.及这些特定字符的组合,组成一个"规则字符",这个"规则字符" 来表达对 ...

  7. day5_ 导入模块和包

    ######################模块导入模块做的事1.产生新的名称空间2.以新建的名称空间为全局名称空间,执行文件的代码3.拿到一个模块名spam,指向spam.py产生的名称空间 imp ...

  8. MySQL数据库安全策略

    1. 删除所有MySQL中的用户(包括root用户): mysql> delete from mysql.user root可以保留,然后修改为其他用户 2. 为管理员root用户设置密码 : ...

  9. CPU-Z五大主要功能及使用方法初步了解

    CPU-Z这款软件除了具有查看CPU温度这个功能之外,还有很多其他的功能.今天就和小编一起去看看CPU-Z的5大功能以及他们的使用方法吧! CPU信息标签页 CPU-Z介绍: CPU-Z是一款著名的免 ...

  10. ztree学习笔记(一)

    在项目当中,经常会用到ztree树形插件,之前做的几个项目当中都用到了这个插件,感觉功能还是很强大的,而且在网上还找到了中文的API,因为项目中的树形结构不是自己做的,所以现在从头学习一下,并且记录一 ...