ServerSocket详解
构造方法
ServerSocket()
ServerSocket(int port)
ServerSocket(int port ,int backlog)
serverSocket(int port,int backlog,InetAddress bindADDR)
port是服务器绑定的端口(服务器要监听的端口);backlog是指定客户连接请求队列的长度(若设置为0,就是该服务器不能被客户端访问),bindAddr指定服务器要绑定的IP地址。
如果把参数port设为0,由操作系统分配端口也称匿名端口,一般不会这样设置,因为客户端要访问服务器。许多操作系统限定了队列的最大长度,一般为50.当队列中的链接请求达到了队列的最大容量,服务器进程所在的主机会拒绝新的连接请求。
注:如果服务端Socket socket = serverSocket.accept()注释掉,客户端可以连接服务端,但服务端队列中的连接请求永远不会被取出,就是该请求的资源没有被释放
----
设定绑定的IP地址
如果主机只有一个IP地址,那么默认情况下,服务器程序就与该IP地址绑定。如果有主机有多个IP,假定一个主机有两个网卡,一个网卡连接到internet,ip地址222.67.5.94,一个网卡用于连接到本地局域网,ip地址为192.168.3.4;如果服务器仅仅被本地局域网中的客户访问,那么可以按如下方式创建ServerScoket:
ServerSocket serverSocket = new ServerSocket(8000,10,InetAddress.getByName("192.168.3.4"));
----
默认构造方法
ServerSocket不带参数构造方法,不与任何端口绑定,作用是在绑定端口前,设置一些ServerSocket的一些选项;
ServerSocket serverSocket = new ServerSocket();
serverSocket.setReuseAddress(true);
serverSocket.bind(new InetSocketAddress(8800));
-------
接收和关闭与客户的连接
ServerSocket的accept()方法从连接请求队列中取出一个客户的连接请求,然后创建与客户连接的Socket对象;如果这时将请求放入多线程中处理,其他请求就不需要等待。
while(true){
Socket socket = null;
try {
socket = serverSocket.accept();
System.out.println("new connection accepted"+
socket.getInetAddress()+":"+socket.getPort());
BufferedReader br = getReader(socket);
PrintWriter pw = getWriter(socket);

String msg = null;

while((msg = br.readLine()) != null){
System.out.println(msg);
pw.println(echo(msg));
if(msg.equals("bye")){
break;
}
}

} catch (Exception e) {
// TODO: handle exception
}finally {
if(socket != null){
socket.close();//关闭单个用户与服务之间的连接,这里是放在try-catch-finally语句,不影响其他客户的连接
}
}
}
----------
关闭ServerSocket;
若关闭ServerSocket,断开所有客户的连接
-----
获取ServerSocket信息
若服务端使用匿名端口(port=0);可以通过ServerSocket中的getLocalPort()获取系统给服务器分配的端口;多次启用服务器,分配的端口可能都不同。
匿名端口适用于服务器与客户之间的临时通信,通信结束,就端口连接,并且ServerSocket占用的临时端口也被释放。(客户端如何获取服务器端口呢)
FTP(文件传输协议)就使用了匿名端口。
FTP协议用于在本地文件系统与远程文件系统之间传送文件。
FTP使用两个并行的TCP连接:一个控制连接,一个是数据连接。控制连接用于在客户和服务器之间发送控制信息,如用户和口令、改变远程目录的命令或上传和下载命令。数据连接用于传送文件。TCP服务器再21端口上监听控制连接,如果有客户要求上传或下载文件,就另外建立一个数据连接,通过它来传送文件。
--------
创建多线程服务器:
用并发性能来衡量一个服务器同时响应多个客户的能力,一个具有好的并发性能的服务器,必须符合两个条件:
能同时接收并处理多个客户连接;
对于每个客户,都会迅速给予响应。
服务器同时处理的客户连接数目越多,并且对每个客户作出响应越快,就并发性能越高。
用多个线程来同时为多个客户提供服务,这是提高服务器的并发性能最常用的手段。
1、为每个客户分配一个工作线程。
2、创建一个线程池,由其中的工作线程为客户服务。
3、利用JDK的java类库现成的线程池,由它的工作线程为客户服务。
----
为每个客户分配一个工作线程。
while(true){
Socket socket = null;
try {
socket = serverSocket.accept();
Thread workThread = new Thread(new Handler(socket));//线程处理类new Handler
workThread.start();//启动线程
} catch (Exception e) {
// TODO: handle exception
}finally {
if(socket != null){
socket.close();
}
}
}
---
class Handler(Socket socket) implements Runnable{
socket与客户之间的通信;
}
----------
创建线程池;在java网络编程的P70页中;
线程池的好处就是:频繁创建、销毁线程销毁系统资源,当某一时期访问量变大,创建线程过多,导致内存不足,系统可能会崩溃。一般操作系统、java虚拟机中的线程切换是20毫秒。
---
非阻塞的原理,当某个线程阻塞了,正在读取数据,那么先让出cpu资源,让其他等待的线程执行。
线程池,服务器接收一个任务,创建一个线程负责这个任务处理,并且将这个线程放入线程池中,如果线程池满了,就不在接收任务。
非阻塞的通信机制主要由java包(新I/O包)的类实现,主要的类包括ServerSocketChannel、SocketChannel、Selector、SelectionKey和ByteBuffer等。
---
线程阻塞的概念:线程在运行中因为某些原因而阻塞。所有处于阻塞状态的线程的共同特征是:放弃CPU,暂停运行,只有等到导致阻塞的原因消除,才能恢复运行。
线程阻塞的原因:
1、线程执行了Thread.sleep(int n)方法,线程放弃CPU,睡眠n毫秒,然后恢复运行。
2、线程要执行一段同步代码,由于无法获得相关的同步锁,只要进入阻塞状态,等到获得了同步锁,才能恢复运行。
3、线程执行了一个对象的wait()方法,进入阻塞状态,只有等到其他线程执行了该对象的notify()或notifyAll()方法,才可能将其唤醒。
4、线程执行I/O操作或进行远程通信时,会因为等待相关的资源而进入阻塞状态。
-------
进行远程通信时,在客户程序中,线程在以下情况可能进入阻塞状态。
1、请求与服务器建立连接时,即当线程执行Socket的带参数构造方法,或执行Socket的Connect()方法时,会进入阻塞状态,此线程才从Socket的构造方法或Connect()方法返回。
2、线程从Socket的输入流读入数据时,如果没有足够的数据,就会进入阻塞状态,直到读到了足够数据,或者达到输入流的末尾,或者出现了异常才从输入流的read()方法返回或异常中断。输入流中有多少数据才算足够呢?这要看线程执行的read()方法的类型。
1)、int read():只要输入流中有一个字节,就算足够。
2)、int read(byte[] buff):只要输入流中的字节数目与参数buff数组的长度相同,就算足够。
3)、String readLine():只要输入流有一行字符串,就算足够。值得注意的是,InoutStream类并没有readLine()方法,在过滤流BufferedReader类中才有此方法。
----
服务器端线程可能产生的阻塞:
1、线程执行ServerSocket的accept方法时,等待客户的连接,直到接收到了客户连接,才从accept()方法返回。
2、线程Socket的输入流读入数据时,如果输入流没有足够的数据,就会进入阻塞状态。
3、线程向Socket的输出流写一批数据时,可能会进入阻塞状态,等到输出了所有数据,或者出现异常,才从输出流的write()方法返回或异常中断。
------
服务器程序用多线程来处理阻塞I/O,尽管能满足同时响应多个客户请求的需求,但是有以下局限:
1、java虚拟机会为每个线程分配独立的堆栈空间,工作线程数目越多,系统开销就越大,而且增加了java虚拟机调度线程的负担,增强了线程之间同步复杂性,提高了线程死锁的可能性。
2、工作线程的许多时间都浪费在阻塞I/O操作上,java虚拟机需要频繁地转让CPU的使用权,使进入阻塞状态的线程放弃CPU,再把CPU分配给处于可运行状态的线程。
----------
非阻塞I/O,服务器程序只需要一个线程就能同时负责接收客户的连接,接收各个客户发送的数据,以及向各个客户发送响应数据。服务程序的处理的流程:
while(一直等待,直到有接收连接就绪的事件、读就绪事件或写就绪事件发生){
if(有客户连接)
接收客户的连接;
if(某个Socket的输入流中有可读数据)
从输入流中读数据;
if(某个Socket的输出流可以写数据)
向输出流写数据;
}
以上处理流程采用了轮询的工作方式,当某一种操作就绪时,就执行该操作,否则就查看是否还有其他就绪的操作可以执行。
为了使轮询的工作方式顺利进行,接收客户连接、从输入流读数据,以及向输出流写数据的操作都应该以非阻塞的方式运行。

ServerSocket详解及线程阻塞_03的更多相关文章

  1. (转)深入详解Java线程池——Executor框架

    转:https://yq.aliyun.com/articles/633782?utm_content=m_1000015330 在Java中,使用线程来异步执行任务.Java线程的创建与销毁需要一定 ...

  2. 【面试】详解同步/异步/阻塞/非阻塞/IO含义与案例

    本文详解同步.异步.阻塞.非阻塞,以及IO与这四者的关联,毕竟我当初刚认识这几个名词的时候也是一脸懵. 目录 1.同步阻塞.同步非阻塞.异步阻塞.异步非阻塞 1.同步 2.异步 3.阻塞 4.非阻塞 ...

  3. 单例模式,reorder详解,线程安全,双检查锁

    单例模式,分为饿汉式单例 和 懒汉式单例. 先把本类对象所需内存在main函数执行前就new出来,这是饿汉式单例. 个人思考: 为什么饿汉式不独霸天下,还有什么必要去研究使用cpp11上支持的双检查锁 ...

  4. java多线程详解(7)-线程池的使用

    在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题: 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了, 这样频繁创建线程就会大大降低系 ...

  5. java多线程详解(3)-线程的互斥与同步

    前言:前一篇文章主要描述了多线程中访成员变量与局部变量问题,我们知道访成员变量有线程安全问题,在多线程程序中 我们可以通过使用synchronized关键字完成线程的同步,能够解决部分线程安全问题 在 ...

  6. 009-ThreadPoolExecutor运转机制详解,线程池使用1-newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor、newScheduledThreadPool

    一.ThreadPoolExecutor理解 为什么要用线程池: 1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务. 2.可以根据系统的承受能力,调整线程池中工作线线程的数 ...

  7. Java Thread.join()详解--父线程等待子线程结束后再结束

    目录(?)[+] 阅读目录 一.使用方式. 二.为什么要用join()方法 三.join方法的作用 join 四.用实例来理解 打印结果: 打印结果: 五.从源码看join()方法   join是Th ...

  8. Java ServerSocket详解

    ServerSocket 构造方法 ServerSocket serverSocket = new ServerSocket(); ServerSocket(); //无参数 ServerSocket ...

  9. java多线程详解(6)-线程间的通信wait及notify方法

    Java多线程间的通信 本文提纲 一. 线程的几种状态 二. 线程间的相互作用 三.实例代码分析 一. 线程的几种状态 线程有四种状态,任何一个线程肯定处于这四种状态中的一种:(1). 产生(New) ...

随机推荐

  1. MongoDB连接

    1. import pymongo client = pymongo.MongoClient(host='localhost',port=27017) 2. client=MongoClient('m ...

  2. ios-时间换算

    经常会遇到时间转换的,在此收藏一个时间换算的方法〜 #pragma mark 时间换算 + (NSString *)setcreateTime:(NSString *)str { //yyyy-MM- ...

  3. What You Can Learn from Actifio Logs

    The Actifio services generate many logs, some of which are useful for troubleshooting. This section ...

  4. visual studio 2017 中默认无法开发 Android 8.0 及以上系统的解决方案

    一般默认比较旧有两个原因,系统版本过旧,Visual Studio 版本过旧. 第一步,将windows 更新到最新版,必须是windows 10 并且更新到最新. 第二步,将visual studi ...

  5. 3-Longest Substring Without Repeating Characters @LeetCode

    3-Longest Substring Without Repeating Characters @LeetCode 题目 题目中得到的信息有: 一段字符串找出不重复子串的最大长度,只需要长度信息. ...

  6. 导出excel,并将数据返回给前端(包含权限判断)

    一.先写导出按钮接口 1.此接口对用户权限进行判断 2.此接口将前端的参数组合拼凑到下一个接口的url中去,用于条件筛选 3.用户有权限的情况下将用户的权限信息保存到redis中去,并将token写到 ...

  7. Python assert(断言)

    Python assert(断言)可以分别后面的判断是否正确,如果错误会报错 示例: a = 1 assert type(a) is int print('No problem') 输出结果: No ...

  8. from组件

    目录 一.生成页面可用的 HTML标签 二.对用户提交的数据进行校验 三. form 综合示例: 四. modelform(自动根据字段生成表单) 五.modelformset 一.生成页面可用的 H ...

  9. 关闭linux服务器防火墙

    --全部关闭  systemctl stop firewalld.service  #停止firewallsystemctl disable firewalld.service  #禁止firewal ...

  10. celery+Rabbit MQ实战记录

    基于以前的一篇文章,celery+Rabbit MQ的安装和使用, 本文更加详细的介绍如何安装和使用celey, Rabbit MQ. 并记录在使用celery时遇到的一些问题. 1.安装 Rabbi ...