了解Java中的Socket编程

136 阅读6分钟

了解Java中的Socket编程

Socket编程是在两台计算机之间通过网络进行数据通信的一种手段。可以使用面向连接的协议或无连接协议进行连接。在我们的案例中,我们将使用TCP/IP,这是一个面向连接的协议。

在交换数据之前,计算机必须建立一个链接,这是为面向连接的协议。UDP(用户数据报协议)是无连接协议的唯一选择。

为了进一步展示套接字,我们将使用客户/服务器架构。客户端和服务器通过向套接字连接的写入和读取进行通信。

套接字的定义

套接字是一个通信端点,作为网络上两台机器之间的链接。它有一个port number ,TCP/IP层可以用它来识别接收数据的应用程序。一个端点通常包括一个port number 和一个IP address

什么是TCP?

传输控制协议(TCP)是一个广泛使用的网络数据传输协议,支持客户/服务器端点。

有两类套接字。

  1. 服务器套接字 - 它等待来自客户端的请求。
  2. 客户端套接字--它在客户和服务器之间建立通信。

客户端必须知道关于服务器的两件事。

  1. 服务器的IP地址。
  2. 端口号。

0到1023之间的端口主要用于管理目的(例如,21用于FTP,23用于Telnet,25用于电子邮件,80用于HTTP)。在我们的程序中,我们将使用端口号5000

创建一个套接字连接

在Java中,我们通过以下步骤创建一个套接字连接。

服务器构造一个ServerSocket 对象,以指定我们的对话将发生在哪个端口。每当发生I/O错误时,就会使用异常处理方法。

accept() 方法被服务器调用,以验证对套接字的传入请求。

然后,客户端通过指定server nameport number 来创建一个Socket 对象。

Socket 类的构造函数试图使用所提供的端口号将客户机连接到服务器。

如果连接成功,客户端和服务器就可以使用I/O streams 。客户端和服务器套接字类负责I/O流的工作。

客户端的OutputStream 与服务器的InputStream 进行通信,而服务器的OutputStream 与客户端的InputStream 进行通信。

一个流基本上是一个有顺序的数据的集合。

流的两个主要类型是。

  1. 字符流(通常用于文本文件)。
  2. 字节流(用于图像)。

字符流是人类可读的语言,而字节流是机器语言。

在这种情况下,客户端和服务器将同时向对方的流广播消息,因为TCP是一个双向的通信协议。

服务器端应用程序的编程

serversocket 是一个使用以下构造函数的ServerSocket 的对象。

serversocket = new ServerSocket(int port)

端口:服务器将监听客户端请求的端口号。

ServerSocket对象保持空闲状态,等待网络请求。我们的客户端将尝试连接到端口5000

确保端口是相同的,否则,连接将失败。

我们将使用两个while 循环。

  • 第一个循环- 这确保服务器确实在运行。
  • 第二个循环--它确保服务器在连接后与客户端进行交互,直到客户端断开连接。
while (true){ //ensures server is running  
    try {  
        socket = serversocket.accept();  
        
        inputStreamReader = new InputStreamReader(socket.getInputStream());  
        outputStreamWriter = new OutputStreamWriter(socket.getOutputStream()); 
        
        bufferedReader = new BufferedReader(inputStreamReader);  
        bufferedWriter = new BufferedWriter(outputStreamWriter);  
        while (true) { // ensures server interacts with the client
            String msgFromClient = bufferedReader.readLine();  
            
            System.out.println("Client: " + msgFromClient); 
            bufferedWriter.write(" MSG Received");  
            bufferedWriter.newLine();  
            bufferedWriter.flush();  
            
          if (msgFromClient.equalsIgnoreCase("BYE"))  
              break;  
  
         }
         socket.close();  
         inputStreamReader.close();  
         outputStreamWriter.close();  
         bufferedReader.close();  
         bufferedWriter.close();  
    
     } catch (IOException e) {  
        e.printStackTrace();  
  }  
}

我们在上面的服务器端程序中使用了以下方法。

  • public InputStream getInputStream() - 它返回一个接收数据的 对象。InputStream
  • public OutputStream getOutputStream()- 它返回一个用于发送数据的OutputStream 对象。
  • public Socket accept() - 它等待客户端连接(在客户端连接之前程序不会继续)。当你连接时,你会得到一个套接字对象,你可以用它来与客户端通信。
  • BufferedReader() - 它包装了inputStreamReader以提高效率。
  • BufferedWriter() - 它包装了outputStreamReader以提高效率。
  • BufferedWriter.flush() - 冲洗输出流。它强制任何缓冲的输出字节被过度写入。当缓冲区满的时候,flush方法被调用。
  • void close() - 这个方法关闭服务器套接字,即停止等待客户端的请求。

下面是服务器端应用程序的全部代码。

package com. company; 
 
import java.io.*;  
import java.net.ServerSocket;  
import java.net.Socket;  
  
public class Server {  
  
  public static void main(String[] args) throws IOException {   
      Socket socket ;  
      InputStreamReader inputStreamReader ;  
      OutputStreamWriter outputStreamWriter ;  
      BufferedReader bufferedReader ;  
      BufferedWriter bufferedWriter ;  
      ServerSocket serversocket ;  
  
      serversocket = new ServerSocket(5000);  
  
      while (true) {  
          try {  
         
              socket = serversocket.accept();  
      
              inputStreamReader = new InputStreamReader(socket.getInputStream());  
              outputStreamWriter = new OutputStreamWriter(socket.getOutputStream());  
              bufferedReader = new BufferedReader(inputStreamReader);  
              bufferedWriter = new BufferedWriter(outputStreamWriter);  

              while (true){  
                  String msgFromClient = bufferedReader.readLine();  
                  System.out.println("Client: " + msgFromClient);   
                  bufferedWriter.write(" MSG Received"); 
                  bufferedWriter.newLine();  
                  bufferedWriter.flush(); 

                  if (msgFromClient.equalsIgnoreCase("BYE"))  
                  break;  
              }  
              socket.close();  
              inputStreamReader.close();  
              outputStreamWriter.close();  
              bufferedReader.close();  
              bufferedWriter.close();  

          } catch (IOException e) {  
              e.printStackTrace();  
          }  
        }  
    }  
}

这段代码的输出样本是。

Connected
Client: Hey there 
Client: I am doing this for the first time
Client: BYE
Closing connection

客户端应用程序的编程

有两种方法可以访问一个Socket 实例。

  1. 服务器将其作为accept() 方法的return 值接收。
  2. 你也可以使用下面的代码来创建一个Socket
socket = new Socket("localhost", 5000);

在上面的代码中,一个localhost 是一个重定向到你的计算机的域名。它可以解析为127.0.0.1 ,作为IP 的地址。第二个参数中是一个端口号。

我们使用套接字对象生成I/O 流,如下图所示。

inputStreamReader = new InputStreamReader(socket.getInputStream());  
outputStreamWriter = new OutputStreamWriter(socket.getOutputStream());

在客户端,我们需要使用一个Scanner 对象来获取用户输入。

Scanner scanner = new Scanner(System.in);

System.in 指明我们正在获取键盘输入。

下面是客户端应用程序的完整代码。

package com. company;  
  
import java.io.*;  
import java.net.Socket;  
import java.util.Scanner;  
  
public class client {  

    public static void main(String[] args) {  
        Socket socket = null;  
        InputStreamReader inputStreamReader = null;  
        OutputStreamWriter outputStreamWriter = null;  
        BufferedReader bufferedReader = null;  
        BufferedWriter bufferedWriter = null;  
        
        try {  
            socket = new Socket("localhost", 5000);  
            inputStreamReader = new InputStreamReader(socket.getInputStream());  
            outputStreamWriter = new OutputStreamWriter(socket.getOutputStream());  
            bufferedReader = new BufferedReader(inputStreamReader);  
            bufferedWriter = new BufferedWriter(outputStreamWriter);  

            Scanner scanner = new Scanner(System.in);  
            while (true){  
                String msgToSend = scanner.nextLine();  
                bufferedWriter.write(msgToSend);  
                bufferedWriter.newLine();  
                bufferedWriter.flush();  
                
                System.out.println("Server: " + bufferedReader.readLine());  //printing the server message
                
                if (msgToSend.equalsIgnoreCase("BYE"))  
                    break;  
            }  
        } catch (IOException e) {  
            e.printStackTrace();  
        } finally {  
             try {  
                  if (socket != null)  
                  socket.close();  
                  if (inputStreamReader != null)  
                    inputStreamReader.close();  
                  if (outputStreamWriter != null)  
                  outputStreamWriter.close();  
                  if (bufferedReader != null)  
                  bufferedReader.close();  
                  if (bufferedWriter != null)  
                  bufferedWriter.close();  
             } catch (IOException e) {  
            e.printStackTrace();  
          }  
       }  
    }  
}

该代码的输出示例如下。

Hey there 
Server:  Message Received
I am doing this for the first time
Server:  Message Received
BYE
Server:  Message Received

测试应用程序

使用Intellij或其他IDE

  • 编译这两个程序。
  • 运行服务器程序,然后再运行客户端程序。
  • 在客户端窗口中输入信息,服务器窗口将同时接收和显示这些信息。
  • 要退出,请输入BYE

使用命令提示符/终端

  • 制作一个新的文件夹,命名为project (这是你的软件包名称)。
  • Server.javaClient.java 放到project 文件夹中。
  • 打开command prompt ,并浏览到root 的路径。
  • 执行javac project\Server.java ,然后是java project.Server
  • 使用与Server 程序相同的程序运行client 程序。
  • 然后你可以在client 窗口中输入信息。

例子。

Hey there 
Server:  Message Received
I am doing this for the first time
Server:  Message Received
BYE
Server:  Message Received

如果端口已经在使用,应用程序可能会导致错误。为了解决这个问题,将端口号改为唯一的值。

总结

在本教程中,我们了解了插座和TCP/IP协议。我们具体介绍了Java中套接字编程的基本原理。

此外,我们还讨论了数据流和客户/服务器交互的工作方式。因此,你可以利用这些知识来建立其他高产的应用程序。