Socket编程:TCP客户端/服务器应用程序

客户机/服务器应用程序模型通常被看作是位于远处的,高功率计算装置,其存储大量数据的业务逻辑。而用户界面在相对便宜的机器上由客户端软件进行处理。这种观点有些模糊,因为任何服务机器的请求可能会被称为服务器。尽管服务器等待客户端开始对话,在某些情况下,在同一个程序既可以充当客户端也可以作为服务器。 从这个意义上说,一台机器可以作为网络客户端进行通信,也可经过一个TCP/IP协议栈的层在服务器程序之间进行通信。 一个套接是由操作系统所提供的一个API,以实现连接。 该packagejava.net提供了必要的元素,以实现两个最上面的TCP/IP层之间的接口通信:应用和传输。本文阐述了客户机/服务器模式背后的理念,关于创建Java中的TCP客户机/服务器应用程序的详细信息。
客户端套接基础知识
Socket是建立了两台主机之间的连接端点。 Java中提供的Socket类既可用于客户端和也可用于服务器。 基本操作领域如下:
连接到远程主机。
发送和接收数据。
关闭连接。
绑定到一个端口。
监听传入的数据。
在有限的端口上接受远程连接。
最后三个操作仅特定于服务器;它们由ServerSocket类实现。客户端程序工作流以如下方式出现:
1. 创建一个新的Socket对象。
2. 尝试连接到远程主机
3. 一旦连接成功,本地和远程主机可保持输入和输出流,并且可以在全双工模式下工作。 接收和发送的数据具有不同的意义,根据所使用的协议(从FTP服务器发送/接收数据会与HTTP服务器不同)。通常,在随数据传输之后有某种协议。
4. Sockets必须在传输完成后封闭两端。某些协议,如HTTP,可确保连接在每次请求服务时被关闭。而另一方面,FTP允许在关闭连接之前处理多个请求。
一个简单的举例
下面的代码演示如何创建客户端Socket。 该应用程序是一个基本的聊天应用程序,在全双工作模式下进行服务器聊天,直到连接明显关闭。
















package tcpsocketdemo;








import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;








public class Client extends JFrame {








   private static final int PORT = 12345;
   private static final int BACKLOG = 100;








   private final JTextField msgField =
      new JTextField();
   private final JTextArea msgDisplayArea =
      new JTextArea();
   private ObjectOutputStream out;
   private ObjectInputStream in;
   private Socket socket;
   private final String host;
   private String msg;








   public Client(String host) {
      super("Client");
      this.host=host;
      msgField.addActionListener((ActionEvent e) -> {
         send(e.getActionCommand());
         msgField.setText("");
      });
      super.add(msgField, BorderLayout.NORTH);
      super.add(new JScrollPane(msgDisplayArea));
      super.setSize(400, 250);
      super.setVisible(true);
      super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   }








   public void startClient() {
      try {
         connectCall();
         setIOStreams();
         processCall();








      } catch (IOException e) {
      }
   }








   private void setIOStreams() throws IOException {
      out = new ObjectOutputStream(socket.getOutputStream());
      out.flush();
      in = new ObjectInputStream(socket.getInputStream());
      showMsg("\nI/O Stream is ready.");
   }








   private void connectCall() throws IOException {
      showMsg("\nConnecting...");
      socket = new Socket(InetAddress.getByName(host), PORT);
      showMsg("Connected to " +
         socket.getInetAddress().getHostName());
   }








   private void processCall() {
      setMsgFieldEditable(true);
      do {
         try {
            msg = (String) in.readObject();
            showMsg("\n" + msg);
         } catch (ClassNotFoundException | IOException ex) {
            showMsg("\nInvalid object received");
         }
      } while (!msg.equals("Server# bye"));








   }








   private void showMsg(final String msg) {
      SwingUtilities.invokeLater(() -> {
         msgDisplayArea.append(msg);
      });
   }








   private void setMsgFieldEditable(final boolean flag) {
      SwingUtilities.invokeLater(() -> {
         msgField.setEditable(flag);
      });
   }








   private void send(String msg) {
      try {
         out.writeObject("\nClient# " + msg);
         out.flush();
         showMsg("\nClient# " + msg);
      } catch (IOException ex) {
         msgDisplayArea.append("Error: " + ex);
      }
   }








   private void endCall() {
      showMsg("\nConnection closed");
      setMsgFieldEditable(false);
      try {
         out.close();
         in.close();
         socket.close();
      } catch (IOException ex) {
      }
   }








}
package tcpsocketdemo;








public class TCPSocketClient{
   public static void main(String[] args){
      String localhost="127.0.0.1";








      Client app=new Client(localhost);
      app.startClient();
   }
}
服务器Socket基础知识
ServerSocket类提供了小生写的一个有关Java的服务器。服务器套接的主要工作就是等待调用,并作出相应的反应。ServerSocket运行在一个以端口为界的服务器上,并监听传入的TCP连接。 当客户端Socket尝试连接到该端口,服务器唤醒由两台主机之间打开一个Socket的连接。 这个Socket对象被用于将数据发送到客户端。 服务器程序的工作流程可以被定义如下:
1. 在一个特定的端口上创建服务器套接。
2. 监听连接到该端口的任何意图。
3. 如果连接尝试成功,从Socket 获得流对象的主机,并与客户通信。
4. 但是,通信得根据该约定的协议建立。
5. 关闭连接。
6. 进一步等待更多的通信尝试(回到步骤2)。
一个简单的举例
下面的代码演示如何创建客户端套接。 可以观察到大多数的功能非常类似于先前创建的客户端程序。 指定这个类作为服务器的元素是ServerSocket对象。
package tcpsocketdemo;








import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.io.EOFException;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;








public class Server extends JFrame {
   private static final int PORT = 12345;
   private static final int BACKLOG = 100;








   private final JTextField msgField =
      new JTextField();
   private final JTextArea msgDisplayArea =
      new JTextArea();
   private ObjectOutputStream out;
   private ObjectInputStream in;
   private ServerSocket server;
   private Socket socket;








   public Server() {
      super("Server");
      msgField.setEditable(false);
      msgField.addActionListener((ActionEvent e) -> {
         send(e.getActionCommand());
         msgField.setText("");
         throw new UnsupportedOperationException
            ("Not supported yet.");
      });
      super.add(msgField, BorderLayout.NORTH);
      super.add(new JScrollPane(msgDisplayArea));
      super.setSize(400, 250);
      super.setVisible(true);
      super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   }








   public void startServer() {
      try {
         server = new ServerSocket(PORT, BACKLOG);
         while (true) {
            try {
               waitForCall();
               setIOStreams();
               processCall();
            } catch (EOFException ex) {
               showMsg("\nServer connection closed");
            } finally {
               endCall();
            }
         }
      } catch (IOException e) {
      }
   }








   private void setIOStreams() throws IOException {
      out = new ObjectOutputStream(socket.getOutputStream());
      out.flush();
      in = new ObjectInputStream(socket.getInputStream());
      showMsg("\nI/O Stream is ready.");
   }








   private void waitForCall() throws IOException {
      showMsg("\nWaiting for call...");
      socket = server.accept();
      showMsg("Connection accepted from " +
         socket.getInetAddress().getHostName());
   }








   private void processCall() {
      String msg = "Connection established successfully.";
      send(msg);
      setMsgFieldEditable(true);
      do {
         try {
            msg = (String) in.readObject();
            showMsg("\n" + msg);
         } catch (ClassNotFoundException | IOException ex) {
            showMsg("\nInvalid object received");
         }
      } while (!msg.equals("Client# bye"));
   }








   private void showMsg(final String msg) {
      SwingUtilities.invokeLater(() -> {
         msgDisplayArea.append(msg);
      });
   }








   private void setMsgFieldEditable(final boolean flag) {
      SwingUtilities.invokeLater(() -> {
         msgField.setEditable(flag);
      });
   }








   private void send(String msg) {
      try {
         out.writeObject("\nServer# " + msg);
         out.flush();
         showMsg("\nServer# " + msg);
      } catch (IOException ex) {
         msgDisplayArea.append("Error: " + ex);
      }
   }








   private void endCall() {
      showMsg("\nConnection closed");
      setMsgFieldEditable(false);
      try {
         out.close();
         in.close();
         socket.close();
      } catch (IOException ex) {
      }
   }
}








packagetcpsocketdemo;
public class TCPSocketServer{








   public static void main(String[] args) {
      Server app=new Server();
      app.startServer();
   }
}








在这里,我们一直在讨论以TCP数据包为代表的面向连接的,基于数据流的传输。还有另一面客户端/服务器交互的事项,其是通过UDP报文表示的无连接,称为数据报。对于我们创建的UDP客户端/服务器应用程序,会在以后的文章中提供更多细节。
一个提示: 有趣的是可以通过Socket编程合并新的Java IO(NIO.2)。 这些是较前沿的功能。 在这里,我们已经包括的基础远远超过以后的文章。
结论
正如我们所看到的,通过套接创建客户端/服务器通信并不难。 有兴趣的读者可重点关注,特别是theServerSocket类,因为这个类是非常强大的,并且可被用来创建你自己定制的服务器或重新塑造一个HTTP,FTP服务器。有趣的是,创建自己的HTTP服务器可以是一个很好的学习经验,这也并不难,至少在Java中。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值