Java的网络编程主要涉及到的内容是Socket编程,那么什么是Socket呢?简单地说,Socket,套接字,就是两台主机之间逻辑连接的端点。TPC/IP协议是传输层协议,主要解决数据如何在网络中传输,而HTTP是应用层协议,主要解决如何包装数据。Socket,本质上就是一组接口,是对TCP/IP协议的封装和应用(程序员层面上)。
一、整体流程
Socket编程主要涉及到客户端和服务器端两个方面,首先是在服务器端创建一个服务器套接字(ServerSocket),并把它附加到一个端口上,服务器从这个端口监听连接。端口号的范围是0到65536,但是0到1024是为特权服务保留的端口号,我们可以选择任意一个当前没有被其他进程使用的端口。
客户端请求与服务器进行连接的时候,根据服务器的域名或者IP地址,加上端口号,打开一个套接字。当服务器接受连接后,服务器和客户端之间的通信就像输入输出流一样进行操作。

二、案例
1. 案例一
下面是一个客户端和服务器端进行数据交互的简单例子,客户端输入正方形的边长,服务器端接收到后计算面积并返回给客户端,通过这个例子可以初步对Socket编程有个把握。
- 服务器端
public class SocketServer {
public static void main(String[] args) throws IOException {
// 端口号
int port = 7000;
// 在端口上创建一个服务器套接字
ServerSocket serverSocket = new ServerSocket(port);
// 监听来自客户端的连接
Socket socket = serverSocket.accept();
DataInputStream dis = new DataInputStream(
new BufferedInputStream(socket.getInputStream()));
DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(socket.getOutputStream()));
do {
double length = dis.readDouble();
System.out.println("服务器端收到的边长数据为:" + length);
double result = length * length;
dos.writeDouble(result);
dos.flush();
} while (dis.readInt() != 0);
socket.close();
serverSocket.close();
}
}
- 客户端
public class SocketClient {
public static void main(String[] args) throws UnknownHostException, IOException {
int port = 7000;
String host = "localhost";
// 创建一个套接字并将其连接到指定端口号
Socket socket = new Socket(host, port);
DataInputStream dis = new DataInputStream(
new BufferedInputStream(socket.getInputStream()));
DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(socket.getOutputStream()));
Scanner sc = new Scanner(System.in);
boolean flag = false;
while (!flag) {
System.out.println("请输入正方形的边长:");
double length = sc.nextDouble();
dos.writeDouble(length);
dos.flush();
double area = dis.readDouble();
System.out.println("服务器返回的计算面积为:" + area);
while (true) {
System.out.println("继续计算?(Y/N)");
String str = sc.next();
if (str.equalsIgnoreCase("N")) {
dos.writeInt(0);
dos.flush();
flag = true;
break;
} else if (str.equalsIgnoreCase("Y")) {
dos.writeInt(1);
dos.flush();
break;
}
}
}
socket.close();
}
}
2. 案例二
可以看到上面的服务器端程序和客户端程序是一对一的关系,为了能让一个服务器端程序能同时为多个客户提供服务,可以使用多线程机制,每个客户端的请求都由一个独立的线程进行处理。下面是改写后的服务器端程序。
public class SocketServerM {
public static void main(String[] args) throws IOException {
int port = 7000;
int clientNo = 1;
ServerSocket serverSocket = new ServerSocket(port);
// 创建线程池
ExecutorService exec = Executors.newCachedThreadPool();
try {
while (true) {
Socket socket = serverSocket.accept();
exec.execute(new SingleServer(socket, clientNo));
clientNo++;
}
} finally {
serverSocket.close();
}
}
}
class SingleServer implements Runnable {
private Socket socket;
private int clientNo;
public SingleServer(Socket socket, int clientNo) {
this.socket = socket;
this.clientNo = clientNo;
}
@Override
public void run() {
try {
DataInputStream dis = new DataInputStream(
new BufferedInputStream(socket.getInputStream()));
DataOutputStream dos = new DataOutputStream(
new BufferedOutputStream(socket.getOutputStream()));
do {
double length = dis.readDouble();
System.out.println("从客户端" + clientNo + "接收到的边长数据为:" + length);
double result = length * length;
dos.writeDouble(result);
dos.flush();
} while (dis.readInt() != 0);
} catch (IOException e) {
e.printStackTrace();
} finally {
System.out.println("与客户端" + clientNo + "通信结束");
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
上面改进后的服务器端代码可以支持不断地并发响应网络中的客户请求。关键的地方在于多线程机制的运用,同时利用线程池可以改善服务器程序的性能。