网络编程
Posted AntarcticPenguin
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了网络编程相关的知识,希望对你有一定的参考价值。
Java最大的特性是跨平台性,它的优势在于网络编程Java最大的特性是跨平台性,它的优势在于网络编程
一、网络编程简介
- C/S结构
- 无连接用户的数据报编程
- 面向连接的Socket编程
-
- 访问数据库JDBC
-
- 访问命名目录服务JNDI
-
- 访问邮件服务器JavaMail
-
- 访问其他服务……
- B/S结构
- 服务器端JSP+Servlet
- 客户端HTTP访问先关类
二、使用URLConnection访问Web应用
访问Web应用的,Java中提供的类包括:
- URL,表示要访问的网页地址;
- URLConnection,表示客户端与Web应用之间的链接,建立链接之后就可以读取从来自服务器的数据。
下载百度首页:
public class URLConnectionDemo {
public static void main(String[] args) {
String urlStr = "http://www.baidu.com";
try {
// 创建URL对象表示百度首页地址
URL url = new URL(urlStr);
// 打开网站链接
URLConnection con = url.openConnection();
// 创建输入流对象
BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));//这里面是进行转换的,字节流getInputStream——》字符流InputStreamReader——》缓冲流BufferedReader,从而提高读写的效率
// 创建输出流对象
BufferedWriter writer = new BufferedWriter(new FileWriter(
"D:\\baidu.html"));//相当于创建了一个文件,来保存下载的信息
String s;
while ((s = reader.readLine()) != null) {
writer.write(s + "\n");
}//这个就是从网站里面一行一行下载信息
writer.flush();
writer.close();
reader.close();
} catch (Exception e) {
}
}
}
这样下载的就是一个简单的网页,而没有其他的,比如说图片资源。如果想要下载整个网站,就要进一步编写代码,在下载好之后的页面分析里面的图片资源和一些超链,如果含有的话就继续下载,类似于网络爬虫。这样就可以把整个网站给爬出来了。不过这里面还要注意搜索的超链一般是两到三层,不要太多,这个可以自己设定。这样下载的就是一个简单的网页,而没有其他的,比如说图片资源。如果想要下载整个网站,就要进一步编写代码,在下载好之后的页面分析里面的图片资源和一些超链,如果含有的话就继续下载,类似于网络爬虫。这样就可以把整个网站给爬出来了。不过这里面还要注意搜索的超链一般是两到三层,不要太多,这个可以自己设定。
三、实例:提取网页中感兴趣的内容
举个例子:从当当网查找某一本书的价格,按照书号查询。在现实世界中,有很多实际的应用。比如比价网,对于同一件商品可以道不同的网站上提取价格信息进行比较。另外一个典型的例子,“去哪儿网”能够从多家航空公司网站查找机票。
其中说明了一些网站去爬别的网站的商品价格,就比如说,一家书店网站,同时监控京东上面的书的价格,只要京东上面调节了价格,这个网站同时也同步跟上。而京东也做了相应的措施,是将书的价格做成了一张图片,这样防止别的网站爬其中的数据,可是这并不能避免,因为加上面那个折扣在哪里,别的网站还是可以爬到相关的数据。贼几把厉害。这样有些网站没办法,因为人家要爬我的数据,访问我的数据,服务器在哪里也受不了啊,所以一些网站会做一个日志,提供人家去爬你的信息,给你一些权限,希望减轻服务器的压力。
下面例子,提取当当网书的价格:
public class PriceCompareDemo {
public static void main(String[] args) {
//这个要自己分析人家的网站,价格前后的特征标签,分析出来你要的信息在什么地方
ServerInfo dangdang = new ServerInfo("http://search.dangdang.com/?key=","<span class=\"price_n\">¥","</span>");
String isbn = "9787560619330";//9787302191773";
double price = dangdang.find(isbn);
System.out.printf("当当的价格是:%8.2f",price);
}
}
class ServerInfo{
protected String url = null;
String prefix = null;
String end = null;
public ServerInfo(String url,String prefix,String end){
this.url = url;
this.prefix = prefix;
this.end = end;
}
private String getResult(String key){
return url+key;
}
public double find(String key){
URL url = null;
URLConnection con = null;
BufferedReader reader = null;
try {
// 创建URL对象表示要访问的网站地址
url = new URL(getResult(key));
// 打开网站链接
con = url.openConnection();
InputStream inputStream = con.getInputStream();
// 创建输入流对象
reader = new BufferedReader(new InputStreamReader(inputStream));
String s;
while ((s = reader.readLine()) != null) {
if(s.indexOf(prefix)>=0){
return getPrice(s);
}
}
return -1;
} catch (Exception e) {
e.printStackTrace();
return -1;
}finally{
try {
reader.close();
} catch (IOException e) {
}
}
}
private double getPrice(String info){
int index = info.indexOf(prefix);
int index2 = info.indexOf(end,index);
String price = info.substring(index+prefix.length(),index2);
try{
return Double.parseDouble(price);
}catch(Exception e){
return -1;
}
}
}
四、Socket通信
- 通信包括两个程序:客户端程序和服务器端。
- 服务器端程序工作过程:
-
- 创建ServerSocket对象,指出端口。
-
- 调用ServerSocket对象的accept()方法监听客户端请求,返回一个Socket对象。
-
- 通过Socket对象得到输入流对象,通过Socket对象得到输出流对象。
-
- 调用Socket对象的close()方法关闭连接,调用ServerSocket对象的close()方法关闭连接。
- 客户端程序工作过程:
-
- 创建Socket对象,在创建的时候需要指出服务器端的主机和端口,主机是运行服务器端程序的电脑的IP地址或者域名,端口是服务器端ServerSocket所监听的端口。
-
- 调用Socket对象的connect方法建立与服务器端的连接。
-
- 通过Socket对象得到输入流对象,接收来自服务器端的信息。通过Socket对象得到输出流对象,通过输出流对象向服务器端发送信息。
-
- 调用Socket对象的close方法关闭连接
例子:
服务器端:Server.java
public class Server {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8888);//创建一个服务器绑定到8888端口上去,必须是未被占用的。
Socket s=null;
BufferedReader in=null;
PrintWriter out=null;
try {
s = ss.accept();//接收客户端的请求,只要有请求就会创建一个Socket对象。有了这个对象就可进行下面的获取输入输出流对象
System.out.println("有客户端请求连接,客户端ip地址:"
+ s.getInetAddress().getHostAddress() + ",远程端口:"
+ s.getPort() + ",本地端口:" + s.getLocalPort());
in = new BufferedReader(new InputStreamReader(s.getInputStream()));
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(s.getOutputStream())), true);
while (true) {
String str = in.readLine();// 读取一行,接收客户端的一句话
if (str.equalsIgnoreCase("bye"))
break;//判断是否为bye(不区分大小写),结束与客户端的通话
System.out.println("接收的客户端数据:" + str);
out.println("服务器响应:" + str);//发出一条消息
}
System.out.println("服务器退出");
} finally {
if(out!=null)
out.close();
if(in!=null)
in.close();
if(s!=null)
s.close();
if(ss!=null)
ss.close();
}
}
}
客户端:Client.java
public class Client {
public static void main(String[] args) throws IOException {
Socket s = new Socket("127.0.0.1", 8888);//找到服务器的地址、端口
BufferedReader in = null;
PrintWriter out = null;
try {
//得到输入输出流
in = new BufferedReader(new InputStreamReader(s.getInputStream()));
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(s.getOutputStream())), true);
while (true) {
// 从键盘接收数据
BufferedReader inkey = new BufferedReader(new InputStreamReader(System.in));
System.out.print("从键盘输入数据:");
String str = inkey.readLine();// 读取一行
out.println(str);
if (str.equalsIgnoreCase("bye"))
break;
String msg = in.readLine();
System.out.println("从服务器收到数据:" + msg);
}
System.out.println("客户端退出");
} finally {
if(out!=null)
out.close();
if(in!=null)
in.close();
if(s!=null)
s.close();
}
}
}
首先运行Server.java,让其处于监听状态。然后运行Client.java,在里面输入数据,回车就可以得到服务器返回的数据。同时打开服务器运行窗口会看到客户端的请求。直到客户端输入bye,然后客户端与服务器才进行结束。
想下:如果是远程,只要获取端口就行,就可以远程访问数据了。自己可以试一下,在服务器上创建并运行服务器Server.java,在pc上创建客户端Client.java,然后进行访问。首先运行Server.java,让其处于监听状态。然后运行Client.java,在里面输入数据,回车就可以得到服务器返回的数据。同时打开服务器运行窗口会看到客户端的请求。直到客户端输入bye,然后客户端与服务器才进行结束。
五、聊天室
- 服务器端负责消息的转发,当客户端连接的时候,服务器把这个消息发给聊天室中所有成员,当客户端发送消息的时候,把这个消息也转发给所有成员。
- 服务器端的程序采用多线程的方式:一个线程监听客户的请求,一个线程负责向所有客户端发送消息,对应每个线程的接收客户端信息的线程
- 客户端程序采用多线程的方式:一个线程负责从键盘接收数据向服务器发送,另外一个线程负责从服务器端接收数据。
服务器端:Server.java
public class Server extends Thread {
ServerSocket ss = null;
// 表示客户端
private List<Socket> clients;//由于有很多客户端,所以这里创建了一个链表
// 发送线程
SenderThread sendThread;
private MessageManager messageManager;//消息管理器,用来管理消息,(最下面那个类)
public Server() {
clients = new ArrayList<>();
messageManager = new MessageManager(clients);
sendThread = new SenderThread(messageManager);
sendThread.start();
}
public void run() {
try {
ss = new ServerSocket(8888);
} catch (Exception e) {
System.out.println("端口被占用!");
return;
}
Socket s = null;
BufferedReader in = null;
while (true) {
try {
s = ss.accept();
clients.add(s);
ReaderThread readerThread = new ReaderThread(s, messageManager,
clients);
readerThread.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void stopServer() {
messageManager.addMessage("服务器要关闭了!");
notifyAll();
try{
ss.close();
}catch(Exception e){}
for (Socket s : clients) {
try {
s.close();
} catch (Exception e) {
}
}
}
public static void main(String[] args) throws IOException {
Server server = new Server();
server.start();
Scanner in = new Scanner(System.in);
while (true) {
String str = in.nextLine();
if (str.equals("exit")) {
in.close();
server.stopServer();
System.exit(0);
} else {
System.out.println("无效指令!");
}
}
}
}
class SenderThread extends Thread {
private MessageManager messageManager;
public SenderThread(MessageManager messageManager) {
this.messageManager = messageManager;
}
public void run() {
messageManager.sendMessage();
}
}
class ReaderThread extends Thread {
int count=0;
MessageManager messageManager;
Socket s;
String name;
private List<Socket> clients;
ReaderThread(Socket s, MessageManager messageManager,
List<Socket> clients) {
this.s = s;
this.messageManager = messageManager;
this.clients = clients;
}
public void run() {
try (BufferedReader in = new BufferedReader(new InputStreamReader(
s.getInputStream()));) {
while (true) {
String msg = in.readLine();
if(count==0){
name = msg;
messageManager.addMessage(name + "进入了聊天室!");
count++;
}else if (msg.equals("bye")) {
messageManager.addMessage(name + "退出了聊天室!");
break;
} else {
messageManager.addMessage(name + "说:" + msg);
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
s.close();
} catch (IOException e) {
}
clients.remove(s);
}
}
}
class MessageManager{
private Queue<String> msgQueue;
private List<Socket> clients;
public MessageManager(List<Socket> clients){
msgQueue = new PriorityQueue<>();
this.clients = clients;
}
public synchronized void addMessage(String msg){
System.out.println("addMessage方法:"+msg);
msgQueue.add(msg);
notifyAll();
}
public synchronized void sendMessage() {
while (true) {
if (msgQueue.size() > 0) {
String msg = msgQueue.poll();
System.out.println("发送一条消息:"+msg);
for (Socket s : clients) {
try {
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(s.getOutputStream())), true);
out.println(msg);
out.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
} else {
try {
wait();
} catch (InterruptedException e) {
}
}
}
}
}
客户端:Client.java
public class Client {
public static void main(String[] args) throws IOException {
Socket s = new Socket("127.0.0.1", 8888);
ReceiveThread t1 = new ReceiveThread(s);
t1.start();
Scanner in = new Scanner(System.in);
try(PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(s.getOutputStream())), true)) {
System.out.println("请输入您的代号:");
String name = in.next();
out.print(name);
out.flush();
String msg = null;
while (true) {
// 从键盘接收数据,然后发送到服务器
msg = in.nextLine();
out.println(msg);
out.flush();
if(msg.equals("bye"))
System.exit(0);
}
} finally {
//in.close();
}
}
}
class ReceiveThread extends Thread{
Socket s;
public ReceiveThread(Socket s){
this.s = s;
}
public void run(){
try(BufferedReader in = new BufferedReader(new InputStreamReader(
s.getInputStream()));) {
while (true) {
String msg = in.readLine();
if(msg==null)
continue;
if(msg.equals("bye")){
System.out.println("服务器停止服务!");
System.exit(0);
}else{
System.out.println(msg);
}
}
} catch(Exception e){
e.printStackTrace();
}
}
}
这个聊天室还是先运行服务器端,然后运行客户端,可以多次运行,每运行一次就代表一个人的操作。
同样也是,我要在服务器上运行服务器端,在pc上多次运行客户端进行聊天
六、用户数据报通信
- 使用UDP发送数据的过程如下:
-
- 创建DatagramSocket对象;
-
- 创建DatagramPacket对象;
-
- 调用DatagramSocket的方法发送DatagramPacket对象。
- 使用UDP接收数据的过程如下:
-
- 创建DatagramSocket对象;
-
- 创建DatagramPacket对象;
-
- 接收数据。
数据报是无连接的,也可以来传送数据,所以说这个是主要用来传送数据的。
接收的数据放在数据包里面。
同样也是有服务器端、客户端。
服务器端:Server.java
public class Server {
// 监听端口
public static final int PORT = 8888;
// 接收的每个数据报的最大值为1K
private static final int LENGTH = 1024;
// 定义该客户端使用的DatagramSocket
private DatagramSocket socket = null;
byte[] buff = new byte[LENGTH];
// 接收数据的DatagramPacket对象
private DatagramPacket inPacket = new DatagramPacket(buff, LENGTH);
public Server() {
try {
// 创建一个客户端DatagramSocket
socket = new DatagramSocket(PORT);
while (true) {
socket.receive(inPacket);//接收消息,这个过程没有回的,如果回的话就是服务器向客户端发送数据了,它是无连接
String temp = new String(inPacket.getData(), 0,
inPacket.getLength());
if (temp.equals("bye")) {
System.out.println("服务器端退出!");
break;
}
System.out.println("接收到:"+temp);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (socket != null)
socket.close();
}
}
public static void main(String[] args) {
new Server();
}
}
客户端:Client.java
public class Client {
// 数据包要发往的服务器的端口和IP地址
public static final int PORT = 8888;
public static final String IP = "127.0.0.1";
private DatagramSocket socket = null;
private DatagramPacket outPacket = null;
public Client() {
try {
// 创建DatagramSocket对象,使用随机端口
socket = new DatagramSocket();
// 使用指定的端口和IP创建DatagramSocket对象——————这个就跟聊天室不一样了,这里指定要发送给谁
outPacket = new DatagramPacket(new byte[0], 0,
InetAddress.getByName(IP), PORT);
// 创建键盘输入流
System.out.println("请输入信息:");
Scanner in = new Scanner(System.in);
// 循环接收键盘输入信息
while (in.hasNextLine()) {
System.out.println("请输入信息:");
// 接收输入
String temp = in.nextLine();
if (temp.equals("bye")) {
System.out.println("客户端退出!");
break;
}
byte[] buff = temp.getBytes();
// 把接收的信息添加到DatagramPacket中
outPacket.setData(buff);
// 发送数据报
socket.send(outPacket);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (socket != null)
socket.close();
}
}
public static void main(String[] args) {
new Client();
}
}
以上是关于网络编程的主要内容,如果未能解决你的问题,请参考以下文章
VSCode自定义代码片段14——Vue的axios网络请求封装
VSCode自定义代码片段14——Vue的axios网络请求封装