ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

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

2022-07-27 00:35:32  阅读:141  来源: 互联网

标签:Java socket java -- TCP new import public Socket


 

 

 

 

 步骤:客户端发送数据

需求:

客户端实现步骤:

  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,socket,java,--,TCP,new,import,public,Socket
来源: https://www.cnblogs.com/yumengqifei/p/16486691.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有