Java进阶: 网络编程三要素,常见的网络编程命令,InetAddress类,如何开发UDP通信程序?如何开发TCP通信程序?TCP通信的原理?

                        知识点梳理

                          ==知识点==

1.网络编程的三要素

2.UDP通信的发送和接收

3.TCP通信的发送和接收

                          超详细讲义

1.1 网络编程三要素【了解】

(共2点)

1.什么是网络编程?

网络编程就是让两台计算机进行信息的发送接收

2.网络编程三要素(送外卖)

IP地址

要想让网络中的计算机能够互相通信,必须为每台计算机指定一个标识号,通过这个标识号来指定要接收数据的计算机和识别发送的计算机,而IP地址就是这个标识号。也就是设备的标识

端口

网络的通信,本质上是两个应用程序的通信。每台计算机都有很多的应用程序,那么在网络通信时,如何区分这些应用程序呢?如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的应用程序了。也就是应用程序的标识

协议

通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。常见的协议有UDP协议和TCP协议

1.2 IP地址【了解】

(共3点)

1.什么是IP地址

网络中设备的唯一标识

2.通过域名如何访问服务器?(身份证号和姓名)

3.IP地址的分类?

1.IPv4:

2.IPv6(128位16个字节):

1.3 网络编程-常见的命令【记忆】

DOS常用命令:

  • ipconfig:查看本机IP地址

  • ping IP地址:检查网络是否连通

特殊IP地址:

  • 127.0.0.1:可以代表本机地址

1.4 InetAddress【重点】

1.InetAddress类是什么?

此类表示Internet协议(IP)地址

2.常用方法

说明方法名
确定主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址 static InetAddress getByName(String host)
获取此IP地址的主机名 String getHostName()
返回文本显示中的IP地址字符串 String getHostAddress()

代码演示

package com.itheima.socketdemo1;


import java.net.InetAddress;
import java.net.UnknownHostException;

//static InetAddress getByName•(String host)
//                         确定主机名称的IP地址。主机名称可以是机器名称,也可以是IP地址
//String getHostName•()     获取此IP地址的主机名
//String getHostAddress•() 返回文本显示中的IP地址字符串
public class InetadressDemo1 {
   public static void main(String[] args) throws UnknownHostException {
       InetAddress address = InetAddress.getByName("沉迷代码");

       String hostName = address.getHostName();
       System.out.println("主机名为" + hostName);

       String ip = address.getHostAddress();
       System.out.println("IP为" + ip);
  }
}

 

1.5 端口【了解】

1.什么是端口?

设备上应用程序的唯一标识

2.什么是端口号?

用两个字节表示的整数,它的取值范围是0~65535。其中,0~1023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败

1.6 协议【了解】

1.什么是协议?

计算机网络中,连接和通信的规则被称为网络通信协议

2.常见协议

2.1什么是UDP协议?

用户数据报协议(User Datagram Protocol)

2.1.1 UDP协议的特点?

  • UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接

  • 消耗系统资源小,通信效率高,大小有限制64k

2.1.2 UDP协议的应用场景?

–发短信(非可靠连接)

–聊天(数据小)

–端对端模式,例如飞秋

2.2 什么是TCP协议?

传输控制协议 (Transmission Control Protocol)

2.2.1 TCP协议的特点?

  • TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据

  • 它提供了两台计算机之间可靠无差错的数据传输

  • 数据传输没有大小限制

  • 占用资源比较多,效率比较低

2.2.2 TCP协议的应用场景?

由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛。例如上传文件、下载文件、浏览网页等

2.UDP通信程序

2.1 UDP发送数据【重点】

(共6点)

1.java中如何使用UDP协议进行通信?

提供了两个类:DatagramPacket和 DatagramSocket

2.DatagramPacket和DatagramSocket的作用?

DatagramPacket:数据的容器

DatagramSocket: 用于发送或接收DatagramPacket

3.构造方法

方法名说明
DatagramSocket() 创建数据报套接字并将其绑定到本机地址上的任何可用端口
DatagramPacket(byte[] buf,int len,InetAddress add,int port) 创建数据包,发送长度为len的数据包到指定主机的指定端口

4.相关方法

方法名说明
void send(DatagramPacket p) 发送数据报包
void close() 关闭数据报套接字

5.UDP发送数据的步骤

无需事先建立连接,只需指明地址,将数据发送过去就可以了

1.使用DatagramPacket将对象封装成数据报

2.使用DatagramSocket对象发送数据报

3.释放资源

代码演示

package com.itheima.socketdemo2;


import java.io.IOException;
import java.net.*;

public class ClientDemo {
   public static void main(String[] args) throws IOException {
       //1.找码头
       DatagramSocket ds = new DatagramSocket();
       //2.打包礼物
       //DatagramPacket•(byte[] buf, int length, InetAddress address, int port)
       String s = "送给村长老丈人的礼物";
       byte[] bytes = s.getBytes();
       InetAddress address = InetAddress.getByName("127.0.0.1");
       int port = 10000;
       DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);
       //3.由码头发送包裹
       ds.send(dp);
       //4.付钱走羊
       ds.close();
  }
}

2.2UDP接收数据【重点】

1.接收数据的步骤

1.使用DatagramSocket对象接收数据报

2.从DatagramPacket对象中取出数据

3.释放资源

2.构造方法

方法名说明
DatagramPacket(byte[] buf, int len) 创建一个DatagramPacket用于接收长度为len的数据包

3.相关方法

方法名说明
byte[] getData() 返回数据缓冲区
int getLength() 返回要发送的数据的长度或接收的数据的长度

示例代码

package com.itheima.socketdemo2;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class ServerDemo {
   //注意点:
       //1.要先运行接收端,再运行发送端
       //2.如果接收端再启动之后,没有接收到数据,那么会死等(阻塞).
       //3.在接收数据的时候,需要调用一个getLength方法,表示接收到了多少字节
   public static void main(String[] args) throws IOException {
       //1.找码头     ---- 表示接收端从10000端口接收数据的.
       DatagramSocket ds = new DatagramSocket(10000);
       //2,创建一个新的箱子
       byte [] bytes = new byte[1024];
       DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
       //3.接收礼物,把礼物放到新的箱子中
       System.out.println("-----------接收前----------");
       ds.receive(dp);
       System.out.println("------------接收后---------");
       //4.从新的箱子里面获取礼物
      // byte[] data = dp.getData();
       int length = dp.getLength();
       System.out.println(new String(bytes,0,length));
       //5.拿完走羊
       ds.close();
  }
}

2.3UDP通信程序练习【重点】

案例需求

UDP发送数据,UDP接收数据,可以相互发送信息

代码实现

package com.itheima.socketdemo3;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

public class ServerDemo {
   public static void main(String[] args) throws IOException {
       DatagramSocket ds = new DatagramSocket(10000);

       while (true) {
           byte [] bytes = new byte[1024];
           DatagramPacket dp = new DatagramPacket(bytes,bytes.length);

           ds.receive(dp);

           byte[] data = dp.getData();
           int length = dp.getLength();

           System.out.println(new String(data,0,length));
      }

      // ds.close();
  }
}
package com.itheima.socketdemo3;

import java.io.IOException;
import java.net.*;
import java.util.Scanner;

public class ClientDemo {
   public static void main(String[] args) throws IOException {
       Scanner sc = new Scanner(System.in);
       DatagramSocket ds = new DatagramSocket();
       while (true) {
           String s = sc.nextLine();
           if("886".equals(s)){
               break;
          }
           byte[] bytes = s.getBytes();
           InetAddress address = InetAddress.getByName("127.0.0.1");
           int port = 10000;
           DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);

           ds.send(dp);
      }
       ds.close();
  }
}

2.4UDP三种通讯方式【了解】

(共3点)

  • 单播

    单播用于两个主机之间的端对端通信

  • 组播

    组播用于对一组特定的主机进行通信

  • 广播

    广播用于一个主机对整个局域网上所有主机上的数据通信

2.5UDP组播实现【了解】

1.组播地址

2.实现步骤

  • 发送端

    1.使用DatagramPackage对象封装数据

    2.使用DatagramSocket对象发送数组

    3.释放资源

  • 接收端

    1.使用MulticastSocket对象接收数据

    2.从DatagramPackage对象取出数据

    3.释放资源

代码实现

package com.itheima.socketdemo4;

import java.io.IOException;
import java.net.*;

public class ClinetDemo {
   public static void main(String[] args) throws IOException {
       DatagramSocket ds = new DatagramSocket();

       String s = "hello 组播";
       byte[] bytes = s.getBytes();
       InetAddress address = InetAddress.getByName("224.0.1.0");
       int port = 10000;
       DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);

       ds.send(dp);

       ds.close();
  }
}
package com.itheima.socketdemo4;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;

public class ServerDemo {
   public static void main(String[] args) throws IOException {
       MulticastSocket ms = new MulticastSocket(10000);

       DatagramPacket dp = new DatagramPacket(new byte[1024],1024);


       //把当前计算机绑定一个组播地址,表示添加到这一组中.
       ms.joinGroup(InetAddress.getByName("224.0.1.0"));


       ms.receive(dp);

       byte[] data = dp.getData();
       int length = dp.getLength();
       System.out.println(new String(data,0,length));

       ms.close();
  }
}

2.6UDP广播实现【了解】

1.广播地址

255.255.255.255

2.实现步骤

  • 发送端

    1.使用DatagramPackage对象封装数据

    2.使用DatagramSocket对象发送数组

    3.释放资源

  • 接收端

    1.使用DatagramSocket对象接收数据

    2.从DatagramPackage对象取出数据

    3.释放资源

代码实现

package com.itheima.socketdemo5;

import java.io.IOException;
import java.net.*;

public class ClientDemo {
   public static void main(String[] args) throws IOException {
       DatagramSocket ds = new DatagramSocket();

       String s = "广播 hello";
       byte[] bytes = s.getBytes();
       InetAddress address = InetAddress.getByName("255.255.255.255");
       int port = 10000;
       DatagramPacket dp = new DatagramPacket(bytes,bytes.length,address,port);

       ds.send(dp);

       ds.close();
  }
}
package com.itheima.socketdemo5;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class ServerDemo {
   public static void main(String[] args) throws IOException {
       DatagramSocket ds = new DatagramSocket(10000);

       DatagramPacket dp = new DatagramPacket(new byte[1024],1024);

       ds.receive(dp);

       byte[] data = dp.getData();
       int length = dp.getLength();
       System.out.println(new String(data,0,length));

       ds.close();
  }
}

3.TCP通信程序

3.1TCP-客户端【重点】

(共5点)

1.TCP通信模型

Java中的TCP通信

  • Java对基于TCP协议的的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信。

  • Java为客户端提供了Socket类,为服务器端提供了ServerSocket类

2.什么是Socket?

Socket对象在客户端和服务器之间建立链接。也称为套接字。是两台机器之间通讯的端点

3.构造方法

方法名说明
Socket(InetAddress address,int port) 创建流套接字并将其连接到指定IP指定端口号
Socket(String host, int port) 创建流套接字并将其连接到指定主机上的指定端口号

4.相关方法

方法名说明
OutputStream getOutputStream() 返回此套接字的输出流

5.客户端发送数据的步骤

1.使用Socket对象与服务器端建立连接

2.使用Scoket对象获得输出流对象

3.使用输出流对象发送数据

4.关闭连接,释放资源

 

示例代码

package com.itheima.socketdemo6;

import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class ClientDemo {
   public static void main(String[] args) throws IOException {
       //1.使用Socket对象与服务器端建立连接
       Socket socket = new Socket("127.0.0.1",10000);

       //2.使用Scoket对象获得输出流对象
       OutputStream os = socket.getOutputStream();
       //3.使用输出流对象发送数据
       /*while (true){
           //验证read方法是阻塞方法
       }*/
       os.write("我是中国人".getBytes());
       //4.关闭连接,释放资源
       os.close();
       socket.close();
  }
}

3.2TCP-服务器端【重点】

(共6点)

1.TCP通信模型

2.什么是ServerSocket?

ServerSocket对象等待客户端建立连接,连接建立以后进行通信

3.ServerSocket的构造方法

方法名说明
ServletSocket(int port) 创建绑定到指定端口的服务器套接字

4.ServerSocket的相关方法

方法名说明
Socket accept() accept方法是阻塞的,作用是等待客户端连接。监听要连接到此的套接字并接受它,

5.如何接收网络发过来的信息?

Socket中的方法

方法说明
InputSteam getInputSteam() 返回此套接字的输入流

6.服务端编写步骤:

1.创建ServerSocket对象

2.调用accept方法等待与客户端Socket建立连接,连接建立成功返回新的Socket的对象

3.使用Socket对象的获得输入流对象

4.使用输入流对象获取客户端Socket发送的数据

5.关闭连接,释放资源

代码实现:

package com.itheima.socketdemo6;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo {
   public static void main(String[] args) throws IOException {
       //1. 创建Socket对象
       ServerSocket ss = new ServerSocket(10000);
       //2. 等待客户端连接
       System.out.println(111);
       Socket accept = ss.accept();
       System.out.println(222);
       //3.获得输入流对象
       InputStream is = accept.getInputStream();
       int b;
       while((b = is.read()) != -1){
           System.out.print((char) b);
      }

       System.out.println("看看我执行了吗?");

       //4.释放资源
       is.close();
       ss.close();
  }
}

3.3 TCP原理分析【了解】(视频15 9‘’)

注意事项

  1. accept方法是阻塞的,作用就是等待客户端连接

  2. 客户端创建对象并连接服务器,此时是通过三次握手协议,保证跟服务器之间的连接

  3. 针对客户端来讲,是往外写的,所以是输出流 针对服务器来讲,是往里读的,所以是输入流

  4. read方法也是阻塞的

  5. 客户端在关流的时候,还多了一个往服务器写结束标记的动作

  6. 最后一步断开连接,通过四次挥手协议保证连接终止

3.4 TCP三次握手【了解】

  • 三次握手

3.5 四次挥手【了解】

  • 四次挥手

3.5TCP程序练习【重点】

  • 案例需求

    客户端:写一条数据给服务器端

    服务器:给客户端回显一个数据

  • 案例分析

    • 客户端创建对象,使用输出流输出数据

    • 服务端创建对象,使用输入流接受数据

    • 服务端使用输出流给出反馈数据

    • 客户端使用输入流接受反馈数据

 

 

1.os.close();如果在这里关流,会导致整个socket都无法使用

2.socket.shutdownOutput();不会导致整个socket关闭

代码实现

package com.itheima.socketdemo7;

import java.io.*;
import java.net.Socket;

public class ClientDemo {
   public static void main(String[] args) throws IOException {
       Socket socket = new Socket("127.0.0.1",10000);

       OutputStream os = socket.getOutputStream();
       os.write("hello".getBytes());
      // os.close();如果在这里关流,会导致整个socket都无法使用
       socket.shutdownOutput();//仅仅关闭输出流.并写一个结束标记,对socket没有任何影响


       /*InputStream is = socket.getInputStream();
       int b;
       while((b = is.read()) !=-1){
           System.out.println((char) b);
       }*/

       BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
       String line;
       while((line = br.readLine())!=null){
           System.out.println(line);
      }
       br.close();
       os.close();
       socket.close();
  }
}
package com.itheima.socketdemo7;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo {
   public static void main(String[] args) throws IOException {
       ServerSocket ss = new ServerSocket(10000);

       Socket accept = ss.accept();

       InputStream is = accept.getInputStream();
       int b;
       while((b = is.read())!=-1){
           System.out.println((char) b);
      }

       System.out.println("看看我执行了吗?");
      /* OutputStream os = accept.getOutputStream();
       os.write("你谁啊?".getBytes());*/
       BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
       bw.write("你谁啊?");
       bw.newLine();
       bw.flush();

       bw.close();
       is.close();
       accept.close();
       ss.close();
  }
}

3.6TCP程序文件上传练习【重点】

  • 案例需求

    客户端:数据来自于本地文件,接收服务器反馈

    服务器:接收到的数据写入本地文件,给出反馈

  • 案例分析

    • 创建客户端对象,创建输入流对象指向文件,每读一次数据就给服务器输出一次数据,输出结束后使用shutdownOutput()方法告知服务端传输结束

    • 创建服务器对象,创建输出流对象指向文件,每接受一次数据就使用输出流输出到文件中,传输结束后。使用输出流给客户端反馈信息

    • 客户端接受服务端的回馈信息

==补充==

直接关闭socket,会导致由socket的流关闭,但是实际开发过程中这样做不安全,用到什么流就一一的关闭就可以了

相关方法

方法名说明
void shutdownInput() 将此套接字的输入流放置在“流的末尾”
void shutdownOutput() 禁止用此套接字的输出流

代码实现

package com.itheima.socketdemo8;

import java.io.*;
import java.net.Socket;

public class ClientDemo {
   public static void main(String[] args) throws IOException {
       Socket socket = new Socket("127.0.0.1",10000);
       //是本地的流,用来读取本地文件的.
       BufferedInputStream bis = new BufferedInputStream(
               new FileInputStream("socketmodule\ClientDir\1.jpg"));
       //写到服务器 --- 网络中的流
       OutputStream os = socket.getOutputStream();
       BufferedOutputStream bos = new BufferedOutputStream(os);
       int b;
       while((b = bis.read())!=-1){
           bos.write(b);//通过网络写到服务器中
      }
       bos.flush();
       //给服务器一个结束标记,告诉服务器文件已经传输完毕
       socket.shutdownOutput();
       BufferedReader br = new BufferedReader(
               new InputStreamReader(socket.getInputStream()));
       String line;
       while((line = br.readLine()) !=null){
           System.out.println(line);
      }
       bis.close();
       bos.close();
       br.close();
       socket.close();
  }
}
package com.itheima.socketdemo8;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo {
   public static void main(String[] args) throws IOException {
       ServerSocket ss = new ServerSocket(10000);
       Socket accept = ss.accept();
       //网络中的流,从客户端读取数据的
       BufferedInputStream bis = new BufferedInputStream(
               accept.getInputStream());
       //本地的IO流,把数据写到本地中,实现永久化存储
       BufferedOutputStream bos =
               new BufferedOutputStream(new FileOutputStream(
                       "socketmodule\ServerDir\copy.jpg"));
       int b;
       while((b = bis.read()) !=-1){
           bos.write(b);
      }
       BufferedWriter bw =
               new BufferedWriter(new OutputStreamWriter(
                       accept.getOutputStream()));
       bw.write("上传成功");
       bw.newLine();
       bw.flush();
       
       bis.close();
       bos.close();
       bw.close();
       accept.close();
       ss.close();
  }
}

3.7TCP程序服务器优化-循环【了解】

优化方案一

  • 需求

    服务器只能处理一个客户端请求,接收完一个图片之后,服务器就关闭了。

  • 解决方案

    使用循环

    代码实现

    package com.itheima.loopserver;

    import java.io.*;
    import java.net.ServerSocket;
    import java.net.Socket;

    public class ServerDemo {
     public static void main(String[] args) throws IOException {
           ServerSocket ss = new ServerSocket(10000);

           while (true) {
               Socket accept = ss.accept();

               //网络中的流,从客户端读取数据的
               BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
               //本地的IO流,把数据写到本地中,实现永久化存储
               BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("optimizeserver\ServerDir\copy.jpg"));

               int b;
               while((b = bis.read()) !=-1){
                   bos.write(b);
              }

               BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
               bw.write("上传成功");
               bw.newLine();
               bw.flush();

               bos.close();
               accept.close();
          }
           //ss.close();
      }
    }

3.8 TCP程序服务器优化-UUID【了解】

  • 需求

    第二次上传文件的时候,会把第一次的文件给覆盖。

  • 解决方案

    UUID. randomUUID()方法生成随机的文件名

代码实现

package com.itheima.filename4UUID;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;

public class ServerDemo {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10000);

        while (true) {
            Socket accept = ss.accept();

            //网络中的流,从客户端读取数据的
            BufferedInputStream bis = new BufferedInputStream(accept.getInputStream());
            //本地的IO流,把数据写到本地中,实现永久化存储
            BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("optimizeserver\ServerDir\" + UUID.randomUUID().toString() + ".jpg"));

            int b;
            while((b = bis.read()) !=-1){
                bos.write(b);
            }

            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(accept.getOutputStream()));
            bw.write("上传成功");
            bw.newLine();
            bw.flush();

            bos.close();
            accept.close();
        }
        //ss.close();


    }
}

3.9 TCP程序服务器优化-多线程【了解】

需求

使用循环虽然可以让服务器处理多个客户端请求。但是还是无法同时跟多个客户端进行通信。

解决方案

开启多线程处理

代码实现

package com.itheima.threadsocket;

import java.io.*;
import java.net.Socket;
import java.util.UUID;

public class ThreadSocket implements Runnable {
    private Socket acceptSocket;

    public ThreadSocket(Socket accept) {
        this.acceptSocket = accept;
    }

    @Override
    public void run() {
        BufferedOutputStream bos = null;
        try {
            //网络中的流,从客户端读取数据的
            BufferedInputStream bis = new BufferedInputStream(acceptSocket.getInputStream());
            //本地的IO流,把数据写到本地中,实现永久化存储
            bos = new BufferedOutputStream(new FileOutputStream("optimizeserver\ServerDir\" + UUID.randomUUID().toString() + ".jpg"));
            int b;
            while((b = bis.read()) !=-1){
                bos.write(b);
            }
            BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(acceptSocket.getOutputStream()));
            bw.write("上传成功");
            bw.newLine();
            bw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(bos != null){
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (acceptSocket != null){
                try {
                    acceptSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
package com.itheima.threadsocket;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.UUID;

public class ServerDemo {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10000);

        while (true) {
            Socket accept = ss.accept();
            ThreadSocket ts = new ThreadSocket(accept);
            new Thread(ts).start();

        }
        //ss.close();
    }
}

3.10 TCP程序服务器优化-线程池【了解】

  • 需求

    使用多线程虽然可以让服务器同时处理多个客户端请求。但是资源消耗太大。

  • 解决方案

    加入线程池

  • 代码实现

    package com.itheima.threadpool;
    
    import java.io.IOException;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.util.concurrent.ArrayBlockingQueue;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ThreadPoolExecutor;
    import java.util.concurrent.TimeUnit;
    
    public class ServerDemo {
        public static void main(String[] args) throws IOException {
            ServerSocket ss = new ServerSocket(10000);
          ThreadPoolExecutor pool = new ThreadPoolExecutor(
                    3,//核心线程数量
                    10,   //线程池的总数量
                    60,   //临时线程空闲时间
                    TimeUnit.SECONDS, //临时线程空闲时间的单位
                    new ArrayBlockingQueue<>(5),//阻塞队列
                    Executors.defaultThreadFactory(),//创建线程的方式
                    new ThreadPoolExecutor.AbortPolicy()//任务拒绝策略
            );
    
            while (true) {
                Socket accept = ss.accept();
                ThreadSocket ts = new ThreadSocket(accept);
                //new Thread(ts).start();
                pool.submit(ts);
    
            }
            //ss.close();
    
    
        }
    }

                        扩展练习

题目1

请写出端口号的范围

0至65535

题目2

判断下列说法是否正确:

由于TCP是面向连接的协议,可以保证数据的完整性,因此在传输重要数据时建议采用TCP协议.

正确,TCP协议是面向连接的,有3次握手的过程,可以保证数据的完整性;

题目3

TCP协议中”三次握手”,指的是什么?

第一次握手,客户端向服务器端发出连接请求,等待服务器确认

第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求

第三次握手,客户端再次向服务器端发送确认信息,确认连接
对应的代码就是 创建客户端对象的时候,就会自动完成”三次握手”的操作;如果不能完成”三次握手”,那么创建对象会失败;

题目4

假如你需要设计一个小游戏,当客户端将一段文字发送给服务器后,服务器会将文字反转然后再发回给客户端;请使用程序实现效果;

要求:

客户端通过键盘输入得到一个字符串发给服务器.(键盘输入不要求循环,发一次即可,如果想增强,也可以把键盘输入加上循环,并自己制定结束的标记)

效果:

参考代码:

public class MyClient {
   public static void main(String[] args) throws IOException {
       Socket s = new Socket("127.0.0.1",8888);
       //键盘输入数据,并发给服务器
       Scanner sc = new Scanner(System.in);
       System.out.println("请输入一句话:");
       String next = sc.next();
       s.getOutputStream().write(next.getBytes());
       s.shutdownOutput();//让服务器端结束循环读的行为
       //接受服务器的响应
       InputStream in = s.getInputStream();
       byte[] arr = new byte[1024*8];
       int i;
       System.out.print("我是客户端,我收到的服务器响应是:");
       while ((i=in.read(arr))!=-1){
           System.out.print(new String(arr,0,i));
      }
       System.out.println();
       s.close();
  }
}
class MyServer{
   public static void main(String[] args) throws IOException {
       ServerSocket ss = new ServerSocket(8888);
       Socket s = ss.accept();//接受客户端请求
       //读数据
       InputStream in = s.getInputStream();
       byte[] arr = new byte[1024*8];
       int i;
       //创建sb,用于保存每次循环收到的客户端发送的数据
       StringBuilder sb = new StringBuilder();
       while ((i=in.read(arr))!=-1){
           sb.append(new String(arr,0,i));
      }
       System.out.print("我是服务器端,我收到的客户端请求是:");
       System.out.println(sb);
       s.getOutputStream().write(sb.reverse().toString().getBytes());
       s.shutdownOutput();
       s.close();
       ss.close();
  }
}

 

题目5

假如你想通过程序和同桌建立沟通,请设计一个程序模拟飞秋的聊天功能;

提示:

客户端和服务器端分别都需要写两个线程呦;

要求:

1:客户端(你)需求: 能够通过键盘录入不断地向服务端发送数据.也能接收到服务端发过来的数据.

2:服务端(你同桌)要求: 能够接收客户端的信息.能够通过键盘录入向浏览器发送数据.

3:输入"886" 结束聊天,停止程序;

效果:

 

参考代码:

public class MyClient {
   public static void main(String[] args) throws IOException {
       Socket s = new Socket("127.0.0.1",9999);
       //创建一个线程,专门负责给服务器发送数据
       new Thread(()->{
           try {
               //键盘输入数据,并发给服务器
               Scanner sc = new Scanner(System.in);
               while (true){
                   System.out.println("我是客户端,请输入您想对服务器说的话:");
                   String next = sc.next();
                   if("886".equals(next)){
                       System.out.println("客户端结束输入了!");
                       break;
                  }
                   s.getOutputStream().write(next.getBytes());
                   
              }
               s.shutdownOutput();//让服务器端结束循环读的行为
          } catch (Exception e) {
               e.printStackTrace();
          }
      }).start();

       //创建一个线程,专门负责读服务器数据
       new Thread(()->{
           try {
               //接受服务器的响应
               InputStream in = s.getInputStream();
               byte[] arr = new byte[1024*8];
               int i;
               while ((i=in.read(arr))!=-1){
                   System.out.print("我是客户端,我收到的服务器响应是:");
                   System.out.println(new String(arr,0,i));
              }
               System.out.print("我是客户端,我停止接受服务器消息了");
          } catch (Exception e) {
               e.printStackTrace();
          }
      }).start();
  }
}
class MyServer{
   public static void main(String[] args) throws IOException {
       ServerSocket ss = new ServerSocket(9999);
       Socket s = ss.accept();//接受客户端请求
       //创建一个线程,专门负责读客户端发送的数据
       new Thread(()->{
           try {
               //读客户端的数据
               InputStream in = s.getInputStream();
               byte[] arr = new byte[1024*8];
               int i;
               while ((i=in.read(arr))!=-1){
                   System.out.print("我是服务器,我收到客户端发来的信息是:");
                   System.out.println(new String(arr,0,i));
              }
               System.out.print("我是服务器,我停止接受客户端消息了!");
          } catch (Exception e) {
               e.printStackTrace();
          }
      }).start();

       //给客户端发数据
       new Thread(()->{
           try {
               //键盘输入数据,并发给客户端
               Scanner sc = new Scanner(System.in);
               while (true){
                   System.out.println("我是服务器,请输入您想对客户端说的话:");
                   String next = sc.next();
                   if("886".equals(next)){
                       System.out.println("服务器结束输入了!");
                       break;
                  }
                   s.getOutputStream().write(next.getBytes());
              }
               s.shutdownOutput();
          } catch (Exception e) {
               e.printStackTrace();
          }
      }).start();
  }
}

 

题目6

模拟网站下载图片的功能,先让客户端给服务器发请求,告诉服务器要下载的文件名,然后服务器就给客户端传递文件,直至传递完成;

要求:

1:服务器使用线程池完成,每次客户请求下载文件时,就使用一个线程与客户端交互;

2:要求客户端保存文件时文件名使用UUID随机生成;

效果:

参考代码:

public class MyClient {
   public static void main(String[] args) throws IOException {
       Socket s = new Socket("127.0.0.1",6666);
        //键盘输入数据,并发给服务器
        Scanner sc = new Scanner(System.in);
        System.out.println("我是客户端,请输入您想下载的文件名:");
        String next = sc.next();
        s.getOutputStream().write((next).getBytes());
        s.shutdownOutput();
       //读服务器的数据并写入本地文件
       InputStream in = s.getInputStream();
       byte[] arr = new byte[1024*8];
       BufferedOutputStream bout = new BufferedOutputStream(new FileOutputStream("D:\"+ UUID.randomUUID().toString().replace("-","")+".jpg"));
       int i;
       while ((i=in.read(arr))!=-1){
           bout.write(arr,0,i);
           bout.flush();
      }
       bout.close();
       s.close();
       System.out.print("我是客户端,我下载文件成功了!");
  }
}
class MyServer{
   public static void main(String[] args) throws IOException {
       ServerSocket ss = new ServerSocket(6666);
       ExecutorService pool = Executors.newFixedThreadPool(10);
       while (true){
           System.out.println("服务器已经就绪,等待客户端请求下载...");
           Socket s = ss.accept();//接受客户端请求
           pool.submit(()->{
               try {
                   //读客户端的数据
                   InputStream in = s.getInputStream();
                   byte[] arr = new byte[1024*8];
                   int i=in.read(arr);
                   String name = new String(arr, 0, i);
                   System.out.println("我是服务器,我收到客户端发来的想要下载的文件名是:"+name);
                   //开始给客户端响应文件信息
                   OutputStream out = s.getOutputStream();
                   BufferedInputStream bin = new BufferedInputStream(new FileInputStream("E:\"+name));
                   while ((i=bin.read(arr))!=-1){
                       out.write(arr,0,i);
                  }
                   s.shutdownOutput();
                   System.out.println("我是服务器的"+Thread.currentThread().getName()+"线程,本次下载任务已完成!");
              } catch (Exception e) {
                   e.printStackTrace();
              }
          });
      }
  }
}

 

 

 

原文地址:https://www.cnblogs.com/859630097com/p/14260087.html