java中和网络编程有关的基本API位于java.net包中,该包中包含了基本的网络编程实现,该包是网络编程的基础。
该包中既包含基础的网络编程类,也包含封装后的专门处理WEB相关的处理类。
我们先简单了解一下,服务器跟客户端之间的联系;
在Java语言中,对于TCP方式的网络编程提供了良好的支持,在实际实现时,以java.net.Socket类代表客户端连接,以java.net.ServerSocket类代表服务器端连接。在进行网络编程时,底层网络通讯的细节已经实现了比较高的封装,所以在程序员实际编程时,只需要指定IP地址和端口号码就可以建立连接了。
目标 1 :实现简单的服务端,客户端,服务端向客户端发送 “hello world”后关闭服务
首先实现服务端,借助于java.net.ServerSocket类
import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class HelloServer {
public static void main(String args[]) throws IOException{
ServerSocket server = null; //定义一个服务端
Socket client = null; //定义一个客服端用来接受
PrintStream out = null; //定义一个打印输出流,向服务端发送信息的同时输出到屏幕,方便查看
server = new ServerSocket(8090); //使用8090端口
System.out.println("服务器运行,等待客户端链接。");
client = server.accept(); //进入阻塞状态,服务端等待客服端的接入
String str = "hello world";
out = new PrintStream(client.getOutputStream()); //得到客服端的输出流,实例化打印输入流
out.println(str); //向客户端输出信息,并打印出来
//关闭服务
client.close();
server.close();
}
}
接下来简单的看一下,java API中的ServerSocket类
构造方法摘要 | |
---|---|
ServerSocket() 创建非绑定服务器套接字。 |
|
ServerSocket(int port) 创建绑定到特定端口的服务器套接字。 |
|
ServerSocket(int port, int backlog) 利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。 |
|
ServerSocket(int port, int backlog,InetAddress bindAddr) 使用指定的端口、侦听 backlog 和要绑定到的本地 IP 地址创建服务器。 |
这里我们调用了第二个构造函数,指定了所用端口
accept()函数则等到客户端的接入
public Socket accept() throws IOException
-
侦听并接受到此套接字的连接。此方法在连接传入之前一直阻塞。
创建新套接字
s
,如果存在安全管理器,则使用s.getInetAddress().getHostAddress()
和s.getPort()
作为参数调用安全管理器的checkAccept
方法,以确保允许该操作。这可能会导致 SecurityException 异常。
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;
public class HelloClient {
public static void main(String args[]) throws UnknownHostException, IOException{
Socket client = null; //定义一个客户端
client = new Socket("localhost",8090); //使用刚才定义的8090接口
BufferedReader buf = null; //定义一个缓存输入流,用于一次接受信息
buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
String str = buf.readLine();
System.out.println("服务器输出内容:" + str);
//关闭服务
buf.close();
client.close();
}
}
定义一个缓存输入流,连接上服务器后直接读取数据即可。
目标 2:实现Echo服务
客户端链接上服务器后,首先先服务器发送信息(一个字符串),服务器接受后在字符串前加上“Echo”并返回给客户端,客户端输出
实现Echo服务,服务器和客户端之间的链接同上,不同是服务要接受信息并加以处理;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class EchoServer {
public static void main(String args[]) throws IOException{
ServerSocket server = null;
Socket client = null;
PrintStream out = null;
BufferedReader buf = null; //定义一个输入流,从客户端可到输入
server = new ServerSocket(8090);
while(true){
System.out.println("服务器运行,等待客户端链接。");
client = server.accept(); //等待用户链接,进入阻塞状态
out = new PrintStream(client.getOutputStream());
//准备接受客户端的输入信息
buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
boolean flag = true;
//标记位表示可以一直接受并回应信息
while(flag){
String str = buf.readLine();
if (str == null || "".equals(str)){
flag = false;
}else{
if ("bye".equals(str)){
flag = false;
}else{
out.println("Echo: " + str);
}
}
}
client.close();
}
}
}
当客户端输入bye时则表示离开
以下是客户端的实现
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class EchoClient {
public static void main(String args[]) throws UnknownHostException, IOException{
Socket client = null;
client = new Socket("localhost",8090);
BufferedReader buf = null; //定义一个缓冲输入流,一次性从服务端接受信息
PrintStream out = null; //向服务端发送信息的输出流
BufferedReader input = null; //从系统读入字符串的输入流
input = new BufferedReader(new InputStreamReader(System.in));
buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
out = new PrintStream(client.getOutputStream());
boolean flag = true;
while(flag){
System.out.println("输入信息");
String str = input.readLine(); //从系统输入得到信息
out.println(str); //将得到的信息发送给服务端
if ("bye".equals(str)){
flag = true; //当字符串为 bye 是终止输入
}else{
String echo = buf.readLine(); //得到服务端返回的信息
System.out.println(echo);
}
}
buf.close();
client.close();
}
}
不足:以上服务器的实现,虽然达到了我们的目的对客户端实现了交互,但是在同一时间内只能对一个客户端进行处理,也就是说当一个客户端正在跟服务器进行交互时,其他客户端是无法接入的。
目标 3:实现一个可多用户操作的服务器
要 实现一个多用户可同时操作的服务器,我们可以在服务器接受到一个客户端,对它开始处理时开启一个新的线程,交给一个新的线程去是实现我们的服务,什么意思呢
我们先来看 多线程服务器的实现
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class EchoThreadServer {
public static void main(String args[]) throws IOException{
ServerSocket server = null;
Socket client = null;
PrintStream out = null;
BufferedReader buf = null;
server = new ServerSocket(8090);
while(true){
System.out.println("服务器运行,等待客户端链接。");
client = server.accept(); //等待用户链接,进入阻塞状态
new Thread(new EchoThread(client)).start();
}
}
}
这里我们通过启动new Thread(。。。)创建了一个新的线程,run()函数中则是对客户端信息处理的实现,对信息的处理同 目标 2;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
public class EchoThread implements Runnable {
private Socket client = null;
public EchoThread(Socket client) {
this.client = client;
}
public void run() {
PrintStream out = null;
BufferedReader buf = null;
try {
out = new PrintStream(client.getOutputStream());
// 准备接受客户端的输入信息
buf = new BufferedReader(new InputStreamReader(client.getInputStream()));
boolean flag = true;
// 标记位表示可以一直接受并回应信息
while (flag) {
String str = buf.readLine();
if (str == null || "".equals(str)) {
flag = false;
} else {
if ("bye".equals(str)) {
flag = false;
} else {
out.println("Echo: " + str);
}
}
}
client.close();
} catch (Exception e) {
System.out.println(e);
}
}
}