简体   繁体   English

Java-使用套接字通过不同的方法读取和写入文件

[英]Java - Using sockets to read from and write to files, by different methods

I basically have an application that lets customers make reservations to a cinema for a movie. 我基本上有一个应用程序,可以让客户预订电影院的电影。 It also has admins that have control over the users database and content admins that take care of the movie and viewings database. 它还具有可以控制用户数据库的管理员和负责电影和收看数据库的内容管理员。

At this point, we are not required to use an actual database but to read from and write to files. 此时,我们不需要使用实际的数据库,而是可以读写文件。 We have to do this through sockets. 我们必须通过套接字来做到这一点。 The files are on the server and the client gets or sends the data through the connection between the sockets. 文件位于服务器上,客户端通过套接字之间的连接获取或发送数据。

There are different java files for the admins, the content admins and the users, each one containing the methods used by each user class. 管理员,内容管理员和用户使用不同的Java文件,每个文件包含每个用户类使用的方法。 All these java files are on the client side. 所有这些Java文件都在客户端。

I have the following questions. 我有以下问题。

  1. Is it correct that all the "logic" of the program (the .java files containing the methods) is on the client side? 程序的所有“逻辑”(包含方法的.java文件)都在客户端是否正确?

  2. Do I have to use one socket for the server side and one for the client side? 我必须在服务器端使用一个插槽,在客户端使用一个插槽吗? In that case, all methods from the different java files communicate and pass data through the one client socket. 在这种情况下,来自不同java文件的所有方法都通过一个客户端套接字进行通信并传递数据。

  3. Different methods read from and write to different files. 不同的方法读取和写入不同的文件。 How do I tell the data passing through the socket from the client to the server, that it needs to be written to a specific file? 我如何告诉从客户端到服务器通过套接字的数据需要写入特定文件? (the same for reading from the appropriate file) (从适当的文件读取相同)

Two more specific examples. 还有两个更具体的示例。

a) There is a method that lets admins add users to the Users.txt file. a)有一种方法可以让管理员将用户添加到Users.txt文件中。 The admin gives the userId (used to differentiate between admins, content admins and users), name, username and password as arguments to the addUser() method through the main method. 管理员通过main方法将userId(用于区分管理员,内容管理员和用户),名称,用户名和密码作为addUser()方法的参数。 How do I send the data through the socket and write them to the correct file? 如何通过套接字发送数据并将其写入正确的文件? Is there a correspondind method on the server side that has a writer that writes the data to the file? 服务器端是否存在一个具有将数据写入文件的写入器的对应方法? Moreover, do I send the data all as a single line, or as different parts? 此外,我是否全部以一行或不同部分的形式发送数据?

public void createUser(int userId, String name, String username, String password){

    try{

        PrintWriter Writer = new PrintWriter(new BufferedWriter(new FileWriter("Users.txt", true)));

        boolean appendToFile = true;

        if (appendToFile) {
            Writer.println(userId + " " + name + " " + username + " " + password);
        }

        System.out.println("The user "+getUsername()+" was created!");
        Writer.close();

        }

        catch (IOException e) {

        }

}

b) Another method enables the customer to search for films based on a keyword. b)另一种方法使客户能够基于关键字搜索电影。 We need to search each line of the Films.txt file (it has the structure filmId(int);filmCategory(enum);filmTitle(string);filmDesription;). 我们需要搜索Films.txt文件的每一行(其结构为filmId(int); filmCategory(enum); filmTitle(string); filmDesription;)。 The searchViewings() method sends the keyword that the user gave, through the socket, and on the server side it needs to search each line of the file for the keyword in the film titles. searchViewings()方法通过套接字发送用户提供的关键字,在服务器端,它需要在文件的每一行中搜索电影标题中的关键字。 Again, is there a correspondind method that contains the logic for searching each line of the file? 再有,是否有一个containsind方法包含用于搜索文件每一行的逻辑? Else, how does the server side with only the keyword available knows what to do with it? 否则,只有关键字可用的服务器端如何知道如何处理呢?

public void searchViewings(String keyword){

    File inputFile = new File("Provoles.txt");

    String currentLine = null;

    boolean flag = false;

    try{

        BufferedReader Reader = new BufferedReader(new FileReader(inputFile));

        System.out.println("The movies that contain the keyword '"+keyword+"' have the following available viewings:");

        while ((currentLine = Reader.readLine()) != null) {

            String s = currentLine;
            String delims = ";";
            String[] tokens = s.split(delims);

            if (tokens[1].indexOf(keyword) != -1){

                flag = true;

                System.out.println("The movie with Film ID '"+tokens[0]+"' and Film Title '"+tokens[1]+"' has an available viewing at the cinema with ID '"+tokens[2]+"'.");

            }

        }   

        if (flag == false){

            System.out.println("There is no movie containing the current keyword.");

        }

        Reader.close();

    }

    catch (IOException e) {

    }
}

The above code was like this before any socket was implemented. 上面的代码在实现任何套接字之前都是这样的。

  1. At last, is there an example that uses multiple files that I can read or run? 最后,有没有使用多个我可以读取或运行的文件的示例?

Is it correct that all the "logic" of the program (the .java files containing the methods) is on the client side? 程序的所有“逻辑”(包含方法的.java文件)都在客户端是否正确?

No not really there has to be somelogic on the server side aswell, written in Java or in another language. 并非没有必要在服务器端也使用Java或其他语言编写某种逻辑。 Or else there is no way the server can read the file and do logic or send the file to the client. 否则,服务器将无法读取文件,执行逻辑或将文件发送给客户端。

Do I have to use one socket for the server side and one for the client side? 我必须在服务器端使用一个插槽,在客户端使用一个插槽吗? In that case, all methods from the different java files communicate and pass data through the one client socket. 在这种情况下,来自不同java文件的所有方法都通过一个客户端套接字进行通信并传递数据。

You don't have to, for this you will ofcourse need atleast one socket but you could also use more than one socket. 您不必这样做,因此您当然需要至少一个插槽,但也可以使用多个插槽。 For example a socket that the admins use and a socket that the clients use. 例如,管理员使用的套接字和客户端使用的套接字。

Different methods read from and write to different files. 不同的方法读取和写入不同的文件。 How do I tell the data passing through the socket from the client to the server, that it needs to be written to a specific file? 我如何告诉从客户端到服务器通过套接字的数据需要写入特定文件? (the same for reading from the appropriate file) (从适当的文件读取相同)

You could send a header through the socket saying something like: read/file1 or write/file4. 您可以通过套接字发送标头,例如:read / file1或write / file4。 Or something similar, the point is just sending some data telling the server what to do. 或类似的事情,关键是发送一些数据告诉服务器该怎么做。

At last, is there an example that uses multiple files that I can read or run? 最后,有没有使用多个我可以读取或运行的文件的示例?

I don't have one as of this moment but I'll google a bit and update this later if I find one. 目前我还没有,但是我会用Google搜索一下,如果以后找到它,请稍后进行更新。

I hope this helps :) 我希望这有帮助 :)

To answer your first question, you can either store the app logic on the client (aka thick/fat client) or on the server (thin client). 要回答第一个问题,您可以将应用程序逻辑存储在客户端(又名胖/胖客户端)或服务器(瘦客户端)上。 From what I know, there's no one exact answer to which is "better". 据我所知,没有一个确切的答案是“更好”。 It really depends on your app. 这确实取决于您的应用程序。 Use thin client (logic stored on the server) if you want the majority of the processing to be done by the server (this is desired for devices with limited processing power, like smart phones). 如果您希望大多数处理由服务器完成,则使用瘦客户机(存储在服务器上的逻辑)(这对于处理能力有限的设备(例如智能手机)而言是理想的)。 Use fat clients if you'd prefer to perform more of the processing on the clients or if you have limited bandwidth to access your server. 如果您希望在客户端上执行更多处理,或者访问服务器的带宽有限,请使用胖客户端。 There are many articles about fat vs thin clients. 关于胖与瘦客户机的文章很多。 Here's one that you might find useful. 这是您可能会发现有用的一种

I'm a little rusty on sockets, so you might want to take the advice of the other answer, but from what I remember each client will have 1+ socket, and the server will also have 1+ (depending on how you choose to implement your connection) socket. 我对套接字有点生疏,所以您可能想采纳其他答案的建议,但据我所知,每个客户端将具有1+套接字,而服务器也将具有1+(取决于您选择的方式)。实现您的连接)套接字。 Both client and server will use the same port number. 客户端和服务器将使用相同的端口号。 See this article for some simple examples. 请参见文章对于一些简单的例子。 When I experimented with them, I used ServerSocket for the server and Socket for the client. 当我尝试它们时,我将ServerSocket用于服务器,将Socket用于客户端。 Check out the different examples in the above link for ideas. 在上面的链接中查看不同的示例以获取想法。 If you have additional questions, let me know and I'll do my best to elaborate. 如果您还有其他问题,请告诉我们,我们将尽力详细说明。

Here's a simple project using sockets and files to read to and write from that I did a while ago. 这是一个简单的项目,使用套接字和文件读取和写入我之前做过的事情。 Hopefully viewing this code will clarify the questions you have with your own code. 希望查看此代码将阐明您对自己的代码的疑问。 Due to the time constraints, I didn't convert it to include the code snippets you've provided, but if you're still stuck with your own code after taking a look at mine, I'll see what I can do. 由于时间限制,我没有将其转换为包括您提供的代码段,但是如果您在看完我的代码后仍然坚持使用自己的代码,我将为您提供帮助。

A few things to note if you run this code: 如果运行此代码,请注意以下几点:

  • This project uses server and client GUIs that are a little messy (I'm not very good at designing GUIs), but the functionality should still be useful. 该项目使用的服务器和客户端GUI有点杂乱(我不太擅长设计GUI),但是功能仍然应该有用。
  • You need to provide the same port number as a command line argument for both the server and client. 您需要为服务器和客户端提供与命令行参数相同的端口号。 You can do this in Eclipse via Run button dropdown -> Run configurations... -> arguments tab ->typing a port number (I used 12345) -> click run). 您可以在Eclipse中通过运行按钮下拉菜单->运行配置...->参数选项卡->键入端口号(我使用12345)->单击运行来执行此操作。 Do this for both the server and client. 对服务器和客户端都执行此操作。
  • Make sure to run the server before you run the client 在运行客户端之前,请确保运行服务器
  • This example only reads from files. 本示例仅从文件读取。 There's no writing functionality. 没有书写功能。
  • Once the client has successfully started, type the name of the file you want to read from (ie: readfile1.txt) including the file extension. 客户端成功启动后,键入要从其中读取的文件的名称(即readfile1.txt),包括文件扩展名。
  • This uses relative path names starting from the root project directory so if your Users.txt file is in your src folder, you'd type "src/Users.txt". 这使用从根项目目录开始的相对路径名,因此,如果Users.txt文件位于src文件夹中,则应键入“ src / Users.txt”。
  • Type the file name in the client's input field at the top of the client GUI to have the entire contents of that file printed below. 在客户端GUI顶部的客户端输入字段中输入文件名,以在下面打印该文件的全部内容。 You can type as many file names as you want one at a time. 您可以一次输入任意多个文件名。

Server code 服务器代码

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;


public class FileServer extends JFrame {
//main function
public static void main(String[] args) {
    int port = Integer.parseInt(args[0]);
    //String file = args[1];
    FileServer server = new FileServer(port);
    server.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    server.runServer();
}

//FileServer class declarations
private File file; //for this example, the file is stored in the root project directory and includes the file extension (ie: .txt, etc)
private ServerSocket ss;
private Socket connection;
private JTextField field;
private JTextArea displayArea;
private int portNum;
private ObjectOutputStream oos;
private ObjectInputStream ois;

public FileServer(int port) {
    super("Server");
    //file = new File(f); //sent from client or done here?
    portNum = port;
    field = new JTextField();
    field.setEditable(false);
    field.addActionListener(
            new ActionListener() {
                public void actionPerformed(ActionEvent event) {
                    sendData(event.getActionCommand());
                    field.setText("");
                }
            }); //ends addActionListener
    add(field, BorderLayout.NORTH);

    displayArea = new JTextArea();
    add(new JScrollPane(displayArea));

    setSize(500, 300);//size of JFrame
    setVisible(true);
}//end constructor

/**
 * code that executes the server obj.  Think of it as FileServer's main().  This is called from main().
 */
public void runServer() {
    try {
        ss = new ServerSocket(portNum, 100); //creates server socket with port # specified by user and a queue of 100
        while (true) { //infinite loop to continually listen for client connections
            try {
                waitForConnection(); //wait for client connections
                getStreams(); //get I/O streams
                processConnection(); 
            }
            catch (EOFException e) {
                e.printStackTrace();
            }
            finally {
                closeConnection();
            }
        }
    }
    catch (IOException e) {
        e.printStackTrace();
    }
}//end runServer

/**
 * creates socket obj to interact with client.  Socket created when connection with client made
 * @throws IOException 
 */
public void waitForConnection() throws IOException {
    displayMessage("Waiting for connection\n");
    connection = ss.accept(); //returns socket obj when connection made with client
    displayMessage("Connection made with " + connection.getInetAddress().getHostName());
}//end waitForConnection

/**
 * gets IO stream objs for FileServer class
 */
public void getStreams() {
    try {
        oos = new ObjectOutputStream(connection.getOutputStream());
        oos.flush();
        ois = new ObjectInputStream(connection.getInputStream());
    }
    catch (IOException e) {
        e.printStackTrace();
    }
    displayMessage("\n Got both IO streams \n");
}//end getStreams

/**
 * receives filename sent from client and creates file
 */
public void processConnection() throws IOException {
    sendData("Connection successful");
    setTextFieldEditable(true);
    do { //added do while for testing
        try {
            String message = (String) ois.readObject(); //should read in filename then create file obj
            displayMessage("\n " + message + " received from client\n");
            displayMessage("Type in absolute path of file in text field above");
            file = new File(message);
            if(!file.exists()) {
                sendData("File not found");
            }
            else {
                Scanner cin = new Scanner(file);
                while(cin.hasNextLine()) {
                    message = cin.nextLine();
                    sendData(message);
                }
                cin.close();
            }

        }
        catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    } while(true);
}//end processConnection

/**
 * closes IO streams
 */
public void closeConnection() throws IOException {
    displayMessage("\nClosing connections\n");
    setTextFieldEditable(false); 
    oos.close();
    ois.close();
    connection.close();
}//end closeConnection

/**
 * sends message to client
 * @param message
 */
public void sendData(String message) {
    try{
        oos.writeObject(message);//this is what sends message
        oos.flush();
        displayMessage("\n in sendData: " + message + "\n");
    }
    catch(IOException e) {
        displayArea.append("\n Error writing object");
        e.printStackTrace();
    }
}//end sendData

public void displayMessage(final String message) {
    SwingUtilities.invokeLater(
            new Runnable() {
                public void run() {
                    displayArea.append(message);
                }
            });//end SwingUtilties
}

private void setTextFieldEditable( final boolean editable )
   {
      SwingUtilities.invokeLater(
         new Runnable() 
         {
            public void run() // sets enterField's editability
            {
               field.setEditable( editable );
            } // end method run
         } // end anonymous inner class
      ); // end call to SwingUtilities.invokeLater
   } // end method setTextFieldEditable

}

Client code 客户代码

import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;

public class FileClient extends JFrame 
{
    public static void main(String[] args) {
    int port = Integer.parseInt(args[0]);
    String fileName = args[1]; //must use absolute path for filename
    FileClient app = new FileClient("127.0.0.1", port, fileName);
    app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    app.runClient();
}

 private JTextField enterField; // enters information from user
 private JTextArea displayArea; // display information to user
 private ObjectOutputStream output; // output stream to server
 private ObjectInputStream input; // input stream from server
private String message = ""; // message from server
private String chatServer; // host server for this application
private Socket client; // socket to communicate with server
private int portNum;
private String fileName;
private File file;

// initialize chatServer and set up GUI
public FileClient( String host, int port, String fileName )
{
  super( "Client" );
  portNum = port;
  this.fileName = fileName;
  //file = new File(fileName);
  chatServer = host; // set server to which this client connectsS

  enterField = new JTextField(); // create enterField
  enterField.setEditable( false );
  enterField.addActionListener( //need to find a way to send fileName to server without having to type it in
     new ActionListener() 
     {
        // send message to server
        public void actionPerformed( ActionEvent event )
        {
           sendData( event.getActionCommand() );
           enterField.setText( "Messages will be displayed in other text box" );
        } // end method actionPerformed
     } // end anonymous inner class
  ); // end call to addActionListener

  add( enterField, BorderLayout.NORTH );

  displayArea = new JTextArea(); // create displayArea
  add( new JScrollPane( displayArea ), BorderLayout.CENTER );

  setSize( 500, 300 ); // set size of window
  setVisible( true ); // show window
} // end Client constructor

// connect to server and process messages from server
public void runClient() 
{
  try // connect to server, get streams, process connection
  {
     connectToServer(); // create a Socket to make connection
     getStreams(); // get the input and output streams
     processConnection(); // process connection
     sendData(fileName); //sends name of file to server to retrieve
     displayMessage("Sent request for " + fileName + " to server.");
  } // end try
  catch ( EOFException eofException ) 
  {
     displayMessage( "\nClient terminated connection" );
  } // end catch
  catch ( IOException ioException ) 
  {
     ioException.printStackTrace();
  } // end catch
  finally 
  {
     closeConnection(); // close connection
  } // end finally
} // end method runClient

// connect to server
private void connectToServer() throws IOException
{      
  displayMessage( "Attempting connection\n" ); //not getting here

  // create Socket to make connection to server
  client = new Socket( InetAddress.getByName( chatServer ), portNum );

  // display connection information
  displayMessage( "Connected to: " + 
     client.getInetAddress().getHostName() );
} // end method connectToServer

// get streams to send and receive data
private void getStreams() throws IOException
{
  // set up output stream for objects
  output = new ObjectOutputStream( client.getOutputStream() );      
  output.flush(); // flush output buffer to send header information

  // set up input stream for objects
  input = new ObjectInputStream( client.getInputStream() );

  displayMessage( "\nGot I/O streams\n" );
 } // end method getStreams

 // process connection with server
 private void processConnection() throws IOException //problem possibly due to processConnection being in infinite loop?
{
  // enable enterField so client user can send messages
  setTextFieldEditable( true );

  do // process messages sent from server
  { 
     try // read message and display it
     {
        message = input.readObject().toString(); // read new message
        displayMessage( "\n" + message ); // display message
     } // end try
     catch ( ClassNotFoundException classNotFoundException ) 
     {
        displayMessage( "\nUnknown object type received" );
        classNotFoundException.printStackTrace();
     } // end catch

  } while ( !message.equals( "SERVER>>> TERMINATE" ) );
} // end method processConnection

// close streams and socket
private void closeConnection() 
{
  displayMessage( "\nClosing connection" );
  setTextFieldEditable( false ); // disable enterField

  try 
  {
     output.close(); // close output stream
     input.close(); // close input stream
     client.close(); // close socket
  } // end try
  catch ( IOException ioException ) 
  {
     ioException.printStackTrace();
  } // end catch
} // end method closeConnection

// send message to server
private void sendData( String message ) //need to send filename to server
{
  try // send object to server
  {
     output.writeObject(message);
     //output.writeObject(fileName); //is writeObject the appropriate write method?
     output.flush(); // flush data to output
     displayMessage( "\nCLIENT>>> " + message );
  } // end try
  catch ( IOException ioException )
  {
     displayArea.append( "\nError writing object" );
     ioException.printStackTrace();
  } // end catch
} // end method sendData

// manipulates displayArea in the event-dispatch thread
private void displayMessage( final String messageToDisplay )
{
  SwingUtilities.invokeLater(
     new Runnable()
     {
        public void run() // updates displayArea
        {
           displayArea.append( messageToDisplay );
        } // end method run
     }  // end anonymous inner class
  ); // end call to SwingUtilities.invokeLater
} // end method displayMessage

// manipulates enterField in the event-dispatch thread
private void setTextFieldEditable( final boolean editable )
{
  SwingUtilities.invokeLater(
     new Runnable() 
     {
        public void run() // sets enterField's editability
        {
           enterField.setEditable( editable );
        } // end method run
     } // end anonymous inner class
  ); // end call to SwingUtilities.invokeLater
} // end method setTextFieldEditable
} // end class Client

I hope this helps. 我希望这有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM