Java网络编程
开发第一个网络程序
java.net包提供了网络编程有关的开发工具类,在此包中有一下两个主要的核心操作类。
- ServerSocket类:是一个封装支持的TCP协议的操作类,主要工作在服务器端,用于接收客户端请求;
- Socket类:也是一个封装了TCP协议的操作类,每一个Socket对象都表示一个客户端。
ServerSocket类的常用方法
No. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public ServerSocket(int port) throws IOException | 构造 | 开辟一个指定的端口监听,一般使用5000以上的端口 |
2 | public Socket accept() throws IOException | 普通 | 服务器端接收客户端请求,通过Socket返回 |
3 | public void close() throws IOException | 普通 | 关闭服务器端 |
Socket类的常用方法
No. | 方法名称 | 类型 | 描述 |
---|---|---|---|
1 | public Socket(String host, int port) throws UnknownHostException,IOException | 构造 | 指定要连接的主机(IP地址)和接口,host代表主机的IP地址,如果是本机直接访问,则使用localhost(127.0.0.1)代替IP |
2 | public OutputStream getOutputStream() throws IOException | 普通 | 取得指定客户端的输出对象,使用PrintStream操作 |
3 | public InputStream getInputStream() throws IOException | 普通 | 从指定的客户端读取数据,使用Scanner操作 |
在客户端,程序可以通过Socket类的getInputStream()方法,取得服务器的输出信息,在服务器端可以通过getOutputStream()方法取得客户端的输出信息。
例:定义服务器端
package Project.Study.ServerSocket;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Test1 {
public static void main(String[]args)throws Exception{
ServerSocket server = new ServerSocket(9999); //所有的服务器必须有接口
System.out.println("等待客户端连接...");
Socket socket = server.accept(); //等待客户端连接
//OutputStream并不方便进行内容的输出,所以利用打印流完成输出
PrintStream out = new PrintStream(socket.getOutputStream());
out.println("Hello World!!!"); //输出数据
out.close();
socket.close();
server.close();
}
}
//结果:
//等待客户端连接...(此处讲出现堵塞情况,一直到客户端连接后才会继续执行)
本程序在本机的9999端口上设置了一个服务器的监听操作(accept()方法表示打开服务器监听),这样当有客户端通过TCP连接方式连接到服务器端后,服务器端将利用PrintStream输出数据,当数据输出完毕后该服务器就将关闭,所以本次定义的服务器只能处理一次客户端的请求。
例:编写客户端
package Project.Study.Socket;
import java.net.Socket;
import java.util.Scanner;
public class Test1 {
public static void main(String[]args)throws Exception{
Socket socket = new Socket("localhost",9999);//连接服务器
//取得客户端的输入数据流对象,表示接收服务器端的输出信息
Scanner scanner = new Scanner(socket.getInputStream());
scanner.useDelimiter("\n"); //设置分隔符
if (scanner.hasNext()){ //判断是否有数据
System.out.println("回应数据:"+scanner.next());//取出数据
}
scanner.close();
socket.close();
}
}
//结果:
//回应数据:Hello World!!!
在TCP程序中,每一个Socket对象都表示一个客户端的信息,所以客户端程序要连接也必须依靠Socket对象操作。在实例化Socket类对象时必须设置要连接的主机名称(本机为localhost,或者填写IP地址)以及连接端口号,当连接成功后就可以利用Scanner进行输入流数据的读取,接收服务器端的回应信息。
网络开发的经典模型——Echo程序
例:实现服务器端
package Project.Study.ServerSocket;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Test2 {
public static void main(String[]args)throws Exception{
ServerSocket serverSocket = new ServerSocket(9999); //定义连接端口
Socket socket = serverSocket.accept(); //等待客户连接
//得到客户端输入的数据以及向客户端输出数据的对象,利用扫描流接收,打印流输出
Scanner scanner = new Scanner(socket.getInputStream());
PrintStream out = new PrintStream(socket.getOutputStream());
boolean flag = true; //设置循环标记
while (flag){
if (scanner.hasNext()){ //是否有内容输入
String str = scanner.next().trim(); //得到客户端发送的内容,并删除空格
if (str.equalsIgnoreCase("####")){ //程序结束标记
out.println("再见!"); //输出结束信息
flag = false; //退出循环
}else{ //回应输入信息
out.println("ECHO:"+str); //加上前缀返回
}
}
}
scanner.close();
out.close();
socket.close();
serverSocket.close();
}
}
该服务器端存在一个缺陷,它是单线程的网络应用,所有的操作都是在主线程上进行的开发,所以只能连接一个客户端,不能连接其他客户端。
例:修改服务器端(使其可以同时处理多个客户端的连接操作)
package Project.Study.ServerSocket;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
class EchoThread implements Runnable{ //建立线程类
private Socket socket; //每个线程处理一个客户端
public EchoThread(Socket socket){ //创建线程对象时传递Socket
this.socket=socket;
}
@Override
public void run(){
try { //每个线程对象取得各自Socket的输入流与输出流
Scanner scanner = new Scanner(socket.getInputStream());
PrintStream out = new PrintStream(socket.getOutputStream());
boolean flag = true; //控制多次接收操作
while (flag){
if (scanner.hasNext()){ //判断是否有内容
String str = scanner.next().trim(); //得到客户端发送的内容
if (str.equalsIgnoreCase("###")){//程序结束
out.println("再见!");
flag = false; //退出循环
}else{ //应该回应输入信息
out.println("ECHO:"+str); //回应信息
}
}
}
scanner.close();
out.close();
socket.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
public class Test3 {
public static void main(String[]args)throws Exception{
ServerSocket serverSocket = new ServerSocket(9999);//在9999端口上监听
boolean flag = true; //循环标记
while (flag){ //接收多个客户端请求
Socket socket = serverSocket.accept(); //客户端连接
new Thread(new EchoThread(socket)).start(); //创建并启动新线程
}
serverSocket.close();
}
}
例:定义客户端
package Project.Study.Socket;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Test2 {
public static void main(String[]args)throws Exception{
Socket socket = new Socket("localhost",9999); //服务器地址和端口
Scanner input = new Scanner(System.in); //键盘输入数据
//利用Scanner包装客户端输入数据(服务器端输出),PrintStream包装客户端输出数据
Scanner scanner = new Scanner(socket.getInputStream());
PrintStream out = new PrintStream(socket.getOutputStream());
input.useDelimiter("\n"); //设置键盘输入分隔符
scanner.useDelimiter("\n"); //设置回应数据分隔符
boolean flag = true; //循环标志
while (flag){
System.out.println("请输入要发送的数据:");
if (input.hasNext()){ //判断键盘是否输入数据
String str = input.next().trim(); //取得键盘输入的数据
out.println(str); //发送数据到服务器端
if (str.equalsIgnoreCase("###")){ //结束标记
flag = false; //结束循环
}
if (scanner.hasNext()){ //服务器有回应
System.out.println(scanner.next()); //输出回应数据
}
}
}
input.close();
scanner.close();
out.close();
socket.close();
}
}
结果博主就不发了,自己在两个端口自问自答感觉有点怪。