zoukankan      html  css  js  c++  java
  • Java--Socket通信

    下面内容是Java开发内容的高级知识点,需要对Java中的面向对象、IO、多线程、以及网络相关知识有一定的基础。(知识永远都有深度,本章节长期更新内容)

    1、网络基础知识

    网络通信的条件:1、两个通信的端都要有各自的IP地址作为唯一标识,简单的来说IP地址用来区分不同机器(计算机)。2、语言要相通。3、如何辨别不同程序的通信需要端口号来区别,简单的来说端口号就是用来标识不同的应用程序。

    TCP/IP是目前世界上使用最广泛的协议,是以TCP和IP为基础的不同层次上多个协议的集合,也称TCP/IP协议族 或 TCP/IP 协议栈。

    TCP:Transmission Control Protocol 传输控制协议
    IP:Internet Protocol 互联网协议

    TCP/IP模型

    >>IP和端口<<
    1、用于区分不同应用程序。
    2、端口号范围为0~65535,其中0~1023为系统所保留。如果自定义端口号,所以建议用1024之后的端口号。
    3、IP地址和端口号组成了所谓的Socket,Socket是网络上运行程序之间双向通信链路的终结点,是TCP和UDP的基础。


    常用的端口号需要记一下:http:80    ftp:21    telnet:23

    ——————————Java中的网络支持—————————
    针对网络通信的不同层次,Java提供的网络功能有四大类:
    >>1、InetAddress:用于标识网络上的硬件资源
    >>2、URL:统一资源定位符 通过URL可以直接读取或写入网络上的数据
    >>3、Socket:使用TCP协议实现网络通信的Socket相关的类。
    >>4、Datagram:使用UDP协议,将数据保存在数据报中,通过网络进行通信。


    2、InetAddress类

    查看I-net-Address的API文档,发现没有构造方法,也就是不能通过new来创建。所以肯定有静态的方法来创建。

     1 import java.net.InetAddress;
     2 import java.net.UnknownHostException;
     3 import java.util.Arrays;
     4 
     5 public class Test1{
     6     public static void main(String[] args) throws UnknownHostException{
     7         // 获取本机的InetAdresss实例
     8         InetAddress address = InetAddress.getLocalHost();
     9         System.out.println("计算机名:"+address.getHostName()+"
    IP地址:"+address.getHostAddress());
    10 
    11         // 获取字节数组形式的IP地址
    12         byte[] bytes = address.getAddress();
    13         System.out.println("字节数组形式的IP:"+Arrays.toString(bytes));
    14         System.out.println(address);
    15 
    16         // 也可以通过机器名来获取InewAdress
    17         InetAddress address2 = InetAddress.getByName("MacBook-Air-2.local");
    18         System.out.println("通过计算机名字创建的InetAddress对象:"+address2);
    19         System.out.println("计算机名:"+address2.getHostName());
    20         System.out.println("IP地址:"+address2.getHostAddress());
    21 
    22         // 也可以通过IP地址来获取InewAdress
    23         InetAddress address3 = InetAddress.getByName("192.168.1.102");
    24         System.out.println("通过计算机IP地址创建的InetAddress对象:"+address3);
    25         System.out.println("计算机名:"+address3.getHostName());
    26         System.out.println("IP地址:"+address3.getHostAddress());
    27 
    28 
    29     }
    30 }

    输出结果:

     
    3、URL

     URL(Uniform Resource Locator)统一资源定位符,表示Internet上某一资源的地址。 俗称就是网址。

     URL由两部分组成:协议名称+资源名称。

     在Java.net包中,提供了URL类来表示URL。

     1 import java.net.MalformedURLException;
     2 import java.net.URL;
     3 
     4 public class Test1{
     5     public static void main(String[] args){
     6         
     7         try {
     8             // 创建一个URL实例
     9             URL imoocURL = new URL("http://www.imooc.com");
    10             URL url = new URL(imoocURL,"/index.html?username=tom#test");
    11             // ?后面表示参数,#后面表示的是锚点
    12 
    13             // 创建URL对象之后,可以根据这个对象获取相关的信息
    14             System.out.println("协议:"+url.getProtocol());
    15             System.out.println("主机:"+url.getHost());
    16             // 如果未指定端口号,则使用默认的端口号,此时getPort()方法返回值为-1
    17             System.out.println("端口:"+url.getPort());
    18             System.out.println("文件路径:"+url.getPath());
    19             System.out.println("文件名:"+url.getFile());
    20             System.out.println("相对路径:"+url.getRef());// 实际上就是#锚点后面的内容
    21             System.out.println("查询字符串:"+url.getQuery());
    22 
    23         } catch (MalformedURLException e) {
    24             e.printStackTrace();
    25         }
    26 
    27 
    28     }
    29 }

    输出:

     下面再通过URL读取网页内容:

     1 import java.net.MalformedURLException;
     2 import java.net.URL;
     3 import java.io.InputStream;
     4 import java.io.InputStreamReader;
     5 import java.io.BufferedReader;
     6 import java.io.IOException;
     7 
     8 /*
     9  * 使用URL读取网页页面内容
    10  */
    11 public class Test1{
    12     public static void main(String[] args){
    13         
    14         try {
    15             // 创建一个URL实例
    16             URL url = new URL("http://www.baidu.com");
    17             // 通过URL的openStream方法获取URL对象所表示的资源的字节输入流
    18             InputStream is = url.openStream();
    19             // 将字节输入流转换为字符输入流
    20             InputStreamReader isr = new InputStreamReader(is,"utf-8");// 如果没有指明编码可能会出现中文乱码
    21             // 为字符输入流添加缓冲
    22             BufferedReader br = new BufferedReader(isr);
    23             String data = br.readLine();// 读取数据
    24             while(data != null){
    25                 System.out.println(data);// 输出数据
    26                 data = br.readLine();
    27             }
    28             // 最后按照上面对象倒序关闭
    29             br.close();
    30             isr.close();
    31             is.close();
    32         } catch (MalformedURLException e) {
    33             e.printStackTrace();
    34         } catch (IOException e) {
    35             e.printStackTrace();
    36         } 
    37     }
    38 }

    输入:


    4、TCP编程

    4-1、Socket简介

    TCP协议是面向连接、可靠的、有序的,以字节流的方式发送数据
    基于TCP协议实现网络通信的类:
    >>1、客户端的Socket类
    >>2、服务器端的ServerSocket类

    基于Socket的TCP通信模型

     

     Socket通信实现步骤:1、创建ServerSocket和Socket。2、打开连接到Socket的输入/输出流。3、按照协议对Socket进行读/写操作。4、关闭输入输出流、关闭Socket。
    通过在线API文档:http://tool.oschina.net/apidocs/apidoc?api=jdk-zh 中查询到的结果:

    4-2、编程实现基于TCP的Socket服务器端和客户端的通信

    服务器端:
        1、创建ServerSocket对象,绑定监听端口。
        2、通过accept()方法监听客户端请求。
        3、链接建立后,通过输入流读取客户端发送的请求信息。
        4、通过输出流向客户端发送响应信息。
        5、关闭相关资源。

     1 package com.heyang;
     2 
     3 import java.io.BufferedReader;
     4 import java.io.IOException;
     5 import java.io.InputStream;
     6 import java.io.InputStreamReader;
     7 import java.net.ServerSocket;
     8 import java.net.Socket;
     9 
    10 /*
    11  * 基于TCP协议的Socket通信,实现用户登录
    12  * 服务器端
    13  */
    14 public class Server {
    15     public static void main(String[] args) {
    16         try {
    17             // 1、创建一个服务器Socket,即ServerSocket,指定绑定的端口,并监听此端口
    18             ServerSocket serverSocket = new ServerSocket(8888);
    19             // 2、调用()方法开始监听,等待客户端的连接
    20             System.out.println("***服务器即将启动,等待客户端的连接***");
    21             Socket socket = serverSocket.accept();// 就会处于阻塞的状态,等待监听
    22             // 3、获取输入流,病读取客户端信息
    23             InputStream is = socket.getInputStream();// 字节输入流
    24             // 将字节流转换为字符流
    25             InputStreamReader isr = new InputStreamReader(is);
    26             // 为输入流添加缓冲
    27             BufferedReader br = new BufferedReader(isr);
    28             String info = null;
    29             while((info = br.readLine())!=null){
    30                 System.out.println("我是服务器,读取客户端发过来的信息:"+info);
    31             }
    32             socket.shutdownInput();//关闭输入流
    33             
    34             // 关闭资源
    35             br.close();
    36             isr.close();
    37             is.close();
    38             socket.close();
    39             serverSocket.close();
    40             
    41         } catch (IOException e) {
    42             // TODO Auto-generated catch block
    43             e.printStackTrace();
    44         }
    45     }
    46 }



    客户端:
        1、创建Socket对象,指明需要连接的服务器的地址和端口号。
        2、连接建立后,通过输出流向服务器端发送请求信息。
        3、通过输入流获取服务器响应的信息。
        4、关闭相关资源。

     1 package com.heyang;
     2 
     3 import java.io.IOException;
     4 import java.io.OutputStream;
     5 import java.io.PrintWriter;
     6 import java.net.Socket;
     7 import java.net.UnknownHostException;
     8 
     9 /*
    10  * 客户端
    11  */
    12 public class Client {
    13     public static void main(String[] args) {
    14         // 1、创建客户端Socket,指定服务器地址和端口
    15         try {
    16             Socket  socket = new Socket("localhost", 8888);
    17             // 2、获取输出流,向服务器端发送信息
    18             OutputStream os = socket.getOutputStream();// 获取字节输出流
    19             // 将输出流包装为打印流
    20             PrintWriter pw = new PrintWriter(os);
    21             pw.write("用户名:admin 密码:123");
    22             pw.flush();
    23             socket.shutdownInput();//关闭输出流
    24             
    25             // 3、关闭资源
    26             pw.close();
    27             os.close();
    28             socket.close();
    29             
    30         } catch (UnknownHostException e) {
    31             // TODO Auto-generated catch block
    32             e.printStackTrace();
    33         } catch (IOException e) {
    34             // TODO Auto-generated catch block
    35             e.printStackTrace();
    36         }
    37     }
    38 }

    输出:

     4-3、完善客户端登陆之后服务器响应客户端

    代码逻辑深化分析:

      在前面简单的代码的基础上,我们需要完善客户端登陆之后服务器响应客户端的逻辑。

      事实上,站在客户端的角度,对外(这里指的是服务器)发出数据流,也就是需要用输出流来输出数据流。

      反过来,站在服务器端的角度,就要不断的监听外部(这里指的是外部客户端)输入进来的数据流,所以就需要输入流来接收输入进来的数据流。

      那么,当服务器端收到客户端传输过来的数据流之后,就应该响应,那么这时候,站在服务器端的角度,要对外进行响应,就需要用输出流来输出响应回馈的信息,这时候就和客户端一开始的输出流代  码一致了。

      回到客户端,由于需要接收和读取服务器端的发出的响应,就需要输入流来接收服务器发过来的回馈信息了,这时候就和服务器端一开始的输入流代码一致了。

    服务器代码:

     1 package com.heyang;
     2 
     3 import java.io.BufferedReader;
     4 import java.io.IOException;
     5 import java.io.InputStream;
     6 import java.io.InputStreamReader;
     7 import java.io.OutputStream;
     8 import java.io.PrintWriter;
     9 import java.net.ServerSocket;
    10 import java.net.Socket;
    11 
    12 /*
    13  * 基于TCP协议的Socket通信,实现用户登录
    14  * 服务器端
    15  */
    16 public class Server {
    17     public static void main(String[] args) {
    18         try {
    19             // 1、创建一个服务器Socket,即ServerSocket,指定绑定的端口,并监听此端口
    20             ServerSocket serverSocket = new ServerSocket(8888);
    21             // 2、调用()方法开始监听,等待客户端的连接
    22             System.out.println("***服务器即将启动,等待客户端的连接***");
    23             Socket socket = serverSocket.accept();// 就会处于阻塞的状态,等待监听
    24             // 3、获取输入流,病读取客户端信息
    25             InputStream is = socket.getInputStream();// 字节输入流
    26             // 将字节流转换为字符流
    27             InputStreamReader isr = new InputStreamReader(is);
    28             // 为输入流添加缓冲
    29             BufferedReader br = new BufferedReader(isr);
    30             String info = null;
    31             while((info = br.readLine())!=null){
    32                 System.out.println("我是服务器,读取客户端发过来的信息:"+info);
    33             }
    34             socket.shutdownInput();//关闭输入流
    35             
    36             // 4、作为服务器端,就需要响应客户端的请求,使用输出流来响应
    37             OutputStream os = socket.getOutputStream();
    38             PrintWriter pw = new PrintWriter(os);
    39             pw.write("欢迎您!");
    40             pw.flush();//调用flush()方法将缓冲输出
    41             
    42             
    43             // 5、关闭资源
    44             pw.close();
    45             os.close();
    46             br.close();
    47             isr.close();
    48             is.close();
    49             socket.close();
    50             serverSocket.close();
    51             
    52         } catch (IOException e) {
    53             // TODO Auto-generated catch block
    54             e.printStackTrace();
    55         }
    56     }
    57 }

    客户端代码:

     1 package com.heyang;
     2 
     3 import java.io.BufferedReader;
     4 import java.io.IOException;
     5 import java.io.InputStream;
     6 import java.io.InputStreamReader;
     7 import java.io.OutputStream;
     8 import java.io.PrintWriter;
     9 import java.net.Socket;
    10 import java.net.UnknownHostException;
    11 
    12 /*
    13  * 客户端
    14  */
    15 public class Client {
    16     public static void main(String[] args) {
    17         // 1、创建客户端Socket,指定服务器地址和端口
    18         try {
    19             Socket  socket = new Socket("localhost", 8888);
    20             // 2、获取输出流,向服务器端发送信息
    21             OutputStream os = socket.getOutputStream();// 获取字节输出流
    22             // 将输出流包装为打印流
    23             PrintWriter pw = new PrintWriter(os);
    24             pw.write("用户名:admin 密码:123");
    25             pw.flush();
    26             socket.shutdownOutput();//关闭输出流
    27             
    28             // 3、获取输入流,并读取服务器端的响应信息
    29             InputStream is = socket.getInputStream();
    30             BufferedReader br = new BufferedReader(new InputStreamReader(is));
    31             String info = null;
    32             while((info = br.readLine())!=null){
    33                 System.out.println("我是客户端,服务器跟我说:"+info);
    34             }
    35             
    36             // 4、关闭资源
    37             br.close();
    38             is.close();
    39             pw.close();
    40             os.close();
    41             socket.close();
    42             
    43         } catch (UnknownHostException e) {
    44             // TODO Auto-generated catch block
    45             e.printStackTrace();
    46         } catch (IOException e) {
    47             // TODO Auto-generated catch block
    48             e.printStackTrace();
    49         }
    50     }
    51 }

    具体运行结果就不展示了。

    4-4、使用多线程实现多客户端的通信

     事实上,以上只实现了单个客户端和单个服务器端进行socket通信。那么问题来了,实际应用程序是由一个服务器持续不断的运行中,然后由多个客户端异步通过服务器进行客户端之间的收发信息,这该如何实现呢?

     基本步骤:

      1、服务器端创建ServerSocket,循环调用accept()等待客户端连接。

      2、客户端创建一个socket并请求和服务器端连接。

      3、服务器端接受客户端请求,创建socket与该客户端建立专线连接。

      4、建立连接的两个socket在一个单独的线程上对话。

      5、服务器端继续等待新的连接。

    服务器端的代码:

     1 package com.heyang;
     2 
     3 
     4 import java.io.IOException;
     5 import java.net.InetAddress;
     6 import java.net.ServerSocket;
     7 import java.net.Socket;
     8 import com.heyang.ServerThread;;
     9 /*
    10  * 基于TCP协议的Socket通信,实现用户登录
    11  * 服务器端
    12  */
    13 public class Server {
    14     public static void main(String[] args) {
    15         try {
    16             // 1、创建一个服务器Socket,即ServerSocket,指定绑定的端口,并监听此端口
    17             ServerSocket serverSocket = new ServerSocket(8888);
    18             // 2、调用()方法开始监听,等待客户端的连接
    19             
    20             // 记录客户端的数量
    21             int count = 0;
    22             System.out.println("***服务器即将启动,等待客户端的连接***");
    23             
    24             while(true){
    25                 // 调用accept()方法开始监听,等待客户端的链接
    26                 Socket socket = serverSocket.accept();
    27                 // 创建一个新的线程
    28                 ServerThread serverThread = new ServerThread(socket);
    29                 // 启动线程·
    30                 serverThread.start();
    31                 
    32                 count++;
    33                 System.out.println("客户端连接的数量:"+count+"个");
    34                 
    35                 // 获取客户端的IP地址等信息
    36                 InetAddress address = socket.getInetAddress();
    37                 System.out.println("当前客户端的IP:"+address.getHostAddress());
    38                 
    39             }
    40             
    41             // 需要死循环持续监听客户端的信息发送
    42 //            serverSocket.close();
    43             
    44         } catch (IOException e) {
    45             // TODO Auto-generated catch block
    46             e.printStackTrace();
    47         }
    48     }
    49 }

    服务器线程代码:

     1 package com.heyang;
     2 
     3 import java.io.BufferedReader;
     4 import java.io.IOException;
     5 import java.io.InputStream;
     6 import java.io.InputStreamReader;
     7 import java.io.OutputStream;
     8 import java.io.PrintWriter;
     9 import java.net.Socket;
    10 
    11 /*
    12  * 服务器端 线程处理类 
    13  */
    14 public class ServerThread extends Thread {
    15     // 创建和本线程相关的socket
    16     Socket socket = null;
    17     
    18     public ServerThread(Socket socket){
    19         this.socket = socket;
    20     }
    21     
    22     // 指向线程的操作,响应服务器端的请求
    23     public void run(){
    24         
    25         InputStream is = null;
    26         InputStreamReader isr = null;
    27         BufferedReader br = null;
    28         OutputStream os = null;
    29         PrintWriter pw = null;
    30         try {
    31             // 3、获取输入流,病读取客户端信息
    32             is = socket.getInputStream();// 字节输入流
    33             // 将字节流转换为字符流
    34             isr = new InputStreamReader(is);
    35             // 为输入流添加缓冲
    36             br = new BufferedReader(isr);
    37             String info = null;
    38             while ((info = br.readLine()) != null) {
    39                 System.out.println("我是服务器,读取客户端发过来的信息:" + info);
    40             }
    41             socket.shutdownInput();//关闭输入流
    42 
    43             // 获取输出流
    44             os = socket.getOutputStream();
    45             pw = new PrintWriter(os);
    46             pw.write("欢迎您!");
    47             pw.flush();//调用flush()方法将缓冲输出
    48         } catch (Exception e) {
    49             // TODO: handle exception
    50         }finally{
    51             try {
    52                 // 5、关闭资源
    53                 if (pw != null) {
    54                     pw.close();
    55                 }
    56                 if (os != null) {
    57                     os.close();
    58                 }
    59                 if (br != null) {
    60                     br.close();
    61                 }
    62                 if (isr != null) {
    63                     isr.close();
    64                 }
    65                 if (is != null) {
    66                     is.close();
    67                 }
    68                 if (socket != null) {
    69                     socket.close();
    70                 } 
    71             } catch (IOException e2) {
    72                 // TODO: handle exception
    73             }
    74         }
    75         
    76         
    77     }
    78     
    79 }

    客户端代码:

     1 package com.heyang;
     2 
     3 import java.io.BufferedReader;
     4 import java.io.IOException;
     5 import java.io.InputStream;
     6 import java.io.InputStreamReader;
     7 import java.io.OutputStream;
     8 import java.io.PrintWriter;
     9 import java.net.Socket;
    10 import java.net.UnknownHostException;
    11 
    12 /*
    13  * 客户端
    14  */
    15 public class Client {
    16     public static void main(String[] args) {
    17         // 1、创建客户端Socket,指定服务器地址和端口
    18         try {
    19             Socket  socket = new Socket("localhost", 8888);
    20             // 2、获取输出流,向服务器端发送信息
    21             OutputStream os = socket.getOutputStream();// 获取字节输出流
    22             // 将输出流包装为打印流
    23             PrintWriter pw = new PrintWriter(os);
    24             pw.write("用户名:admin 密码:123");
    25             pw.flush();
    26             socket.shutdownOutput();//关闭输出流
    27             
    28             // 3、获取输入流,并读取服务器端的响应信息
    29             InputStream is = socket.getInputStream();
    30             BufferedReader br = new BufferedReader(new InputStreamReader(is));
    31             String info = null;
    32             while((info = br.readLine())!=null){
    33                 System.out.println("我是客户端,服务器跟我说:"+info);
    34             }
    35             
    36             // 4、关闭资源
    37             br.close();
    38             is.close();
    39             pw.close();
    40             os.close();
    41             socket.close();
    42             
    43         } catch (UnknownHostException e) {
    44             // TODO Auto-generated catch block
    45             e.printStackTrace();
    46         } catch (IOException e) {
    47             // TODO Auto-generated catch block
    48             e.printStackTrace();
    49         }
    50     }
    51 }

    运行结果不展示。


    5、UDP编程

    UDP协议(用户数据报协议)是无连接、不可靠的、无序的。

    特点:传输速度相对比较快

    UDP协议以数据报作为数据传输的载体

    进行数据传输时,首先需要将要传输的数据定义成数据报(Datagram),在数据报中指明数据所要达到的Socket(主机地址和端口号),然后在将数据报发送出去。

    相关操作的Java类

    DatagramPacket:表示数据报包

    DatagramSocket:进行端到端通信的类

    5-1、编程实现基于UDP的Socket通信之服务器端

    5-2、编程实现基于UDP的Socket通信之客户端

  • 相关阅读:
    HTML基础(一)基本语法知识
    本地方法接口
    本地方法栈
    虚拟机栈相关的问题
    栈帧的内部结构--一些附加信息
    基于角色的权限控制设计
    SpringBoot普通消息队列线程池配置
    栈帧的内部结构--动态返回地址(Return Address)
    栈帧的内部结构--动态链接 (Dynamic Linking)
    栈帧的内部结构--操作数栈(Opreand Stack)
  • 原文地址:https://www.cnblogs.com/goodboy-heyang/p/6372058.html
Copyright © 2011-2022 走看看