热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

[Java]详解Socket和ServerSocket学习笔记

对于即时类应用或者即时类的游戏,HTTP协议很多时候无法满足于我们的需求。这会,Socket对于我们来说就非常实用了。下面是本次学习的笔记。

对于即时类应用或者即时类的游戏,HTTP协议很多时候无法满足于我们的需求。这会,Socket对于我们来说就非常实用了。下面是本次学习的笔记。主要分异常类型、交互原理、Socket、ServerSocket、多线程这几个方面阐述。

异常类型

在了解Socket的内容之前,先要了解一下涉及到的一些异常类型。以下四种类型都是继承于IOException,所以很多之后直接弹出IOException即可。

UnkownHostException:   主机名字或IP错误

ConnectException:  服务器拒绝连接、服务器没有启动、(超出队列数,拒绝连接)

SocketTimeoutException: 连接超时

BindException:  Socket对象无法与制定的本地IP地址或端口绑定

交互过程

Socket与ServerSocket的交互,下面的图片我觉得已经说的很详细很清楚了。

Socket

构造函数

Socket()

Socket(InetAddress address, int port)throws UnknownHostException, IOException

Socket(InetAddress address, int port, InetAddress localAddress, int localPort)throws IOException

Socket(String host, int port)throws UnknownHostException, IOException

Socket(String host, int port, InetAddress localAddress, int localPort)throws IOException

除去第一种不带参数的之外,其它构造函数会尝试建立与服务器的连接。如果失败会抛出IOException错误。如果成功,则返回Socket对象。

InetAddress是一个用于记录主机的类,其静态getHostByName(String msg)可以返回一个实例,其静态方法getLocalHost()也可以获得当前主机的IP地址,并返回一个实例。Socket(String host, int port, InetAddress localAddress, int localPort)构造函数的参数分别为目标IP、目标端口、绑定本地IP、绑定本地端口。

Socket方法

getInetAddress();      远程服务端的IP地址

getPort();          远程服务端的端口

getLocalAddress()      本地客户端的IP地址

getLocalPort()        本地客户端的端口

getInputStream();     获得输入流

getOutStream();      获得输出流

值得注意的是,在这些方法里面,最重要的就是getInputStream()和getOutputStream()了。

Socket状态

isClosed();            //连接是否已关闭,若关闭,返回true;否则返回false

isConnect();      //如果曾经连接过,返回true;否则返回false

isBound();            //如果Socket已经与本地一个端口绑定,返回true;否则返回false

如果要确认Socket的状态是否处于连接中,下面语句是很好的判断方式。

boolean isCOnnection=socket.isConnected() && !socket.isClosed();  //判断当前是否处于连接

半关闭Socket

很多时候,我们并不知道在获得的输入流里面到底读多长才结束。下面是一些比较普遍的方法:

  • 自定义标识符(譬如下面的例子,当受到“bye”字符串的时候,关闭Socket)
  • 告知读取长度(有些自定义协议的,固定前几个字节表示读取的长度的)
  • 读完所有数据
  • 当Socket调用close的时候关闭的时候,关闭其输入输出流

ServerSocket

构造函数

ServerSocket()throws IOException

ServerSocket(int port)throws IOException

ServerSocket(int port, int backlog)throws IOException

ServerSocket(int port, int backlog, InetAddress bindAddr)throws IOException

注意点:

1. port服务端要监听的端口;backlog客户端连接请求的队列长度;bindAddr服务端绑定IP

2. 如果端口被占用或者没有权限使用某些端口会抛出BindException错误。譬如1~1023的端口需要管理员才拥有权限绑定。

3. 如果设置端口为0,则系统会自动为其分配一个端口;

4. bindAddr用于绑定服务器IP,为什么会有这样的设置呢,譬如有些机器有多个网卡。

5. ServerSocket一旦绑定了监听端口,就无法更改。ServerSocket()可以实现在绑定端口前设置其他的参数。

 单线程的ServerSocket例子

public void service(){
  while(true){
    Socket socket=null;
    try{
      socket=serverSocket.accept();//从连接队列中取出一个连接,如果没有则等待
      System.out.println("新增连接:"+socket.getInetAddress()+":"+socket.getPort());
      ...//接收和发送数据
    }catch(IOException e){e.printStackTrace();}finally{
      try{
        if(socket!=null) socket.close();//与一个客户端通信结束后,要关闭Socket
      }catch(IOException e){e.printStackTrace();}
    }
  }
}

多线程的ServerSocket

多线程的好处不用多说,而且大多数的场景都是多线程的,无论是我们的即时类游戏还是IM,多线程的需求都是必须的。下面说说实现方式:

  • 主线程会循环执行ServerSocket.accept();
  • 当拿到客户端连接请求的时候,就会将Socket对象传递给多线程,让多线程去执行具体的操作;

实现多线程的方法要么继承Thread类,要么实现Runnable接口。当然也可以使用线程池,但实现的本质都是差不多的。

 这里举例:

下面代码为服务器的主线程。为每个客户分配一个工作线程:

public void service(){
  while(true){
    Socket socket=null;
    try{
      socket=serverSocket.accept();            //主线程获取客户端连接
      Thread workThread=new Thread(new Handler(socket));  //创建线程
      workThread.start();                  //启动线程
    }catch(Exception e){
      e.printStackTrace();
    }
  }
}

 当然这里的重点在于如何实现Handler这个类。Handler需要实现Runnable接口:

class Handler implements Runnable{
  private Socket socket;
  public Handler(Socket socket){
    this.socket=socket;
  }
  
  public void run(){
    try{
      System.out.println("新连接:"+socket.getInetAddress()+":"+socket.getPort());
      Thread.sleep(10000);
    }catch(Exception e){e.printStackTrace();}finally{
      try{
        System.out.println("关闭连接:"+socket.getInetAddress()+":"+socket.getPort());
        if(socket!=null)socket.close();
      }catch(IOException e){
        e.printStackTrace();
      }
    }
  }
}

当然是先多线程还有其它的方式,譬如线程池,或者JVM自带的线程池都可以。这里就不说明了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • Docker的安全基准
    nsitionalENhttp:www.w3.orgTRxhtml1DTDxhtml1-transitional.dtd ... [详细]
  • 本文介绍如何解决在 IIS 环境下 PHP 页面无法找到的问题。主要步骤包括配置 Internet 信息服务管理器中的 ISAPI 扩展和 Active Server Pages 设置,确保 PHP 脚本能够正常运行。 ... [详细]
  • 1:有如下一段程序:packagea.b.c;publicclassTest{privatestaticinti0;publicintgetNext(){return ... [详细]
  • 本文介绍了如何在具备多个IP地址的FTP服务器环境中,通过动态地址端口复用和地址转换技术优化网络配置。重点讨论了2Mb/s DDN专线连接、Cisco 2611路由器及内部网络地址规划。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 创建第一个 MUI 移动应用项目
    本文将详细介绍如何使用 HBuilder 创建并运行一个基于 MUI 框架的移动应用项目。我们将逐步引导您完成项目的搭建、代码编写以及真机调试,帮助您快速入门移动应用开发。 ... [详细]
  • 深入理解 SQL 视图、存储过程与事务
    本文详细介绍了SQL中的视图、存储过程和事务的概念及应用。视图为用户提供了一种灵活的数据查询方式,存储过程则封装了复杂的SQL逻辑,而事务确保了数据库操作的完整性和一致性。 ... [详细]
  • 梦幻西游挖图奇遇:70级项链意外触发晶清诀,3000W轻松到手
    在梦幻西游中,挖图是一项备受欢迎的活动,无论是小宝图还是高级藏宝图,都吸引了大量玩家参与。通常情况下,小宝图的数量保证了稳定的收益,但特技装备的出现往往能带来意想不到的惊喜。本文讲述了一位玩家通过挖图获得70级晶清项链的故事,最终实现了3000W的游戏币逆袭。 ... [详细]
  • 本文探讨了 RESTful API 和传统接口之间的关键差异,解释了为什么 RESTful API 在设计和实现上具有独特的优势。 ... [详细]
  • 本文详细介绍了Java编程语言中的核心概念和常见面试问题,包括集合类、数据结构、线程处理、Java虚拟机(JVM)、HTTP协议以及Git操作等方面的内容。通过深入分析每个主题,帮助读者更好地理解Java的关键特性和最佳实践。 ... [详细]
  • 如何配置Unturned服务器及其消息设置
    本文详细介绍了Unturned服务器的配置方法和消息设置技巧,帮助用户了解并优化服务器管理。同时,提供了关于云服务资源操作记录、远程登录设置以及文件传输的相关补充信息。 ... [详细]
  • 网络攻防实战:从HTTP到HTTPS的演变
    本文通过一系列日记记录了从发现漏洞到逐步加强安全措施的过程,探讨了如何应对网络攻击并最终实现全面的安全防护。 ... [详细]
  • MQTT技术周报:硬件连接与协议解析
    本周开发笔记重点介绍了在新项目中使用MQTT协议进行硬件连接的技术细节,涵盖其特性、原理及实现步骤。 ... [详细]
  • UNP 第9章:主机名与地址转换
    本章探讨了用于在主机名和数值地址之间进行转换的函数,如gethostbyname和gethostbyaddr。此外,还介绍了getservbyname和getservbyport函数,用于在服务器名和端口号之间进行转换。 ... [详细]
  • 邮件(带附件,模拟文件上传,跨服务器)发送核心代码1.测试邮件发送附件接口***测试邮件发送附件*@parammultipartFile*@return*@RequestMappi ... [详细]
author-avatar
yax何
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有