网络编程实际上就是通过套接字进行连接后进行通信,本质还是程序进行IO操作。
所谓套接字,实际上就是IP地址加上端口号的组合,通过套接字,可以连接到网络中某一台计算机的某一个进程。
下面就是客户端和服务器的简单例子,这个简单的例子中,服务器只能和一个客户端进行通信,并且当客户端连接结束后,客户端和服务器同时停止。
-
简单的客户端服务器模型
我们首先来看一下服务器的代码,服务器的执行一共有五个步骤
实例化一个ServerSocket对象
使用Socket对象接收ServerSocket示例执行accept()方法的返回值,该方法会一直被被阻塞,建立socket连接
和客户端进行通信
关闭Socket连接
关闭ServerSocket连接
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerDemo {
public static final int PORT = 8005;
public static void main(String[] args) throws IOException {
ServerSocket s = new ServerSocket(PORT);
System.out.println("Started:" + s);
try{
Socket socket = s.accept();
try{
System.out.println("Connection accepted: " + socket);
BufferedReader in =
new BufferedReader(
new InputStreamReader(socket.getInputStream()));
PrintWriter out =
new PrintWriter(
new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())));
while(true){
String str = in.readLine();
System.out.println("Echoing: " + str);
if(str.equals("END")) break;
out.println("Receiving: " + str);
out.flush();
}
} finally {
System.out.println("closing...");
socket.close();
}
} finally {
s.close();
}
}
}
客户端连接相对来说比较简单,连接步骤有三个
建立Socket连接,其中需要传入两个参数,分别是服务器的IP地址和端口号
和服务器进行通信
关闭Socket连接
import java.io.*;
import java.net.InetAddress;
import java.net.Socket;
public class ClientDemo {
public static void main(String[] args) throws IOException {
InetAddress addr = InetAddress.getByName("127.0.0.1");
System.out.println("addr=" + addr);
Socket socket = new Socket(addr, ServerDemo.PORT);
try{
System.out.println("socket=" + socket);
BufferedReader in =
new BufferedReader(new InputStreamReader(System.in));
BufferedReader serverIn =
new BufferedReader(new InputStreamReader(socket.getInputStream()));
PrintWriter out =
new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())));
while(true){
String line = in.readLine();
out.println(line);
out.flush();
String serverInfo = serverIn.readLine();
if(serverInfo != null){
System.out.println(serverInfo);
}
if(line.equals("END")) break;
}
} finally {
System.out.println("closing...");
socket.close();
}
}
}
上述例子中ServerDemo是服务器类,ClientDemo是客户端类,连接建立后,客户端可以给服务器发送消息,知道服务器收到"END"字符串的时候停止服务。
-
可以服务多个客户端的服务器模型
上述的服务器模型只能对一个客户端进行连接,当连接断开之后,服务器程序也会停止。当然,一个好用的服务器需要随时随刻都能服务尽可能多的客户端,我们只需要对上述代码稍加修改,就能实现一个功能稍微复杂的服务器模型。
可以服务多个客户端的服务器模型的基本逻辑是,持续监听accept()方法是否有返回,一旦有返回,就接收其返回的Socket对象,并且新建立一个线程执行服务器逻辑,然后再持续监听accept()方法,重复上述步骤。
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class MultiServerDemo {
public static final int PORT = 8005;
public static void main(String[] args) throws IOException {
ServerSocket s = new ServerSocket(PORT);
System.out.println("Server Started");
try {
while(true){
Socket socket = s.accept();
try {
new ServerOneDemo(socket);
} catch (IOException e) {
socket.close();
}
}
} finally {
s.close();
}
}
}
class ServerOneDemo extends Thread{
private Socket socket;
private BufferedReader in;
private PrintWriter out;
public ServerOneDemo(Socket s) throws IOException{
socket = s;
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
// 启动线程
start();
}
public void run(){
try {
while(true){
String str = in.readLine();
System.out.println("Echoing: " + str);
if(str.equals("END")) break;
out.println("Receiving: " + str);
out.flush();
}
System.out.println("closing...");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
上述代码分为两个类来编写,其中MultiServerDemo类负责监控accpet()方法的返回情况,一点有返回值就会实例化一个ServerOneDemo。ServerOneDemo类继承了Thread,实现了run()方法,在构造函数中接收一个Socket对象作为参数,并且执行start()方法自动开启线程。执行代码,可以发现,只要我们不结束程序,那么就可以一直和客户端建立连接并接收消息。