使用 Sockets 发送和接收数据

Posted

技术标签:

【中文标题】使用 Sockets 发送和接收数据【英文标题】:Using Sockets to send and receive data 【发布时间】:2011-08-06 12:28:49 【问题描述】:

我正在使用套接字连接我的 android 应用程序(客户端)和 Java 后端服务器。每次与服务器通信时,我想从客户端发送两个数据变量。

1) 某种消息(由用户通过界面定义)

2) 消息的语言(由用户通过界面定义)

我如何发送这些以便服务器将它们解释为一个单独的实体?

在服务器端读取数据并做出适当的结论后,我想向客户端返回一条消息。 (我想我会没事的)

所以我的两个问题是如何确定正在发送的两个字符串(客户端到服务器)在客户端是唯一的,以及如何在服务器端分离这两个字符串。 (我在想一个字符串数组,但无法确定这是否可行或合适。)

我打算发布一些代码,但我不确定这会有什么帮助。

【问题讨论】:

【参考方案1】:

我假设您正在使用 TCP 套接字进行客户端-服务器交互?将不同类型的数据发送到服务器并使其能够区分两者的一种方法是将第一个字节(如果您有超过 256 种类型的消息,则使用更多)作为某种标识符。如果第一个字节为 1,则为消息 A,如果为 2,则为消息 B。通过套接字发送此消息的一种简单方法是使用 DataOutputStream/DataInputStream

客户:

Socket socket = ...; // Create and connect the socket
DataOutputStream dOut = new DataOutputStream(socket.getOutputStream());

// Send first message
dOut.writeByte(1);
dOut.writeUTF("This is the first type of message.");
dOut.flush(); // Send off the data

// Send the second message
dOut.writeByte(2);
dOut.writeUTF("This is the second type of message.");
dOut.flush(); // Send off the data

// Send the third message
dOut.writeByte(3);
dOut.writeUTF("This is the third type of message (Part 1).");
dOut.writeUTF("This is the third type of message (Part 2).");
dOut.flush(); // Send off the data

// Send the exit message
dOut.writeByte(-1);
dOut.flush();

dOut.close();

服务器:

Socket socket = ... // Set up receive socket
DataInputStream dIn = new DataInputStream(socket.getInputStream());

boolean done = false;
while(!done) 
  byte messageType = dIn.readByte();

  switch(messageType)
  
  case 1: // Type A
    System.out.println("Message A: " + dIn.readUTF());
    break;
  case 2: // Type B
    System.out.println("Message B: " + dIn.readUTF());
    break;
  case 3: // Type C
    System.out.println("Message C [1]: " + dIn.readUTF());
    System.out.println("Message C [2]: " + dIn.readUTF());
    break;
  default:
    done = true;
  


dIn.close();

显然,您可以发送各种数据,而不仅仅是字节和字符串 (UTF)。

请注意,writeUTF 写入修改后的 UTF-8 格式,前面是一个无符号的两字节编码整数的长度指示符,为您提供2^16 - 1 = 65535 字节要发送。这使得readUTF 可以找到编码字符串的结尾。如果您决定自己的记录结构,则应确保记录的结尾和类型是已知的或可检测的。

【讨论】:

@user671430 您还需要什么才能将此问题标记为已回答吗? 嗨玩家,一个小问题。在服务器端,我使用的是 ServerSocket 而不仅仅是 Socket。在这种情况下,DataInputStream 似乎不受支持。 您的客户如何连接?我假设您正在某处执行 socket.accept() 以等待客户端连接?该函数返回一个可以调用 getInputStream 和 getOutputStream 的 Socket。【参考方案2】:

执行此操作的最简单方法是将套接字包装在 ObjectInput/OutputStreams 中并发送序列化的 java 对象。您可以创建包含相关数据的类,然后您无需担心处理二进制协议的细节。只需确保在编写每个对象“消息”后刷新对象流。

【讨论】:

尽管消息是很久以前写的,但我有一个问题,如何区分发送字符串消息或元素列表?我发布了我的 [问题] (***.com/questions/25699232/…) 这是一个糟糕的想法,您应该避免对象序列化只是为了创建这样的低级协议。使用DataInputStream / DataOutputStream 编写整数要好得多。 @MaartenBodewes - 欢迎您提出意见。与生活中的所有事物一样,编程是一系列权衡。您可以用便利性(java 序列化)换取额外的代码/复杂性(手写序列化协议)或具有学习曲线的额外库(基于 json/xml 的序列化库)。这一切都取决于您想在哪里投入时间和精力。 我们在这里讨论的是两个完全不同的 Java 环境,它们很可能不同步。所以你可能不得不使用不同类的不同版本。如果您需要更新它们,那么您必须确保所有客户端同时切换或使用版本号等解决它。如果您想在服务器或客户端上切换运行时,那么您可能必须重做协议。 Java 序列化当然有它的好处,但我主要在严格控制的系统上使用它(例如,在同一个场中的服务器之间)。 @MaartenBodewes 实际上 java 序列化对迁移类有很多内置支持(它非常强大和灵活)。您当然可以使用 java 序列化支持多个客户端版本。并且使用 DataInput 和 DataOutput 编写您自己的自定义序列化协议并不能神奇地解决您提到的任何问题。【参考方案3】:
    //Client

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

    public class Client 
        public static void main(String[] args) 

        String hostname = "localhost";
        int port = 6789;

        // declaration section:
        // clientSocket: our client socket
        // os: output stream
        // is: input stream

            Socket clientSocket = null;  
            DataOutputStream os = null;
            BufferedReader is = null;

        // Initialization section:
        // Try to open a socket on the given port
        // Try to open input and output streams

            try 
                clientSocket = new Socket(hostname, port);
                os = new DataOutputStream(clientSocket.getOutputStream());
                is = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
             catch (UnknownHostException e) 
                System.err.println("Don't know about host: " + hostname);
             catch (IOException e) 
                System.err.println("Couldn't get I/O for the connection to: " + hostname);
            

        // If everything has been initialized then we want to write some data
        // to the socket we have opened a connection to on the given port

        if (clientSocket == null || os == null || is == null) 
            System.err.println( "Something is wrong. One variable is null." );
            return;
        

        try 
            while ( true ) 
            System.out.print( "Enter an integer (0 to stop connection, -1 to stop server): " );
            BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
            String keyboardInput = br.readLine();
            os.writeBytes( keyboardInput + "\n" );

            int n = Integer.parseInt( keyboardInput );
            if ( n == 0 || n == -1 ) 
                break;
            

            String responseLine = is.readLine();
            System.out.println("Server returns its square as: " + responseLine);
            

            // clean up:
            // close the output stream
            // close the input stream
            // close the socket

            os.close();
            is.close();
            clientSocket.close();   
         catch (UnknownHostException e) 
            System.err.println("Trying to connect to unknown host: " + e);
         catch (IOException e) 
            System.err.println("IOException:  " + e);
        
                   
    





//Server




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

public class Server1 
    public static void main(String args[]) 
    int port = 6789;
    Server1 server = new Server1( port );
    server.startServer();
    

    // declare a server socket and a client socket for the server

    ServerSocket echoServer = null;
    Socket clientSocket = null;
    int port;

    public Server1( int port ) 
    this.port = port;
    

    public void stopServer() 
    System.out.println( "Server cleaning up." );
    System.exit(0);
    

    public void startServer() 
    // Try to open a server socket on the given port
    // Note that we can't choose a port less than 1024 if we are not
    // privileged users (root)

        try 
        echoServer = new ServerSocket(port);
        
        catch (IOException e) 
        System.out.println(e);
           

    System.out.println( "Waiting for connections. Only one connection is allowed." );

    // Create a socket object from the ServerSocket to listen and accept connections.
    // Use Server1Connection to process the connection.

    while ( true ) 
        try 
        clientSocket = echoServer.accept();
        Server1Connection oneconnection = new Server1Connection(clientSocket, this);
        oneconnection.run();
           
        catch (IOException e) 
        System.out.println(e);
        
    
    


class Server1Connection 
    BufferedReader is;
    PrintStream os;
    Socket clientSocket;
    Server1 server;

    public Server1Connection(Socket clientSocket, Server1 server) 
    this.clientSocket = clientSocket;
    this.server = server;
    System.out.println( "Connection established with: " + clientSocket );
    try 
        is = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
        os = new PrintStream(clientSocket.getOutputStream());
     catch (IOException e) 
        System.out.println(e);
    
    

    public void run() 
        String line;
    try 
        boolean serverStop = false;

            while (true) 
                line = is.readLine();
        System.out.println( "Received " + line );
                int n = Integer.parseInt(line);
        if ( n == -1 ) 
            serverStop = true;
            break;
        
        if ( n == 0 ) break;
                os.println("" + n*n ); 
            

        System.out.println( "Connection closed." );
            is.close();
            os.close();
            clientSocket.close();

        if ( serverStop ) server.stopServer();
     catch (IOException e) 
        System.out.println(e);
    
    

【讨论】:

只有代码的cmets一般不受欢迎,请在代码上方说明你是做什么的;代码应该用来解释答案;它不应该是答案本身。

以上是关于使用 Sockets 发送和接收数据的主要内容,如果未能解决你的问题,请参考以下文章

JAVA中Sockets长连接时使用read()阻塞的问题!急救!

Web Sockets 客户端一旦开始接收数据就会抛出异常

Socket 使用 Node.js 发出 ajax 接收的数据

什么是使用 .NET 3.5 异步 System.Net.Sockets.Socket 停止数据流?

套接字 - 接收/发送消息 (php)

C#通过Socket在网络间发送和接收图片的演示源码