互联网协议(Internet Protocol, IP) 用来唯一地标识互联网上的计算机。IP地址由四段用点隔开的0~255的十进制数组成。由于IP地址不容易记住,所以经常将它们映射成 域名(Domain Name)这样有含义的名字,如 www.baidu.com。在互联网上有专门的域名服务器(Domain Name Server, DNS)把主机的名字转换成 IP 地址。IP协议是在互联网中从一台计算机向另一台计算机传输数据的一种低层协议,数据是以包的形式封装的。两个 IP 一起使用的叫高层的协议是传输控制协议(Transmission Control Protocol, TCP)和 用户数据报协议(User Datagram Protocol, UDP)。其中 TCP 能够让两台主机建立连接并交换数据流,TCP确保数据的传送,也确保数据包以它们发送的顺序正确传送,TCP协议基于流通信,传输过程是 无损的 和可靠的。UDP是一种用在IP之上的标准的、低开销的、无连接的、主机对主机的协议,但是UDP不能保证传输没有丢失。
套接字(Socket)是两台主机之间逻辑链接的端点,可以用来发送和接收数据。
要创建一个服务器,需要创建一个服务器套接字(Server Socket),并把它附加到一个端口上,服务器从这个端口监听连接。端口表示套接字上的TCP服务,范围是 0~65536, 但是0~1024是为特权服务保留的端口号。距离来说,电子邮件服务器运行在25端口上,web服务器运行在80端口上。创建代码及监听代码如下:
ServerSocket serverSocket = new ServerSocket(port); //创建服务器套接字并附加到port端口上 (0<=port<=65536)
Socket socket = serverSocket.accept(); //创建服务器套接字后,服务器使用此语句监听链接。此语句会一直等待,直到一个客户端连接到服务器套接字。
客户端执行下面语句,请求与服务器进行连接:
Socket socket = new Socket(serverName, port); // serverName:服务器的互联网主机名或IP地址。 port:端口
Socket socket = new Socket("130.254.204.234", 8000); //i.e.
Socket socket = new Socket("www.baidu.com", 8000); //i.e.
程序可以使用主机名 localhost 或者 IP 地址 127.0.0.1 来引用客户端正在运行的计算机。
通过创建输入和输出流来进行服务器和客户端之间的数据交换:
InputStream input = socket.getInputStream();
OutputStream output = socket.getOutputStream();
____________________________________________________________________________________________________________________________________________
下面给出一个客户端程序和一个服务器程序。客户端向服务器发送圆半径,服务器接收数据,并用它来生成面积,然后将结果返回给客户端。客户端在控制台上显示结果。
服务器代码:
import java.awt.*;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.swing.*;
public class Server extends JFrame {
private JTextArea jta = new JTextArea();
public static void main(String[] args) {
new Server();
}
public Server() {
setLayout(new BorderLayout());
add(new JScrollPane(jta), BorderLayout.CENTER);
setTitle("Server");
setSize(500, 300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
try {
ServerSocket serverSocket = new ServerSocket(8000);
jta.append("Server started at " + new Date() + '\n');
Socket socket = serverSocket.accept();
DataInputStream inputFromClient = new DataInputStream(socket.getInputStream());
DataOutputStream outputToClient = new DataOutputStream(socket.getOutputStream());
while(true) {
double radius = inputFromClient.readDouble();
double area = radius * radius * Math.PI;
outputToClient.writeDouble(area);
jta.append("Radius received from client: " + radius + '\n');
jta.append("Area found: " + area + '\n');
}
} catch(IOException ex) {
System.err.println(ex);
}
}
}
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.awt.*;
import javax.swing.*;
public class Client extends JFrame {
private JTextField jtf = new JTextField();
private JTextArea jta = new JTextArea();
private DataOutputStream toServer;
private DataInputStream fromServer;
public static void main(String[] args) {
new Client();
}
public Client() {
JPanel p = new JPanel();
p.setLayout(new BorderLayout());
p.add(new JLabel("Enter radius"), BorderLayout.WEST);
p.add(jtf, BorderLayout.CENTER);
jtf.setHorizontalAlignment(JTextField.RIGHT);
setLayout(new BorderLayout());
add(p, BorderLayout.NORTH);
add(new JScrollPane(jta), BorderLayout.CENTER);
jtf.addActionListener(new TextFieldListener());
setTitle("Client");
setSize(500, 300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
try {
Socket socket = new Socket("localhost", 8000);
// Socket socket = new Socket("ip", 8000);
fromServer = new DataInputStream(socket.getInputStream());
toServer = new DataOutputStream(socket.getOutputStream());
} catch(IOException ex) {
jta.append(ex.toString() + '\n');
}
}
private class TextFieldListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
try {
double radius = Double.parseDouble(jtf.getText().trim());
toServer.writeDouble(radius);
toServer.flush();
double area = fromServer.readDouble();
jta.append("Radius is " + radius + '\n');
jta.append("Area received from server is " + area + '\n');
} catch(IOException ex) {
System.out.println(ex);
}
}
}
}
____________________________________________________________________________________________________________________________________________
可以使用该类获取客户端的主机名和IP地址:
InetAddress inetAddress = socket.getInetAddress();
inetAddress.getHostName();
inetAddress.getHostAddress();
一个服务器同时被多个客户端连接是非常常见的,可以使用线程处理服务器上的多个客户的同时访问。
package socket;
import java.util.*;
import java.awt.*;
import javax.swing.*;
import java.net.*;
import java.io.*;
public class MultiThreadServer extends JFrame {
private JTextArea jta = new JTextArea();
public static void main(String[] args) {
new MultiThreadServer();
}
public MultiThreadServer() {
setLayout(new BorderLayout());
add(new JScrollPane(jta), BorderLayout.CENTER);
setTitle("MultiThreadServer");
setSize(500, 300);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
try {
ServerSocket serverSocket = new ServerSocket(8000);
jta.append("Server started at " + new Date() + '\n');
int clientNo = 1;
while(true) {
Socket socket = serverSocket.accept();
jta.append("Starting thread for client " + clientNo + " at " + new Date() + '\n');
InetAddress inetAddress = socket.getInetAddress();
jta.append("Client " + clientNo + "'s host name is " + inetAddress.getHostName() + "\n");
jta.append("Client " + clientNo + "'s host address is " + inetAddress.getHostAddress() + "\n");
HandleAClient task = new HandleAClient(socket);
new Thread(task).start();
clientNo++;
}
} catch(IOException ex) {
System.err.println(ex);
}
}
class HandleAClient implements Runnable {
private Socket socket;
public HandleAClient(Socket socket) {
this.socket = socket;
}
public void run() {
try {
DataInputStream inputFromClient = new DataInputStream(socket.getInputStream());
DataOutputStream outputToClient = new DataOutputStream(socket.getOutputStream());
while(true) {
double radius = inputFromClient.readDouble();
double area = radius * radius * Math.PI;
outputToClient.writeDouble(area);
jta.append("radius received from client " + radius + '\n');
jta.append("Area found " + area + '\n');
}
} catch(IOException ex) {
System.err.println(ex);
}
}
}
}