客户机/服务器应用程序模型通常被看作是位于远处的,高功率计算装置,其存储大量数据的业务逻辑。而用户界面在相对便宜的机器上由客户端软件进行处理。这种观点有些模糊,因为任何服务机器的请求可能会被称为服务器。尽管服务器等待客户端开始对话,在某些情况下,在同一个程序既可以充当客户端也可以作为服务器。 从这个意义上说,一台机器可以作为网络客户端进行通信,也可经过一个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中。
客户端套接基础知识
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中。