技术文章

【网络编程】| 搭建控制台通信demo

狮子也疯狂

An editor at Blogzine


  • 2023-05-25
  • 8天前
  • 2571
  • 8 Views
  • 100

🦁TCP通信实现原理

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

请求响应模式:

  • Socket类:发送TCP消息。
  • ServerSocket类:创建服务器

**套接字Socket是一种进程间的数据交换机制。**套接字与主机地址和端口地址相关联。主机地址就是客户端或服务器程序所在的主机的IP地址。端口地址是指客户端或服务器程序使用的主机的通信端口。(使用输入输出流进行通信)

TCP/IP套接字是最可靠的双向流协议,使用TCP/IP可以发送任意数量的数据

image-20230523173102168

通过 Socket的编程顺序

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

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

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

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

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

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

  7. 关闭流及Socket。

🦁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();
     }
   }
}

tips:

​ 都是先运行服务端再运行客户端哦!

🦁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();
     }
   }
}

🦁优化点对点聊天应用

前面的案例中,客户端和服务器都有大量重复代码(发送消息和接收消息),其实在网络编程中,编写客户端和服务器唯一的区别就是在实例化socket的时候不一样(一个是实例化ServerSocket,指定监听端口,通过ServerSocket获取客户端的socket,一个是实例化Socket),其它部分都是一致的,所以我们可以把接收信息线程和发送消息线程提取出来,客户端和服务端写在同一个实例里面,通过判断用户输入的信息来执行实例化服务器还是客户端。

/**
 * 发送消息线程
 */
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();
      //启动接收消息的线程
      new Receive(socket).start();
     }catch(Exception e){
      e.printStackTrace();
     }finally{
      if(serverSocket != null){
        try {
          serverSocket.close();
         } catch (IOException e) {
          e.printStackTrace();
         }
       }
     }
   }
}

image-20230524091713406

版权声明:

本文为[狮子也疯狂]所创,转载请带上原文链接,感谢

https://blog.csdn.net/m0_58847451/article/details/130840816


评论数 0



留下回复

如果您是个网络喷子或者键盘侠,那么建议您多看少说。