网络编程
预备知识
- 要学习网络编程首先要了解一些计算机网络的知识。
- 计算机网络三大要素:通信网络+服务器+客户端
- 服务器一般使用Linux、Unix或Windows Server等操作系统
- 不同于之前的非网络程序,网络编程既要求编写客户端应用程序有要求编写服务器端应用程序
- 遵循TCP/IP协议
- 应用层
- 传输层
- 网络层
- 链路层
- 比起UDP只发送不管对方收到没,TCP事先建立连接双向数据传输更可靠
- 网络上两台计算机之间的通信,本质上是两个网络应用程序之间的通信
- 统一资源定位符URL语法 protocol://[:port]path[?query]
Java API 提供的因特网地址类java.net.InetAddress
- 使用方法
import java.io.IOException;
import java.net.*;
public class JInetAddress {
public static void main(String[] args) {
try {
InetAddress local = InetAddress.getLocalHost(); //获取本机的因特网地址对象
System.out.println("通过getLoaclHost获得本机因特网网址对象:"+local);
System.out.println("获取主机名:"+local.getHostName());
System.out.println("获取主机IP地址:"+local.getHostAddress());
System.out.println(local.isReachable(1));
String cauWeb = "www.cau.edu.cn"; // 中国农业大学网站的主机名
InetAddress cau = InetAddress.getByName(cauWeb); // 根据主机名创建对象
System.out.println("通过主机名得本机因特网网址对象:"+cau);
System.out.println("获取主机名:"+cau.getHostName());
System.out.println("获取主机IP地址:"+cau.getHostAddress());
}
catch (UnknownHostException e){
e.printStackTrace();
}
catch (IOException e) {
System.out.println(e.getMessage());
}
}
}
- 实例化一个地址对象–通过静态方法 获取本机的因特网地址对象
InetAddress local = InetAddress.getLocalHost();
- 实例化一个地址对象–通过静态方法 根据主机名创建对象
InetAddress cau = InetAddress.getByName("www.cau.edu.cn");
- 调用非静态方法查看主机名
System.out.println("获取主机名:"+local.getHostName());
- 调用非静态方法查看IP地址
System.out.println("获取主机IP地址:"+local.getHostAddress());
- 调用非静态方法查看是否连通
System.out.println(local.isReachable(1));
- 注意InetAddress可能会抛出的IOException是勾选异常,必须处理
java.net.URL 访问网络资源
import java.net.*;
import java.io.*;
public class JWebPageTest {
public static void main(String[] args) {
try { // 处理可能出现的勾选异常IOException
String url_str = "http://www.cau.edu.cn/index.html";
//String url_str = "https://www.oracle.com/technetwork/java/index.html";
URL url = new URL(url_str);
System.out.println("从网页读取信息: " +url);
InputStreamReader in = new InputStreamReader(url.openStream(),"UTF-8");
char cbuf[] = new char[3000]; //读3000字符
int len = in.read(cbuf);
for(int n=0;n<len;n++) {
System.out.print(cbuf[n]);
}
System.out.println("......\n以上是从网页读出的信息。");
System.out.println("字符编码是:"+in.getEncoding());
in.close();
}
catch(IOException e) { e.printStackTrace(); } // 捕捉并处理勾选异常
}
}
一个网络编程的例子(TCP连接)
功能简介:
- 客户端:向服务端发送请求,从服务端获取当前时间
- 服务端:监听端口,如果检测到有来自某客户端的请求,就向该客户端输出当前时间
基本思路
- 服务端与客户端之间建立TCP连接,使用Socket
- 利用Java.net.Socket搭建客户端应用程序
- 利用Java.net.ServerSocket搭建服务服务器应用程序
- 服务端应用程序应该具有处理客户端请求的算法(方法)
- 应用层协议规定该系统的端口8989(这个端口是我规定的,你可以在自己的协议中指定端口)
- 服务器遵守协议,监听端口8989
- 客户端事先知道服务端的ip地址,并且遵守应用层协议,通过8989端口请求服务端
代码实现
- 服务端
import java.io.*;
import java.net.*;
import java.time.LocalDateTime;
public class JTimeServerST {
public static void main(String[] args) {
// 服务端
try {
ServerSocket ss = new ServerSocket(8989);
System.out.println("启动服务器...本端[ "+ss.getLocalSocketAddress()+" ]...");
while (true) {
Socket s = ss.accept();
System.out.println("收到请求...并和远端["+s.getInetAddress()+ "]建立了TCP连接");
timeService(s);
}
}
catch(IOException e) {
System.out.println("IOException1");
}
}
public static void timeService(Socket s) {
System.out.println("timeService()执行中...");
InputStreamReader in = null;
OutputStreamWriter out = null;
try {
// 接受服务请求
in = new InputStreamReader(s.getInputStream());
char[] buf = new char[7];
in.read(buf,0,buf.length);
String request = new String(buf);
System.out.println("客户端的服务请求是" + request);
// 发送服务响应
out = new OutputStreamWriter(s.getOutputStream());
LocalDateTime t = LocalDateTime.now();
System.out.println(t);
String response = String.format("Hello,%s! %s", request, t);
out.write(response,0,response.length());
out.flush();
}
catch(IOException e) {
System.out.println("IOException2");
}
finally {
try {
if(in != null) in.close();
if(out != null) out.close();
if(s != null) s.close();
}
catch(IOException e) {
System.out.println("IOException3");
}
}
}
}
- 客户端
import java.net.*;
import java.io.*;
public class JTimeClient {
public static void main(String[] args) {
//客户端
Socket s = null;
InputStreamReader in = null;
OutputStreamWriter out = null;
try {
s = new Socket("192.168.136.129",8989);
System.out.println("本端 ["+s.getLocalSocketAddress()+"] connected......");
System.out.println("远端 ["+s.getRemoteSocketAddress()+"] connected......");
// 发送服务请求
out = new OutputStreamWriter(s.getOutputStream());
int id = (int)(Math.random()*10);
String request = "Clinet" + id;
out.write(request, 0, request.length());
out.flush();
System.out.println("发送的请求是:"+request);
//接受服务响应
in = new InputStreamReader(s.getInputStream());
char[] buf = new char[100];
in.read(buf, 0, buf.length);
String response = new String(buf);
System.out.println("接受的服务相应是:"+response);
}
catch (IOException e) {
System.out.println("IOException");
}
finally {
try {
if(in != null)
in.close();
if(out != null)
out.close();
if(s != null)
s.close();
System.out.println("客户端程序退出");
}
catch(IOException e) {
System.out.println("IOException");
}
}
}
}
运行
- 先启动服务器
- 注:此时没有客户端没有客户端接入
-
运行客户端
-
此时的服务端
服务端部分代码解析
- 启动服务器 创建一个套接字,监听8989端口
ServerSocket ss = new ServerSocket(8989);
- 在监听端口时发现有TCP连接,确认连接,返回套接字
Socket s = ss.accept();
- 从套接字
Socket
中获取输入流对象并进行包装,使之成为更强大的InputStreamReader
对象,以便服务端读取来自客户端的信息,即接收来自客户端的服务请求
in = new InputStreamReader(s.getInputStream());
- 从套接字
Socket
中获取输入流对象并进行包装,是之成为更强大的OutputStreamWriter
对象,以便服务器输出信息到客户端,即向客户端发送服务响应
out = new OutputStreamWriter(s.getOutputStream());
客户端部分代码解析
- 指定主机IP、端口建立TCP连接
s = new Socket("192.168.136.129",8989);
- 从套接字
Socket
中获取到输出流对象并进行包装,使之成为更加强大的OutputStreamWriter
对象,以便从客户端输出信息到服务器,即向服务器发送请求
out = new OutputStreamWriter(s.getOutputStream());
- 从套接字
Socket
中获取输入流对象并进行包装,使之成为成为更强大的InputStreamReader
对象,以便客户端读取来自服务器信息,即接收服务响应
in = new InputStreamReader(s.getInputStream());
另一个网络编程的例子(基于UDP协议)
说明
- 基于UDP协议的通信是单向连接,发送方只管发送,接受方只管接受,连接不是同时维持的
- JavaAPI提供java.net.DatagramSocket类包装数据
- JavaAPI提供数据报套接字类java.net.DatagramSocket类来发送接受数据(DatagramSocket的对象)
发送方
import java.net.*;
import java.io.*;
public class JUDPSender {
public static void main(String[] args) {
try {
System.out.print("Send data to...... ");
// 发送前的准备工作:准备好接收方网址、端口和需发送的数据
InetAddress udpReceiver = InetAddress.getByName("localhost");
int port = 9000;
String msg = "Hello, World!";
byte buf[] = msg.getBytes(); //字符流转字节流
DatagramPacket pack = new DatagramPacket(buf, buf.length, udpReceiver, port);
DatagramSocket ds = new DatagramSocket();
ds.send(pack);
ds.close();
System.out.println("Done");
} catch(IOException e) { System.out.println( e.getMessage() ); }
} }
接收方
import java.net.*;
import java.io.*;
public class JUDPReceiver {
public static void main(String[] args) {
try {
System.out.println("Receive data ......\n");
byte buf[] = new byte[128];
DatagramPacket pack = new DatagramPacket(buf, buf.length);
DatagramSocket ds = new DatagramSocket(9000);
ds.receive(pack);
// 分析接收到的数据报包裹,例如发送方的网址、端口和数据等
InetAddress udpSender = pack.getAddress();
int port = pack.getPort();
String msg = new String(pack.getData(), 0, pack.getLength());
System.out.println("Receive data from " +udpSender +":" +port);
System.out.println("所接收到的数据:" +msg);
}
catch(IOException e) { System.out.println(e.getMessage()); }
} }
运行
- 发送方
- 接受方