Java --> 网络编程2【TCP通信】

2022/7/27 14:23:00

本文主要是介绍Java --> 网络编程2【TCP通信】,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

 

 

 

 

 步骤:客户端发送数据

需求:

客户端实现步骤:

  1. 创建客户端的Soecket对象,请求与服务端的连接
  2. 使用Socket对象调用getOutputStream( )方法得到字节输出流
  3. 使用字节输出流完成数据的发送
  4. 释放资源:关闭socket管道

服务端实现步骤:

  1. 创建ServerSocket对象,注册服务端端口
  2. 调用ServerSocket对象的accept()方法,等待客户端的连接,并得到Socket管道对象
  3. 通过Socket对象调用getInputStream()方法得到字节输入流,完成数据的接收
  4. 释放资源:关闭socket管道
 1 import java.io.BufferedReader;
 2 import java.io.InputStream;
 3 import java.io.InputStreamReader;
 4 import java.net.ServerSocket;
 5 import java.net.Socket;
 6 
 7 //开发Socket网络编程入门代码的服务端,实现接收消息
 8 public class ServerDemo2 {
 9     public static void main(String[] args) {
10         System.out.println("————————————服务端启动——————————————");
11         try {
12             //1、注册服务端的端口
13             ServerSocket serverSocket = new ServerSocket(5555);
14 
15             //2、调用accept方法,等待接收客户端的Socket连接请求,建立Socket通道
16             Socket socket = serverSocket.accept();
17 
18             //3、从socket管道中得到一个字节输出流
19             InputStream inputStream = socket.getInputStream();
20 
21             //4、把字符输入流包装成换成字符输入流进行消息的接收
22             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
23 
24             //5、按照行来读取消息
25             String str;
26             if ((str = bufferedReader.readLine()) != null){
27                 System.out.print(socket.getRemoteSocketAddress() + "说了:" + str);
28             }
29         } catch (Exception e) {
30             e.printStackTrace();
31         }
32     }
33 }
 1 import java.io.OutputStream;
 2 import java.io.PrintStream;
 3 import java.net.Socket;
 4 
 5 //完成Socket网络编程入门案例的客户端开发,实现一发、一收
 6 public class ClientDemo1 {
 7     public static void main(String[] args) {
 8         System.out.println("————————————客户端启动——————————————");
 9         try {
10             //1、创建Socket管道请求与服务端连接
11             //public Socket(String host, int port)
12             /**
13              * host : 服务器地址【主机】
14              * port : 服务端的端口
15              */
16             Socket socket = new Socket("127.0.0.1",5555);
17             //2、从socket通信管道中得到一个字节输出流,负责发送数据
18             OutputStream outputStream = socket.getOutputStream();
19 
20             //3、把低级的字节流包装成打印流
21             PrintStream printStream = new PrintStream(outputStream);
22 
23             //4、发送消息
24             //此处的println <-->对应服务端的newLine()
25             //print <--> 对应服务端的print
26             printStream.println("我是TCP的客户端,此时已经与你建立连接,并发出邀请~");
27             printStream.flush();
28 
29             //5、关闭资源
30             //socket.close();
31         } catch (Exception e) {
32             e.printStackTrace();
33         }
34     }
35 }

  •  案例:实现TCP通信实现:多发、多收消息
  1. 可以使用死循环控制服务端收完消息继续等待下一条消息
  2. 客户端也可以使用死循环等在用户不断输入消息
  3. 客户端一旦输入了exit,则关闭客户端程序,并释放资源

同样是上述代码,做少许改动即可:

客户端:-->

 

服务端 -->

 

 缺点:因为现在服务端只有一个线程,只能与一个客户端进行通信,所以不可以实现多收【并发】

 

改进:TCP通信 -- 同时可以接收多个客户端的消息【重点】

  • 引入多线程

模型:

 实现思路:

  1. 主线程定义了循环负责接收客户端Socket管道连接
  2. 每接受到一个Socket通信管道后分配一个独立的线程负责处理它
 1 import java.net.ServerSocket;
 2 import java.net.Socket;
 3 
 4 //实现服务端可以同时处理多个客户的消息
 5 public class ServerDemo2 {
 6     public static void main(String[] args) {
 7         System.out.println("————————————服务端启动——————————————");
 8         try {
 9             //1、注册服务端的端口
10             ServerSocket serverSocket = new ServerSocket(5555);
11             //定义一个死循环由主线程负责不断地接收客户端的socket连接请求,建立socket管道
12             while(true){
13                 //2、调用accept方法,等待接收客户端的Socket连接请求,建立Socket通道
14                 Socket socket = serverSocket.accept();
15                 //每接收到一个客户端的Socket管道,交给一个独立的子线程负责读取消息
16                 System.out.println(socket.getRemoteSocketAddress() + "来了,上线了~");
17                 //3、创建独立线程并处理socket
18                 new ServerReaderThread(socket).start();
19             }
20         } catch (Exception e) {
21             e.printStackTrace();
22         }
23     }
24 }
 1 import java.io.*;
 2 import java.net.Socket;
 3 
 4 public class ServerReaderThread extends Thread{
 5     private Socket socket;
 6     public ServerReaderThread(Socket socket){
 7         this.socket = socket;
 8     }
 9     @Override
10     public void run() {
11         try{
12             //从socket管道中得到一个字节输入流
13             InputStream inputStream = socket.getInputStream();
14             //把字节输入流包装成缓冲字节输入流进行消息的接收
15             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
16             //按行来读取消息
17             String message;
18             while ((message = bufferedReader.readLine()) != null){
19                 System.out.println(socket.getRemoteSocketAddress() + "说了:" + message);
20             }
21         }catch (Exception e){
22             //e.printStackTrace();  //出异常代表此客户端已经下线
23             System.out.println(socket.getRemoteSocketAddress() + "下线了");
24         }
25     }
26 }
 1 import java.io.OutputStream;
 2 import java.io.PrintStream;
 3 import java.net.Socket;
 4 import java.util.Scanner;
 5 
 6 //完成Socket网络编程入门案例的客户端开发,实现一发、一收
 7 public class ClientDemo1 {
 8     public static void main(String[] args) {
 9         System.out.println("————————————客户端启动——————————————");
10         try {
11             //1、创建Socket管道请求与服务端连接
12             //public Socket(String host, int port)
13             /**
14              * host : 服务器地址【主机】
15              * port : 服务端的端口
16              */
17             Socket socket = new Socket("127.0.0.1",5555);
18             //2、从socket通信管道中得到一个字节输出流,负责发送数据
19             OutputStream outputStream = socket.getOutputStream();
20 
21             //3、把低级的字节流包装成打印流
22             PrintStream printStream = new PrintStream(outputStream);
23 
24             //4、发送消息
25             //此处的println <-->对应服务端的newLine()
26             //print <--> 对应服务端的print
27             Scanner sc = new Scanner(System.in);
28             while (true){
29                 System.out.println("请说:");
30                 String message = sc.nextLine();
31                 if ("exit".equals(message)){
32                     break;
33                 }
34                 printStream.println(message);
35                 printStream.flush();
36             }
37         } catch (Exception e) {
38             e.printStackTrace();
39         }
40     }
41 }

 

 

 上述过程实现了TCP通信的多发、多收 。

存在问题:客户端与服务端的线程模型是N - N的关系,解决方法 --> 引入线程池处理多个客户端消息

 

 

 

 1 import java.io.OutputStream;
 2 import java.io.PrintStream;
 3 import java.net.Socket;
 4 import java.util.Scanner;
 5 
 6 //拓展 : 使用线程池优化,解决TCP通信模式
 7 public class ClientDemo1 {
 8     public static void main(String[] args) {
 9         System.out.println("----------客户端启动------------");
10         try{
11             //1、创建Socket通信管道,请求与服务端进行连接
12             Socket socket = new Socket("127.0.0.1",5656);
13             //2、从socket管道中得到一个字节输出流,负责发送数据
14             OutputStream outputStream = socket.getOutputStream();
15             //3、把低级的字节流包装成为打印流
16             PrintStream printStream = new PrintStream(outputStream);
17 
18             Scanner sc = new Scanner(System.in);
19             while (true){
20                 System.out.println("请说:");
21                 String message = sc.nextLine();
22                 if ("exit".equals(message)){
23                     break;
24                 }
25                 printStream.println(message);
26                 printStream.flush();
27             }
28         }catch (Exception e){
29             e.printStackTrace();
30         }
31     }
32 }
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.*;

//服务端可以实现
public class ServerDemo2 {
    //使用静态变量定义一个线程池
    private static ExecutorService pool = new ThreadPoolExecutor(3,5,6,
            TimeUnit.SECONDS,new ArrayBlockingQueue<>(2),
            Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy()); //新任务来抛异常
    public static void main(String[] args) {
        System.out.println("--------服务端启动----------");
        try{
            //注册端口
            ServerSocket serverSocket = new ServerSocket(5656);
            //定义死循环,每接收到一个客户端的socket管道,把它交给一个独立的子线程进行处理
            while (true) {
                Socket socket = serverSocket.accept();
                System.out.println(socket.getRemoteSocketAddress() + "来了,上线了~");
                Runnable target = new ServerReaderRunnable(socket); //充当任务
                pool.execute(target);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
 1 import java.io.BufferedReader;
 2 import java.io.InputStream;
 3 import java.io.InputStreamReader;
 4 import java.net.Socket;
 5 
 6 public class ServerReaderRunnable implements Runnable{
 7     private Socket socket;
 8     public ServerReaderRunnable(Socket socket){
 9         this.socket = socket;
10     }
11     @Override
12     public void run() {
13         try{
14              //从socket管道中得到一个字节输入流,接收数据
15             InputStream inputStream = socket.getInputStream();
16             //把字节输入流包装成缓冲字符输入流
17             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
18             //一行一行地读取数据
19             String message;
20             while((message = bufferedReader.readLine()) != null){
21                 System.out.println(socket.getRemoteSocketAddress() + "说:" + message);
22             }
23         }catch (Exception e){
24             System.out.println(socket.getRemoteSocketAddress() + "下线了~");
25         }
26     }
27 }

 优点:

  • 服务端可以复用线程处理多个客户端,可以避免系交通瘫痪
  • 适合客户端通信时长较短地场景【通信较长则会长时间占用线程,导致其它任务不能被服务】

 

案例:TCP通信实战案例 - - 即时通信

  • 即时通信,是指一个客户端的消息发送出去,其它客户端可以接收到
  • 即时通信需要进行端口转发的设计思想
  • 服务端需要把在线的Socket管道存储起来
  • 一旦接收到一个消息要推送给其它设备管理

 

 

 1 import java.io.OutputStream;
 2 import java.io.PrintStream;
 3 import java.net.Socket;
 4 import java.util.Scanner;
 5 
 6 /**
 7  * 1、客户端发送消息、
 8  * 2、客户端收消息
 9  */
10 public class ClientDemo1 {
11     public static void main(String[] args) {
12         System.out.println("-------------客户端启动---------------");
13         try{
14             //创建socket管道,与服务端建立连接
15             Socket socket = new Socket("127.0.0.1",5252);
16             //创建一个独立地线程专门用来负责读取从来自服务端地消息
17             ClientReadThread clientReadThread=  new ClientReadThread(socket);
18             clientReadThread.start();
19             //从socket管道中得到一个字节输出流管道
20             OutputStream outputStream = socket.getOutputStream();
21             //把低级地输出流包装成为打印流
22             PrintStream printStream = new PrintStream(outputStream);
23             //发送消息
24             Scanner sc = new Scanner(System.in);
25             while (true){
26                 String message;
27                 message = sc.nextLine();
28                 if ("exit".equals(message)){
29                     break;
30                 }
31                 printStream.println(message);
32                 printStream.flush();
33             }
34         }catch (Exception e){
35             e.printStackTrace();
36         }
37     }
38 }
 1 import java.io.BufferedReader;
 2 import java.io.InputStream;
 3 import java.io.InputStreamReader;
 4 import java.net.Socket;
 5 
 6 public class ClientReadThread extends Thread{
 7     private Socket socket;
 8     public ClientReadThread(Socket socket){
 9         this.socket = socket;
10     }
11     @Override
12     public void run() {
13         try{
14             //创建字节输入流管道从socket中地到一个字节输入流对象
15             InputStream inputStream = socket.getInputStream();
16             //把低级地字节输入流包装成缓冲字符输入流
17             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
18             //按照行进行读取
19             String message;
20             while((message = bufferedReader.readLine()) != null){
21                 System.out.println("收到消息:" + message);
22             }
23         }catch (Exception e){
24             //出异常表示服务端把该线程移除掉了
25             System.out.println("服务端把你踢出去了");
26         }
27     }
28 }
 1 import java.net.ServerSocket;
 2 import java.net.Socket;
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 //服务端
 7 public class ServerDemo2 {
 8     //定义一个静态的List集合用于存储所有的客户端管道
 9     public static List<Socket> allClientSockets = new ArrayList<>();
10     public static void main(String[] args) {
11         System.out.println("---------服务端启动----------");
12         try {
13             //注册端口
14             ServerSocket serverSocket = new ServerSocket(5252);
15             //定义死循环不断地接收客户端的连接请求
16             //注:在这里等待客户端的socket管道连接
17             while (true) {
18                 Socket socket = serverSocket.accept();
19                 allClientSockets.add(socket);
20                 ServerReadThread target = new ServerReadThread(socket);
21                 target.start();
22             }
23         } catch (Exception e) {
24             e.printStackTrace();
25         }
26     }
27 }
 1 import java.io.*;
 2 import java.net.Socket;
 3 
 4 public class ServerReadThread extends Thread{
 5     private Socket socket;
 6     public ServerReadThread(Socket socket){
 7         this.socket = socket;
 8     }
 9     @Override
10     public void run() {
11         try{
12             //字节输入流
13             InputStream inputStream = socket.getInputStream();
14             //把低级的输入流包装成缓冲字符输入流
15             BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
16             //按照行来进行读取
17             String message;
18             while ((message = bufferedReader.readLine()) != null){
19                 System.out.println(socket.getRemoteSocketAddress() + "来了~~~");
20                 //把这个消息进行端口转发给全部客户端的socket管道
21                 sendMessageToAll(message);
22             }
23         }catch (Exception e){
24             //出现异常表示此用户已经下线
25             System.out.println(socket.getRemoteSocketAddress() + "下线了~~~");
26             //从集合中移除改用户
27             System.out.println(ServerDemo2.allClientSockets.remove(socket));
28         }
29     }
30 
31     //转发消息给所有人
32     private void sendMessageToAll(String message) throws Exception{
33         for (Socket socket : ServerDemo2.allClientSockets) {
34             //得到每个socket管道的输出流
35             OutputStream outputStream = socket.getOutputStream();
36             //把低级的字节输出流管道包装成打印流
37             PrintStream printStream = new PrintStream(outputStream);
38             printStream.println(message);
39             printStream.flush();
40         }
41     }
42 }

 

 

 

 

  •  TCP通信实战案例 -- 模拟BS系统【了解】

 特点:

  1. 客户端使用浏览器发起请求【不需要开发客户端】
  2. 服务端必须按照浏览器的协议规则响应数据

 

 

 1 import java.net.ServerSocket;
 2 import java.net.Socket;
 3 import java.util.concurrent.*;
 4 
 5 //使用TCP模拟BS架构通信系统
 6 public class BSserverDemo {
 7     //定义一个静态变量记住一个线程池对象
 8     public static ExecutorService pool = new ThreadPoolExecutor(3,5,6, TimeUnit.SECONDS,
 9             new ArrayBlockingQueue<>(2), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
10     public static void main(String[] args) {
11         System.out.println("------服务端启动--------");
12         try{
13             //1、注册端口
14             ServerSocket serverSocket = new ServerSocket(8080); //连接浏览器
15             //2、创建死循环可以接收多个客户端的请求
16             while (true){
17                 //每接收到一个客户端的管道都将其做成任务,扔到线程池中【达到线程复用】
18                 Socket socket = serverSocket.accept();
19                 ServerReaderRunnable target = new ServerReaderRunnable(socket);
20                 pool.execute(target);
21             }
22         }catch (Exception e){
23             e.printStackTrace();
24         }
25     }
26 }
 1 import java.io.*;
 2 import java.net.Socket;
 3 
 4 public class ServerReaderRunnable implements Runnable{
 5     private Socket socket;
 6     public ServerReaderRunnable(Socket socket){
 7         this.socket = socket;
 8     }
 9     @Override
10     public void run() {
11         try{
12             //浏览器已经与本线程建立了通信管道
13             //相应消息给浏览器显示
14             OutputStream outputStream = socket.getOutputStream();
15             PrintStream printStream = new PrintStream(outputStream);
16             //必须相应HTTP协议格式数据,否则浏览器不认识信息
17             printStream.println("HTTP/1.1 200 OK"); //协议类型和版本、响应成功的消息
18             printStream.println("Content-Type:text/html;charset=UTF-8"); //响应的数据类型:文本/网页
19             printStream.println(); //必须发送要给空行
20             //响应数据给浏览器
21             printStream.println("<span style='color:green;font-size:50px'>我是服务器返回给客户端的一条数据</span>" ); //响应正文
22             printStream.close();
23         }catch (Exception e){
24             e.printStackTrace();
25         }
26     }
27 }
 1 //不使用线程池
 2 
 3 //import java.io.OutputStream;
 4 //import java.io.PrintStream;
 5 //import java.net.Socket;
 6 //
 7 //public class ServerReaderThread extends Thread{
 8 //    private Socket socket;
 9 //    public ServerReaderThread(Socket socket){
10 //        this.socket = socket;
11 //    }
12 //    //创建输出流给网页
13 //    @Override
14 //    public void run() {
15 //        try{
16 //            //浏览器已经与本线程建立了通信管道
17 //            //相应消息给浏览器显示
18 //            OutputStream outputStream = socket.getOutputStream();
19 //            PrintStream printStream = new PrintStream(outputStream);
20 //            //必须相应HTTP协议格式数据,否则浏览器不认识信息
21 //            printStream.println("HTTP/1.1 200 OK"); //协议类型和版本、响应成功的消息
22 //            printStream.println("Content-Type:text/html;charset=UTF-8"); //响应的数据类型:文本/网页
23 //            printStream.println(); //必须发送要给空行
24 //            //响应数据给浏览器
25 //            printStream.println("<span style='color:red;font-size:50px'>我是服务器返回给客户端的一条数据</span>" ); //响应正文
26 //            printStream.close();
27 //        }catch (Exception e){
28 //            e.printStackTrace();
29 //        }
30 //    }
31 //}



这篇关于Java --> 网络编程2【TCP通信】的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程