Java基础深化和提高 ---- 网络编程

news2024/11/16 6:02:27

网络编程基本概念

计算机网络

计算机网络是指将地理位置不同的具有独立功能的多台计算机及其 外部设备,通过通信线路连接起来,在网络操作系统,网络管理软 件及网络通信协议的管理和协调下,实现资源共享和信息传递的计 算机系统。

从其中我们可以提取到以下内容:

1 计算机网络的作用:资源共享和信息传递。

2 计算机网络的组成:

    计算机硬件:计算机(大中小型服务器,台式机、笔记本等)、外部设备(路由器、交换机 等)、通信线路(双绞线、光纤等)。

   计算机软件:网络操作系统(Windows 2000 Server/Advance Server、Unix、Linux等)、 网络管理软件(WorkWin、SugarNMS等)、网络通信协议(如TCP/IP协议栈等)。 

 网络通信协议

 什么是网络通信协议

通过计算机网络可以实现不同计算机之间的连接与通信,但是计算 机网络中实现通信必须有一些约定即通信协议,对速率、传输代 码、代码结构、传输控制步骤、出错控制等制定标准。

国际标准化组织(ISO,即International Organization for Standardization)定义了网络通信协议的基本框架,被称为OSI (Open System Interconnect,即开放系统互联)模型。要制定通 讯规则,内容会很多,比如要考虑A电脑如何找到B电脑,A电脑在 发送信息给B电脑时是否需要B电脑进行反馈,A电脑传送给B电脑的 数据格式又是怎样的?内容太多太杂,所以OSI模型将这些通讯标准 进行层次划分,每一层次解决一个类别的问题,这样就使得标准的 制定没那么复杂。

OSI模型制定的七层标准模型,分别是:应用层, 表示层,会话层,传输层,网络层,数据链路层,物理层。

OSI七层协议模型:

 

 网络协议的分层

虽然国际标准化组织制定了这样一个网络通信协议的模型,但是实 际上互联网通讯使用最多的网络通信协议是TCP/IP网络通信协议。 TCP/IP 模型,也是按照层次划分,共四层:应用层,传输层,网络 层,网络接口层(物理+数据链路层)。 OSI模型与TCP/IP模型的对应关系:

 

 数据封装与解封

 数据封装(Data Encapsulation)是指将协议数据单元(PDU)封 装在一组协议头和协议尾中的过程。在OSI七层参考模型中,每层主 要负责与其它机器上的对等层进行通信。该过程是在协议数据单元 (PDU)中实现的,其中每层的PDU一般由本层的协议头、协议尾 和数据封装构成。

数据发送处理过程

   1 应用层将数据交给传输层,传输层添加上TCP的控制信息(称为TCP头部),这个数据单元称 为段(Segment),加入控制信息的过程称为封装。然后,将段交给网络层。

2 网络层接收到段,再添加上IP头部,这个数据单元称为包(Packet)。然后,将包交给数据链 路层。

3 数据链路层接收到包,再添加上MAC头部和尾部,这个数据单元称为帧(Frame)。然后, 将帧交给物理层。

4 物理层将接收到的数据转化为比特流,然后在网线中传送。

数据接收处理过程

1 物理层接收到比特流,经过处理后将数据交给数据链路层。

2 数据链路层将接收到的数据转化为数据帧,再除去MAC头部和尾部,这个除去控制信息的过 程称为解封,然后将包交给网络层。

 

3 网络层接收到包,再除去IP头部,然后将段交给传输层。

4 传输层接收到段,再除去TCP头部,然后将数据交给应用层。

从以上传输过程中,可以总结出以下规则:

1 发送方数据处理的方式是从高层到底层,逐层进行数据封装。

2 接收方数据处理的方式是从底层到高层,逐层进行数据解封。

接收方的每一层只把对该层有意义的数据拿走,或者说每一层只能 处理发送方同等层的数据,然后把其余的部分传递给上一层,这就 是对等层通信的概念。

数据封装与解封:

    数据封装

 

数据解封

 

IP地址 

 IP是Internet Protocol Address,即"互联网协议地址"。 用来标识网络中的一个通信实体的地址。通信实体可以是计算机、 路由器等。 比如互联网的每个服务器都要有自己的IP地址,而每个 局域网的计算机要通信也要配置IP地址。 路由器是连接两个或多个网络的网络设备。

IP地址分类: 

 目前主流使用的IP地址是IPV4,但是随着网络规模的不断扩大, IPV4面临着枯竭的危险,所以推出了IPV6。

 

 IPV4,采用32位地址长度,只有大约43亿个地址,它只有4段 数字,每一段最大不超过255。随着互联网的发展,IP地址不够 用了,在2019年11月25日IPv4位地址分配完毕。 IPv6采用128位地址长度,几乎可以不受限制地提供地址。按保 守方法估算IPv6实际可分配的地址,整个地球的每平方米面积 上仍可分配1000多个地址。

IP地址实际上是一个32位整数(称为IPv4),以字符串表示的IP地 址如 192.168.0.1 实际上是把32位整数按8位分组后的数字表示,目的是 便于阅读。 IPv6地址实际上是一个128位整数,它是目前使用的IPv4的升级版, 以字符串表示类似于 2001:0db8:85a3:0042:1000:8a2e:0370:7334 

公有地址 

公有地址(Public address)由Inter NIC(Internet Network Information Center互联网信息中心)负责。这些IP地址分配给注 册并向Inter NIC提出申请的组织机构。通过它直接访问互联网。

 私有地址

私有地址(Private address)属于非注册地址,专门为组织机构内 部使用。 以下列出留用的内部私有地址

A类 10.0.0.0--10.255.255.255

B类 172.16.0.0--172.31.255.255

C类 192.168.0.0--192.168.255.255

注意事项

1、127.0.0.1 本机地址

2、192.168.0.0--192.168.255.255为私有地址,属于非注册地址,专门为组织机构内部使用 

端口port 

端口号用来识别计算机中进行通信的应用程序。因此,它也被称为 程序地址。 一台计算机上同时可以运行多个程序。传输层协议正是利用这些端 口号识别本机中正在进行通信的应用程序,并准确地进行数据传 输。

 

 总结

1、IP地址好比每个人的地址(门牌号),端口好比是房间号。必须同时指定IP地址和端口号才 能够正确的发送数据。

2、IP地址好比为电话号码,而端口号就好比为分机号。

端口分配 

端口是虚拟的概念,并不是说在主机上真的有若干个端口。通过端 口,可以在一个主机上运行多个网络应用程序。 端口的表示是一个 16位的二进制整数,对应十进制的0-65535。 操作系统中一共提供了0~65535可用端口范围。 按端口号分类:

公认端口(Well Known Ports):从0到1023,它们紧密绑定 (binding)于一些服务。通常这些端口的通讯明确表明了某种服务 的协议。例如:80端口实际上总是HTTP通讯。

注册端口(Registered Ports):从1024到65535。它们松散地绑 定于一些服务。也就是说有许多服务绑定于这些端口,这些端口同 样用于许多其它目的。例如:许多系统处理动态端口从1024左右开 始。

URL 

 URL作用:

 URL(Uniform Resource Locator),是互联网的统一资源定位 符。用于识别互联网中的信息资源。通过URL我们可以访问文件、 数据库、图像、新闻等。 在互联网上,每一信息资源都有统一且唯一的地址,该地址就叫 URL,URL由4部分组成:协议 、存放资源的主机域名、资源文件名 和端口号。如果未指定该端口号,则使用协议默认的端口。例如 http 协议的默认端口为 80。 在浏览器中访问网页时,地址栏显示 的地址就是URL。 在java.net包中提供了URL类,该类封装了大量复杂的涉及从远程站 点获取信息的细节。

Socket 

 我们开发的网络应用程序位于应用层,TCP和UDP属于传输层协 议,在应用层如何使用传输层的服务呢?在应用层和传输层之间, 则是使用套接字Socket来进行分离。

套接字就像是传输层为应用层开的一个小口,应用程序通过这个小 口向远程发送数据,或者接收远程发来的数据;而这个小口以内, 也就是数据进入这个口之后,或者数据从这个口出来之前,是不知 道也不需要知道的,也不会关心它如何传输,这属于网络其它层次 工作。

Socket实际是传输层供给应用层的编程接口。Socket就是应用层与 传输层之间的桥梁。使用Socket编程可以开发客户机和服务器应用 程序,可以在本地网络上进行通信,也可通过Internet在全球范围 内通信。

 TCP协议和UDP协议

TCP协议

TCP(Transmission Control Protocol,传输控制协议)。TCP方 式就类似于拨打电话,使用该种方式进行网络通讯时,需要建立专 门的虚拟连接,然后进行可靠的数据传输,如果数据发送失败,则 客户端会自动重发该数据。

TCP在建立连接时又分三步走:

n 第一步,是请求端(客户端)发送一个包含SYN即同步 (Synchronize)标志的TCP报文,SYN同步报文会指明客户端使用 的端口以及TCP连接的初始序号。

n 第二步,服务器在收到客户端的SYN报文后,将返回一个 SYN+ACK的报文,表示客户端的请求被接受,同时TCP序号被加 一,ACK即确认(Acknowledgement)。 

n 第三步,客户端也返回一个确认报文ACK给服务器端,同样TCP序 列号被加一,到此一个TCP连接完成。然后才开始通信的第二步: 数据处理。 n 这就是所说的TCP的三次握手(Three-way Handshake)。

UDP协议

UDP(User Data Protocol,用户数据报协议)

UDP是一个非连接的协议,传输数据之前源端和终端不建立连接, 当它想传送时就简单地去抓取来自应用程序的数据,并尽可能快地 把它扔到网络上。 在发送端,UDP传送数据的速度仅仅是受应用程 序生成数据的速度、 计算机的能力和传输带宽的限制; 在接收端, UDP把每个消息段放在队列中,应用程序每次从队列中读一个消息 段。

UDP方式就类似于发送短信,使用这种方式进行网络通讯时,不需 要建立专门的虚拟连接,传输也不是很可靠,如果发送失败则客户 端无法获得。 UDP 因为没有拥塞控制,一直会以恒定的速度发送数据。即使网络 条件不好,也不会对发送速率进行调整。这样实现的弊端就是在网 络条件不好的情况下可能会导致丢包,但是优点也很明显,在某些 实时性要求高的场景(比如电话会议)就需要使用 UDP 而不是 TCP 

TCP和UDP区别 

这两种传输方式都在实际的网络编程中使用,重要的数据一般使用 TCP方式进行数据传输,而大量的非核心数据则可以通过UDP方式 进行传递,在一些程序中甚至结合使用这两种方式进行数据传递。 由于TCP需要建立专用的虚拟连接以及确认传输是否正确,所以使 用TCP方式的速度稍微慢一些,而且传输时产生的数据量要比UDP 稍微大一些。

 

 总结

TCP是面向连接的,传输数据安全,稳定,效率相对较低。

UDP是面向无连接的,传输数据不安全,效率较高。

 Java网络编程中的常用类

Java为了跨平台,在网络应用通信时是不允许直接调用操作系统接 口的,而是由java.net包来提供网络功能。下面我们来介绍几个 java.net包中的常用的类。

InetAddress的使用 

作用:封装计算机的IP地址和DNS(没有端口信息)

注:DNS是Domain Name System,域名系统。

特点:

这个类没有构造方法。如果要得到对象,只能通过静态方法:

getLocalHost()、getByName()、 getAllByName()、 getAddress()、getHostName()

 获取本机信息

获取本机信息需要使用getLocalHost方法创建InetAddress对象。 getLocalHost()方法返回一个InetAddress对象,这个对象包含了本 机的IP地址,计算机名等信息。

public class InetTest {
    public static void main(String[] args)throws Exception {
        //实例化InetAddress对象
        InetAddress inetAddress = InetAddress.getLocalHost();
        //返回当前计算机的IP地址
      System.out.println(inetAddress.getHostAddress());
        //返回当前计算机名
      System.out.println(inetAddress.getHostName());
   }
}

根据域名获取计算机的信息

根据域名获取计算机信息时需要使用getByName(“域名”)方法创建 InetAddress对象。

public class InetTest2 {
    public static void main(String[] args)throws Exception {
        InetAddress inetAddress = InetAddress.getByName("www.baidu.com");
      System.out.println(inetAddress.getHostAddress());
      System.out.println(inetAddress.getHostName());
   }
}

根据IP获取计算机的信息

根据IP地址获取计算机信息时需要使用getByName(“IP”)方法创建 InetAddress对象。

public class InetTest3 {
    public static void main(String[] args)throws  Exception {
        InetAddress inetAddress = InetAddress.getByName("14.215.177.38");
      System.out.println(inetAddress.getHostAddress());
      System.out.println(inetAddress.getHostName());
   }
}

 InetSocketAddress的使用

作用:包含IP和端口信息,常用于Socket通信。此类实现 IP 套接字 地址(IP 地址 + 端口号),不依赖任何协议。 InetSocketAddress相比较InetAddress多了一个端口号,端口的作 用:一台拥有IP地址的主机可以提供许多服务,比如Web服务、 FTP服务、SMTP服务等,这些服务完全可以通过1个IP地址来实 现。 那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址, 因为IP 地址与网络服务的关系是一对多的关系。实际上是通过“IP地 址+端口号”来区分不同的服务的。

public class InetSocketTest {
    public static void main(String[] args) {
        InetSocketAddress inetSocketAddress = new InetSocketAddress("www.baidu.com",80);
      System.out.println(inetSocketAddress.getAddress().getHostAddress());
      System.out.println(inetSocketAddress.getHostName());
   }
}

URL的使用

IP地址标识了Internet上唯一的计算机,而URL则标识了这些计算机 上的资源。 URL 代表一个统一资源定位符,它是指向互联网“资源” 的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对 象的引用,例如对数据库或搜索引擎的查询。 为了方便程序员编程,JDK中提供了URL类,该类的全名是 java.net.URL,有了这样一个类,就可以使用它的各种方法来对 URL对象进行分割、合并等处理。

public class UrlTest {
    public static void main(String[] args)throws Exception {
        URL url = new URL("https://www.itbaizhan.com/search.html?kw=java");
        System.out.println("获取与此URL相关联协议的默认端口:"+url.getDefaultPort());
        System.out.println("访问资源:"+url.getFile());
        System.out.println("主机名"+url.getHost());
        System.out.println("访问资源路径:"+url.getPath());
        System.out.println("协议:"+url.getProtocol());
        System.out.println("参数部分:"+url.getQuery());
   }
}

 通过URL实现最简单的网络爬虫

public class UrlTest2{
    public static void main(String[] args)throws Exception {
            URL url = new URL("https://www.itbaizhan.com/");
            try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) {
             StringBuilder sb = new StringBuilder();
                String temp;
     /*
      * 这样就可以将网络内容下载到本地机器。
      * 然后进行数据分析,建立索引。这也是搜索引擎的第一步。
      */
                while ((temp = br.readLine()) != null) {
                    sb.append(temp);
               }
                System.out.println(sb);
           } catch (Exception e) {
                e.printStackTrace();
           }
   }
}

TCP通信的实现和项目案例

TCP通信实现原理

前边我们提到TCP协议是面向的连接的,在通信时客户端与服务器 端必须建立连接。在网络通讯中,第一次主动发起通讯的程序被称 作客户端(Client)程序,简称客户端,而在第一次通讯中等待连接的 程序被称作服务器端(Server)程序,简称服务器。一旦通讯建立,则 客户端和服务器端完全一样,没有本质的区别。

 “请求-响应”模式:

Socket类:发送TCP消息。

ServerSocket类:创建服务器。

 套接字Socket是一种进程间的数据交换机制。这些进程既可以在同 一机器上,也可以在通过网络连接的不同机器上。换句话说,套接 字起到通信端点的作用。单个套接字是一个端点,而一对套接字则 构成一个双向通信信道,使非关联进程可以在本地或通过网络进行 数据交换。一旦建立套接字连接,数据即可在相同或不同的系统中 双向或单向发送,直到其中一个端点关闭连接。套接字与主机地址 和端口地址相关联。主机地址就是客户端或服务器程序所在的主机 的IP地址。端口地址是指客户端或服务器程序使用的主机的通信端 口。 在客户端和服务器中,分别创建独立的Socket,并通过Socket的属 性,将两个Socket进行连接,这样,客户端和服务器通过套接字所 建立的连接使用输入输出流进行通信。 TCP/IP套接字是最可靠的双向流协议,使用TCP/IP可以发送任意数 量的数据。 实际上,套接字只是计算机上已编号的端口。如果发送方和接收方 计算机确定好端口,他们就可以通信了。 客户端与服务器端的通信关系图:

 

 TCP/IP通信连接的简单过程:

位于A计算机上的TCP/IP软件向B计算机发送包含端口号的消息,B 计算机的TCP/IP软件接收该消息,并进行检查,查看是否有它知道 的程序正在该端口上接收消息。如果有,他就将该消息交给这个程 序。 要使程序有效地运行,就必须有一个客户端和一个服务器。

 通过Socket的编程顺序:

1 创建服务器ServerSocket,在创建时,定义ServerSocket的监听端口(在这个端口接收客户端发来 的消息)

2 ServerSocket调用accept()方法,使之处于阻塞状态。

3 创建客户端Socket,并设置服务器的IP及端口。

4 客户端发出连接请求,建立连接。

5 分别取得服务器和客户端Socket的InputStream和OutputStream。

6 利用Socket和ServerSocket进行数据传输。

7 关闭流及Socket。

TCP通信入门案例 

创建服务端

public class BasicSocketServer {
    public static void main(String[] args) {
        System.out.println("服务器启动等待监听。。。。");
        //创建ServerSocket
        try(ServerSocket ss =new ServerSocket(8888);
            //监听8888端口,此时线程会处于阻塞状态。
            Socket socket = ss.accept();
            //连接成功后会得到与客户端对应的 Socket对象,并解除线程阻塞。
           //通过客户端对应的Socket对象中的输入流对象,获取客户端发送过来的消息。
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()))
       ){       
            System.out.println(br.readLine());
       }catch(Exception e){
            e.printStackTrace();
            System.out.println("服务器启动失败。。。。");
       }
   }
}

创建客户端

public class BasicSocketClient {
    public static void main(String[] args) {
        //创建Socket对象
        try(Socket socket =new Socket("127.0.01",8888);
            //创建向服务端发送消息的输出流对象。
            PrintWriter pw = new PrintWriter(socket.getOutputStream())){
            pw.println("服务端,您好!");
            pw.flush();
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}

TCP单向通信

单向通信是指通信双方中,一方固定为发送端,一方则固定为接收 端。

 创建服务端

public class OneWaySocketServer {
    public static void main(String[] args) {
        System.out.println("服务端启动,开始监听。。。。。");
        try(ServerSocket serverSocket = new ServerSocket(8888);
            //监听8888端口,获与取客户端对应的 Socket对象
            Socket socket = serverSocket.accept();
            //通过与客户端对应的Socket对象获取输入流对象
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //通过与客户端对应的Socket对象获取输出流对象
            PrintWriter pw = new PrintWriter(socket.getOutputStream())){
            System.out.println("连接成功!");
            while(true){
                //读取客户端发送的消息
                String str = br.readLine();
                System.out.println("客户端说:"+str);
                if("exit".equals(str)){
                    break;
               }
                pw.println(str);
                pw.flush();
           }
       }catch(Exception e){
            e.printStackTrace();
            System.out.println("服务端启动失败。。。。。");
       }
   }
}

创建客户端

public class OneWaySocketClient {
    public static void main(String[] args) {
        //获取与服务端对应的Socket对象
        try(Socket socket = new Socket("127.0.0.1",8888);
        //创建键盘输入对象
            Scanner scanner = new Scanner(System.in);
            //通过与服务端对应的Socket对象获取输出流对象
            PrintWriter pw = new PrintWriter(socket.getOutputStream());
            //通过与服务端对应的Socket对象获取输入流对象
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream())))
        {
            while(true){
                //通过键盘输入获取需要向服务端发送的消息
                String str = scanner.nextLine();
                
                //将消息发送到服务端
                pw.println(str);
                pw.flush();
                if("exit".equals(str)){
                    break;
               }
                //读取服务端返回的消息
                String serverInput = br.readLine();
               System.out.println("服务端返回的:"+serverInput);
           }
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}

TCP双向通信

双向通信是指通信双方中,任何一方都可为发送端,任何一方都可 为接收端。

创建服务端

public class TwoWaySocketServer {
    public static void main(String[] args) {
        System.out.println("服务端启动!监听端口8888。。。。");
        try(ServerSocket serverSocket  = new ServerSocket(8888);
            Socket socket = serverSocket.accept();
            //创建键盘输入对象
            Scanner scanner = new Scanner(System.in);
            //通过与客户端对应的Socket对象获取输入流对象
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //通过与客户单对应的Socket对象获取输出流对象
            PrintWriter pw  = new PrintWriter(socket.getOutputStream());){
            while(true){
                //读取客户端发送的消息
                String str = br.readLine();
                System.out.println("客户端说:"+str);
                String keyInput = scanner.nextLine();
                //发送到客户端
                pw.println(keyInput);
                pw.flush();
           }
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}

创建客户端

public class TwoWaySocketClient {
    public static void main(String[] args) {
        try(Socket socket = new Socket("127.0.0.1", 8888);
            //创建键盘输入对象
            Scanner  scanner = new Scanner(System.in);
            //通过与服务端对应的Socket对象获取输入流对象
            BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            //通过与服务端对应的Socket对象获取输出流对象
            PrintWriter pw = new PrintWriter(socket.getOutputStream());){
            while (true) {
                String keyInput = scanner.nextLine();
                pw.println(keyInput);
                pw.flush();
                String input = br.readLine();
                System.out.println("服务端说:" + input);
           }
       } catch (Exception e) {
            e.printStackTrace();
       }
   }
}

创建点对点的聊天应用

创建服务端

/**
* 发送消息线程
*/
class Send extends Thread{
    private Socket socket;
    public Send(Socket socket){
        this.socket = socket;
   }
    @Override
    public void run() {
        this.sendMsg();
   }
    /**
     * 发送消息
     */
    private void sendMsg(){
        //创建Scanner对象
        try(Scanner scanner = new Scanner(System.in);
            //创建向对方输出消息的流对象
            PrintWriter pw = new PrintWriter(this.socket.getOutputStream());)
          {
            while(true){
                String msg = scanner.nextLine();
                pw.println(msg);
                pw.flush();
           }
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}
/**
* 接收消息的线程
*/
class Receive extends Thread{
    private Socket socket;
    public Receive(Socket socket){
        this.socket = socket;
   }
    @Override
    public void run() {
        this.receiveMsg();
   }
    /**
     * 用于接收对方消息的方法
     */
    private void receiveMsg(){
        //创建用于接收对方发送消息的流对象
        try(BufferedReader  br = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));){
            while(true){
                    String msg = br.readLine();
                    System.out.println("他说:"+msg);
           }
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}
public class ChatSocketServer {
    public static void main(String[] args) {
        try(ServerSocket serverSocket = new ServerSocket(8888);){
            System.out.println("服务端启动,等待连接。。。。。");
            Socket socket = serverSocket.accept();
            System.out.println("连接成功!");
            new Send(socket).start();
            new Receive(socket).start();
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}

创建客户端

/**
* 用于发送消息的线程类
*/
class ClientSend extends Thread{
    private Socket socket;
    public ClientSend(Socket socket){
        this.socket = socket;
   }
    @Override
    public void run() {
        this.sendMsg();
   }
    /**
     * 发送消息
     */
    private void sendMsg(){
        //创建Scanner对象
        try(Scanner scanner = new Scanner(System.in);
            //创建向对方输出消息的流对象
            PrintWriter pw = new PrintWriter(this.socket.getOutputStream());)
            {
            while(true){
                String msg = scanner.nextLine();
                pw.println(msg);
                pw.flush();
           }
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}
/**
* 用于接收消息的线程类
*/
class ClientReceive extends Thread{
    private Socket socket;
    public ClientReceive(Socket socket){
        this.socket = socket;
   }
    @Override
    public void run() {
        this.receiveMsg();
   }
    /**
     * 用于接收对方消息的方法
     */
    private void receiveMsg(){
        //创建用于接收对方发送消息的流对象
        try(BufferedReader br = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));){
            while(true){
                String msg = br.readLine();
                System.out.println("他说:"+msg);
           }
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}
public class ChatSocketClient {
    public static void main(String[] args) {
        try {
            Socket socket = new Socket("127.0.0.1", 8888);
            System.out.println("连接成功!");
            new ClientSend(socket).start();
            new ClientReceive(socket).start();
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}

优化点对点聊天应用

/**
* 发送消息线程
*/
class Send extends Thread{
    private Socket socket;
    private Scanner scanner;
    public Send(Socket socket,Scanner scanner){
        this.socket = socket;
        this.scanner = scanner;
}
    @Override
    public void run() {
        this.sendMsg();
   }
    /**
     * 发送消息
     */
    private void sendMsg(){
        //创建向对方输出消息的流对象
        try(PrintWriter pw = new PrintWriter(this.socket.getOutputStream()))
{
            
            while(true){
                String msg = scanner.nextLine();
                pw.println(msg);
                pw.flush();
           }
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}
/**
* 接收消息的线程
*/
class Receive extends Thread{
    private Socket socket;
  public Receive(Socket socket){
        this.socket = socket;
   }
    @Override
    public void run() {
        this.receiveMsg();
   }
    /**
     * 用于接收对方消息的方法
     */
    private void receiveMsg(){
        //创建用于接收对方发送消息的流对象
        try(BufferedReader br = new BufferedReader(new InputStreamReader(this.socket.getInputStream()))){
            while(true){
                String msg = br.readLine();
                System.out.println("他说:"+msg);
           }
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}
public class GoodTCP {
    public static void main(String[] args)
      {
        Scanner scanner = null;
        ServerSocket serverSocket = null;
        Socket socket = null;
        try{
            scanner = new Scanner(System.in);
            System.out.println("请输入:server,<port> 或者:<ip>,<port>");
            String str = scanner.nextLine();
            String[] arr = str.split(",");
            if("server".equals(arr[0])){
                //启动服务端
                System.out.println("TCP Server Listen at "+arr[1]+" .....");
                serverSocket = new ServerSocket(Integer.parseInt(arr[1]));
                socket = serverSocket.accept();
                System.out.println("连接成功!");
           }else{
                //启动客户端
                socket = new Socket(arr[0],Integer.parseInt(arr[1]));
                System.out.println("连接成功!");
           }
            //启动发送消息的线程
            new Send(socket,scanner).start();
            //启动接收消息的线程
         }catch(Exception e){
            e.printStackTrace();
       }finally{
            if(serverSocket != null){
                try {
                    serverSocket.close();
               } catch (IOException e) {
                    e.printStackTrace();
               }
           }
       }
   }
}

一对多应用

一对多应用设计

各socket对间独立问答,互相间不需要传递信息。

 一对多应答型服务器

/**
* 定义消息处理线程类
*/
class Msg extends Thread{
    private Socket socket;
    public Msg(Socket socket){
        this.socket = socket;
   }
    @Override
    public void run() {
        this.msg();
   }
    /**
     * 将从客户端读取到的消息写回给客户端
     */
    private void msg(){
        try(BufferedReader br = new BufferedReader(new InputStreamReader(this.socket.getInputStream()));
            PrintWriter pw = new PrintWriter(this.socket.getOutputStream())){
            while(true){
                pw.println(br.readLine()+" [ok]");
                pw.flush();
           }
       }catch(Exception e){
            e.printStackTrace();
          System.out.println(this.socket.getInetAddress()+" 断线了!");
       }
   }
}
public class EchoServer {
    public static void main(String[] args) {
        try(ServerSocket serverSocket = new ServerSocket(8888)){
            //等待多客户端连接
            while(true){
                Socket socket = serverSocket.accept();
                new Msg(socket).start();
           }
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}

一对多聊天服务器

服务器设计

1、服务器的连接设计

2、服务器的线程设计

 创建一对多聊天服务应用

/**
* 接收客户端消息的线程类
*/
class ChatReceive extends Thread{
    private Socket socket;
    public ChatReceive(Socket socket){
        this.socket =socket;
   }
    @Override
    public void run() {
        this.receiveMsg();
   }
    /**
     * 实现接收客户端发送的消息
     */
    private void receiveMsg(){
        try(BufferedReader br = new BufferedReader(new InputStreamReader(this.socket.getInputStream()))){
            while(true){
                String msg = br.readLine();
                synchronized ("abc"){
                    //把读取到的数据写入公共数据区
                    ChatRoomServer.buf=" ["+this.socket.getInetAddress()+"] "+msg;
                    //唤醒发送消息的线程对象。
                    "abc".notifyAll();
               }
           }
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}
/**
* 向客户端发送消息的线程类
*/
class ChatSend extends Thread{
    private Socket socket;
    public ChatSend(Socket socket){
        this.socket = socket;
   }
    @Override
    public void run() {
        this.sendMsg();
   }
    /**
     * 将公共数据区的消息发送给客户端
     */
    private void sendMsg(){
        try(PrintWriter  pw = new PrintWriter(this.socket.getOutputStream())){
            while(true){
                synchronized ("abc"){
                    //让发送消息的线程处于等待状态
                    "abc".wait();
                    //将公共数据区中的消息发送给客户端
                    pw.println(ChatRoomServer.buf);
                    pw.flush();
               }
           }
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}
public class ChatRoomServer {
    //定义公共数据区
    public static String buf;
    public static void main(String[] args) {
        System.out.println("Chat Server Version 1.0");
        System.out.println("Listen at 8888.....");
        try(ServerSocket serverSocket = new ServerSocket(8888)){
            while(true){
                Socket socket = serverSocket.accept();
                System.out.println("连接到:"+socket.getInetAddress());
                new ChatReceive(socket).start();
                new ChatSend(socket).start();
           }
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}

UDP通信的实现和项目案例

UDP通信实现原理

UDP协议与之前讲到的TCP协议不同,是面向无连接的,双方不需 要建立连接便可通信。UDP通信所发送的数据需要进行封包操作 (使用DatagramPacket类),然后才能接收或发送(使用 DatagramSocket类)。

 

 DatagramPacket:数据容器(封包)的作用

此类表示数据报包。 数据报包用来实现封包的功能。

常用方法:

 

DatagramSocket:用于发送或接收数据报包 

当服务器要向客户端发送数据时,需要在服务器端产生一个 DatagramSocket对象,在客户端产生一个DatagramSocket对象。 服务器端的DatagramSocket将DatagramPacket发送到网络上,然 后被客户端的DatagramSocket接收。 DatagramSocket有两种常用的构造函数。一种是无需任何参数 的,常用于客户端;另一种需要指定端口,常用于服务器端。

如下 所示:

1 DatagramSocket() :构造数据报套接字并将其绑定到本地主机上任何可用的端口。

2 DatagramSocket(int port) :创建数据报套接字并将其绑定到本地主机上的指定端口。

 常用方法:

 UDP通信编程基本步骤:

1 创建客户端的DatagramSocket,创建时,定义客户端的监听端口。

2 创建服务器端的DatagramSocket,创建时,定义服务器端的监听端口。

3 在服务器端定义DatagramPacket对象,封装待发送的数据包。

4 客户端将数据报包发送出去。

5 服务器端接收数据报包。

 UDP通信入门案例

创建服务端

public class UDPServer {
    public static void main(String[] args) {
        //创建服务端接收数据的DatagramSocket对象
        try(DatagramSocket datagramSocket = new DatagramSocket(9999)){
            //创建数据缓存区
            byte[] b = new byte[1024];
            //创建数据报包对象
            DatagramPacket dp =new DatagramPacket(b,b.length);
            //等待接收客户端所发送的数据
            datagramSocket.receive(dp);
            String str = new String(dp.getData(),0,dp.getLength());
            System.out.println(str);
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}

创建客户端

public class UDPClient {
    public static void main(String[] args) {
        //创建数据发送对象 DatagramSocket,需要指定消息的发送端口
        try(DatagramSocket ds = new DatagramSocket(8888)) {
            //消息需要进行类型转换,转换成字节数据类型。
            byte[] b = "程序员".getBytes();
            //创建数据报包装对象DatagramPacket
            DatagramPacket dp = new DatagramPacket(b, b.length, new InetSocketAddress("127.0.0.1", 9999));
            //发送消息
            ds.send(dp);
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}

传递基本数据类型

创建服务端

public class BasicTypeUDPServer {
    public static void main(String[] args) {
        try(DatagramSocket datagramSocket = new DatagramSocket(9999)){
            byte[] buf = new byte[1024];
            DatagramPacket dp = new DatagramPacket(buf,buf.length);
            datagramSocket.receive(dp);
            
            //实现数据类型转换
            try(DataInputStream dis = new DataInputStream(new ByteArrayInputStream(dp.getData()))){
                //通过基本数据数据流对象获取传递的数据
              System.out.println(dis.readLong());
           }
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}

创建客户端

public class BasicTypeClient {
    public static void main(String[] args) {
        long n = 2000L;
        try(DatagramSocket datagramSocket = new DatagramSocket(9000);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            DataOutputStream dos = new DataOutputStream(bos)){
            dos.writeLong(n);
            //将基本数据类型数据转换成字节数组类型
            byte[] arr = bos.toByteArray();
            DatagramPacket dp = new DatagramPacket(arr,arr.length,new InetSocketAddress("127.0.0.1",9999));
            datagramSocket.send(dp);
       }catch (Exception e){
            e.printStackTrace();
       }
   }
}

传递自定义对象类型

创建Person类

/**
* 当该对象需要在网络上传输时,一定要实现Serializable接口
*/
public class Person implements Serializable
{
    private String name;
    private int age;
    public String getName() {
        return name;
   }
    public void setName(String name) {
        this.name = name;
   }
    public int getAge() {
        return age;
   }
    public void setAge(int age) {
        this.age = age;
   }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
   }
}

创建服务端

public class ObjectTypeServer {
    public static void main(String[] args) {
        try(DatagramSocket datagramSocket = new DatagramSocket(9999);){
            byte[] b = new byte[1024];
            DatagramPacket dp = new DatagramPacket(b,b.length);
            datagramSocket.receive(dp);
            //对接收的内容做类型转换
            try(ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(dp.getData()))){
               System.out.println(objectInputStream.readObject());
           }
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}

创建客户端

public class ObjectTypeClient {
    public static void main(String[] args) {
        try(DatagramSocket datagramSocket = new DatagramSocket(8888);
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos)){
            Person p = new Person();
            p.setName("Oldlu");
            p.setAge(18);
            oos.writeObject(p);
            byte[] arr = bos.toByteArray();
            DatagramPacket dp = new DatagramPacket(arr,arr.length,new InetSocketAddress("127.0.0.1",9999));
            datagramSocket.send(dp);
       }catch(Exception e){
            e.printStackTrace();
       }
   }
}

本章总结

端口是虚拟的概念,并不是说在主机上真的有若干个端口。

在www上,每一信息资源都有统一且唯一的地址,该地址就叫 URL(Uniform Resource Locator),它是www的统一资源定 位符。

TCP与UDP的区别

     TCP是面向连接的,传输数据安全,稳定,效率相对较低。

     UDP是面向无连接的,传输数据不安全,效率较高。

Socket通信是一种基于TCP协议,建立稳定连接的点对点的通 信。

网络编程是由java.net包来提供网络功能。

       InetAddress:封装计算机的IP地址和DNS(没有端口信息!)。

       InetSocketAddress:包含IP和端口,常用于Socket通信。

       URL:以使用它的各种方法来对URL对象进行分割、合并等处理。

基于TCP协议的Socket编程和通信

      “请求-响应”模式:

                 Socket类:发送TCP消息。

                 ServerSocket类:创建服务器。

UDP通讯的实现

        DatagramSocket:用于发送或接收数据报包。

                 常用方法:send()、receive()、 close()。

        DatagramPacket:数据容器(封包)的作用

                常用方法:构造方法、getAddrress(获取发送或接收方计算机的IP地址)、getData(获取发送 或接收的数据)、setData(设置发送的数据)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/23044.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux系统下文件的压缩与打包

压缩和解压缩 压缩数据或者解压数据&#xff1a; 为什么要移出数据信息前面的 / 目录 -P --absolute-paths 不要从文件名中去除 /1 compress和uncompress 格式 compress Options [file ...]常用选项 -d 解压缩&#xff0c;相当于uncompress-c 结果输出至标准输出…

Spring 事务失效的场景

1、private、final、static 方法 被 Transactional 注解标注的方法的访问权限必须是 public&#xff1b; 被 Transactional 注解标注的方法不能被 final、static 修饰&#xff0c;被标注的方法必须是可覆盖的。这是因为事务底层使用的是 aop&#xff0c;而 aop 使用的是代理模…

嵌入式为何钟爱SourceInsight,主要因为这个功能~

已剪辑自: https://mp.weixin.qq.com/s/F-gafwbZswpnY8EaCz8HxQ 不管是玩单片机还是嵌入式linux&#xff0c;只要是与硬件结合比较紧密的部分目前基本上还是C语言&#xff0c;当然了&#xff0c;不知道以后C语言会不会被取代&#xff0c;即便哪天被某种更加优秀的语言取代&…

m基于matlab的MIMO信道容量分析,对比了不同天线数量;非码本预编码SVD,GMD;码本预编码DFT,TxAA以及空间分集

目录 1.算法概述 2.仿真效果预览 3.MATLAB部分代码预览 4.完整MATLAB程序 1.算法概述 MIMO信道容量 平均功率分配的MIMO信道容量&#xff1a;通过Matlab仿真在加性高斯白噪声情况下的SISO (1*1)、SIMO(1*6)、MISO(4*1)、MIMO(3*3)、MIMO&#xff08;4*4&#xff09;系统的…

VMware Explore 2022 China,赋能中国企业加速实现云智能

全球领先的企业软件创新者VMware&#xff08;NYSE: VMW&#xff09;的年度技术盛会—— VMware Explore 2022 China于11月8日正式举行。本届大会以“探索多云宇宙”为主题&#xff0c;汇聚云计算领域的顶尖技术专家和创新者&#xff0c;通过150多场解决方案演讲、围绕云基础架构…

[附源码]Python计算机毕业设计超市团购平台

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

JuiceFS 使用效果及注意事项

以某线上集群为例&#xff0c;数据规模&#xff1a;每天写入 5TB&#xff0c;数据储存 30 天&#xff0c;热数据储存一周&#xff0c;节点数量&#xff1a;5 个热节点&#xff0c;15 个冷节点。 采用 JuiceFS 后&#xff0c;热节点保持不变&#xff0c;冷节点从 15 个降到了 1…

[b01lers2020]Life on Mars (难发现的sql注入)

[b01lers2020]Life on Mars 打开界面以后&#xff0c;查看各种都没有提示&#xff0c;果断扫描目录&#xff0c;但也是没获得什么。 到这里只能burp抓个包看看 抓包两个界面发现了get传参&#xff0c;考虑是否存在sql注入 尝试额以后发现只返回了一个1&#xff0c;试一下别的引…

Nginx显示500错误原因和解决方法

文章目录1.背景2.Nginx 常见的几种报错3. 解决500错误1.背景 最近在操作nginx 的时候出现了 Nginx 500 内部错误&#xff0c;在此记录一下原因&#xff0c;项目采用的是前后端分离方式&#xff0c;后端SpringBoot &#xff0c;前端是Vue 部署到Nginx 下。 2.Nginx 常见的几种…

从水质监测运维工程师转行软件测试,武汉校区小哥哥除了收获10k高薪,还有...

从大学步入社会&#xff0c;每个人都会面临角色和身份的转变&#xff0c;大部分同学对此充满信心和期待&#xff0c;仿佛有用不完的精力、耗不尽的热情。可是社会中的磨砺也让心态慢慢变得成熟&#xff0c;生活、文化等方面的落差显现出来&#xff0c;想要做出改变&#xff0c;…

java项目-第151期ssm文物管理系统_java毕业设计_计算机毕业设计

java项目-第151期ssm文物管理系统_java毕业设计_计算机毕业设计 【源码请到资源专栏下载】 今天分享的项目是《ssm文物管理系统》 该项目分为2个角色&#xff0c;管理员、用户。 用户可以浏览前台文物信息、论坛交流、文物资讯、留言反馈。 管理员负责登录后台系统&#xff0c…

[附源码]java毕业设计校医院病人跟踪治疗信息管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

产品质量模型

产品质量模型将系统/软件产品质量属性划分为8个特征&#xff1a;功能性、性能效率、兼容性、易用性、可靠性、安全性、可维护性、可移植性&#xff0c;每个特征由一组相关子特征组成。 衡量一个优秀软件的维度&#xff1a; 产品质量模型只应用于软件产品&#xff0c;或者包含软…

从零开始做一款Unity3D游戏<三>——编写游戏机制

添加跳跃 了解枚举 使用层遮罩 发射投射物 实例化对象 管理游戏对象的创建 游戏管理器 维护玩家属性 get和set属性 精益求精 图形用户界面 胜败条件 使用预编译指定和命令空间 总结 前面一章&#xff0c;我们专注于通过代码来移动玩家和相机&#xff0c;同时了解了…

深入理解计算机系统——Midterm Exam 2012

深入理解计算机系统——Midterm Exam 2012Problem 4 疑问Problem 6 疑问Problem 7 疑问Problem 8 疑问试卷&#xff1a;Midterm Exam 2012 答案&#xff1a;Exam Solutions Problem 4 疑问 第 4 题为第三章内容&#xff0c;这里觉得题目有一步骤写错了&#xff0c;<13> …

让Python可视化事半功倍!

分享一款不错的Matplotlib可视化查询手册&#xff0c;让Python Matplotlib事半功倍&#xff01; 当可视化时&#xff0c;记不清楚图形Marker名称、colormap名称、坐标轴刻度设置、图例设置等等时&#xff0c;稍微扫描一眼该手册&#xff0c;直接YYDS。 下面简单介绍一下这个手册…

SpringBoot SpringBoot 开发实用篇 5 整合第三方技术 5.5 变更缓存供应商 Ehcache

SpringBoot 【黑马程序员SpringBoot2全套视频教程&#xff0c;springboot零基础到项目实战&#xff08;spring boot2完整版&#xff09;】 SpringBoot 开发实用篇 文章目录SpringBootSpringBoot 开发实用篇5 整合第三方技术5.5 变更缓存供应商 Ehcache5.5.1 缓存实现方案5.5.…

部署支持使用Redis哨兵模式,支持纳管ClickHouse数据库,JumpServer堡垒机v2.28.0发布

2022年11月21日&#xff0c;JumpServer开源堡垒机正式发布v2.28.0版本。在这一版本中&#xff0c;JumpServer的部署支持使用Redis哨兵集群作为后端缓存数据库&#xff0c;从而使系统更加健壮和高可用。操作日志审计方面&#xff0c;新增支持查看资源变更信息。当资源有新增、更…

今天给大家介绍一篇基基于SSM超市管理系统的设计与实现

项目描述 临近学期结束&#xff0c;还是毕业设计&#xff0c;你还在做java程序网络编程&#xff0c;期末作业&#xff0c;老师的作业要求觉得大了吗?不知道毕业设计该怎么办?网页功能的数量是否太多?没有合适的类型或系统?等等。这里根据疫情当下&#xff0c;你想解决的问…

【迁移学习】分布差异的度量以及迁移学习的统一表征方法

在文本分类中&#xff0c;由于文本数据有其领域特殊性&#xff0c;因此&#xff0c;在一个领域上训练的分类器&#xff0c;不能直接拿来作用到另一个领域上&#xff0c;这就需要用到迁移学习。 迁移学习是机器学习中重要的研究领域&#xff0c;ICML、NIPS、AAAI、ICIR等国际人工…