网络编程
简述:做的一些个人感觉容易忽视和比较重要的知识点,关于计算机网络方面会有单独的记录,不详细展开,这里只针对java中的网络编程基础部分。
什么叫做URL
- 在www上,每一信息资源都有统一且唯一的地址,该地址就叫URL(Uniform Resource Locator),它是www的统一资源定位符。
- URL由4部分组成:协议 、存放资源的主机域名、资源文件名和端口号。如果未指定该端口号,则使用协议默认的端口。
- 例如http 协议的默认端口为 80。 在浏览器中访问网页时,地址栏显示的地址就是URL。
- 在java.net包中提供了URL类,该类封装了大量复杂的涉及从远程站点获取信息的细节。
Scoket
- 在应用层和传输层之间,使用套接Socket来进行分离。
- Socket实际是传输层供给应用层的编程接口。
- Socket编程可以开发客户机和服务器应用程序,可以在本地网络上进行通信,也可通过Internet在全球范围内通信。
java.net包中的常用类
Java为了可移植性,不允许直接调用操作系统,而是由java.net包来提供网络功能。Java虚拟机负责提供与操作系统的实际连接。
#### InetAddress
- 作用:封装计算机的IP地址和DNS(没有端口信息)。
- 这个类没有构造方法。如果要得到对象,只能通过静态方法:getLocalHost()、getByName()、 getAllByName()、 getAddress()、getHostName()。
InetSocketAddress
- 包含IP和端口信息,常用于Socket通信。此类实现 IP 套接字地址(IP 地址 + 端口号),不依赖任何协议。
URL
- URL标识了计算机上的资源。
- 类 URL 代表一个统一资源定位符,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,例如对数据库或搜索引擎的查询。
- DK中提供了URL类,该类的全名是java.net.URL,有了这样一个类,就可以使用它的各种方法来对URL对象进行分割、合并等处理。
下面是URL类的使用方法:
public class TestUrl {
public static void main(String[] args) throws MalformedURLException {
URL url = new URL("https://www.google.com/imgres?imgurl=http%3A%2F%2Fpic.netbian.com%2Fuploads%2Fallimg%2F191006%2F005346-1570294426c666.jpg&imgrefurl=http%3A%2F%2Fpic.netbian.com%2F4kfengjing%2F&tbnid=nDH0bFXlObwFBM&vet=12ahUKEwjDsYG8pNrlAhWnHjQIHb7TCzgQMygAegUIARDcAQ..i&docid=HiLtAxuwJ1_OzM&w=450&h=287&q=%E9%A3%8E%E6%99%AF&hl=zh-CN&ved=2ahUKEwjDsYG8pNrlAhWnHjQIHb7TCzgQMygAegUIARDcAQ");
System.out.println("获取与此url默认关联的协议的默认端口:"+url.getDefaultPort());
System.out.println("端口号后面的内容getFile:"+url.getFile());
System.out.println("主机名:"+url.getHost());
System.out.println("路径:"+url.getPath());
System.out.println("获取端口号:"+url.getPort());
System.out.println("获取协议:"+url.getProtocol());
System.out.println("参数部分:"+url.getQuery());
System.out.println("锚点:"+url.getRef());
}
}
看控制台输出信息:
获取与此url默认关联的协议的默认端口:443
端口号后面的内容getFile:/imgres?imgurl=http%3A%2F%2Fpic.netbian.com%2Fuploads%2Fallimg%2F191006%2F005346-1570294426c666.jpg&imgrefurl=http%3A%2F%2Fpic.netbian.com%2F4kfengjing%2F&tbnid=nDH0bFXlObwFBM&vet=12ahUKEwjDsYG8pNrlAhWnHjQIHb7TCzgQMygAegUIARDcAQ..i&docid=HiLtAxuwJ1_OzM&w=450&h=287&q=%E9%A3%8E%E6%99%AF&hl=zh-CN&ved=2ahUKEwjDsYG8pNrlAhWnHjQIHb7TCzgQMygAegUIARDcAQ
主机名:www.google.com
路径:/imgres
获取端口号:-1
获取协议:https
参数部分:imgurl=http%3A%2F%2Fpic.netbian.com%2Fuploads%2Fallimg%2F191006%2F005346-1570294426c666.jpg&imgrefurl=http%3A%2F%2Fpic.netbian.com%2F4kfengjing%2F&tbnid=nDH0bFXlObwFBM&vet=12ahUKEwjDsYG8pNrlAhWnHjQIHb7TCzgQMygAegUIARDcAQ..i&docid=HiLtAxuwJ1_OzM&w=450&h=287&q=%E9%A3%8E%E6%99%AF&hl=zh-CN&ved=2ahUKEwjDsYG8pNrlAhWnHjQIHb7TCzgQMygAegUIARDcAQ
锚点:null
下面这个是获取百度url的一些东西,得到的是它的整个页面html信息:
public class TestSpider {
public static void main(String[] args) {
basicSpider();
}
static void basicSpider() {
URL url = null;
InputStream is = null;
BufferedReader br = null;
StringBuilder sb = new StringBuilder();
String temp = "";
try {
url = new URL("http://www.baidu.com");
is = url.openStream();
br = new BufferedReader(new InputStreamReader(is));
/*
* 这样就可以将网络内容下载到本地机器。
* 然后进行数据分析,建立索引。这也是搜索引擎的第一步。
*/
while ((temp = br.readLine()) != null) {
sb.append(temp);
}
System.out.println(sb);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
控制台输出:
<!DOCTYPE html><!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>鐧惧害涓?涓嬶紝浣犲氨鐭ラ亾</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=鐧惧害涓?涓? class="bg s_btn"></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>鏂伴椈</a> <a href=http://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>鍦板浘</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>瑙嗛</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>璐村惂</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>鐧诲綍</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">鐧诲綍</a>');</script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">鏇村浜у搧</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>鍏充簬鐧惧害</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>©2017 Baidu <a href=http://www.baidu.com/duty/>浣跨敤鐧惧害鍓嶅繀璇?</a> <a href=http://jianyi.baidu.com/ class=cp-feedback>鎰忚鍙嶉</a> 浜琁CP璇?030173鍙? <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>
基于TCP协议的Socket编程和通信
- 主机地址就是客户端或服务器程序所在的主机的IP地址。端口地址是指客户端或服务器程序使用的主机的通信端口。
- 在客户端和服务器中,分别创建独立的Socket,并通过Socket的属性,将两个Socket进行连接,这样,客户端和服务器通过套接字所建立的连接使用输入输出流进行通信。
- TCP/IP套接字是最可靠的双向流协议,使用TCP/IP可以发送任意数量的数据。
通过Socket的编程顺序:
- 创建服务器ServerSocket,在创建时,定义ServerSocket的监听端口(在这个端口接收客户端发来的消息)。
- ServerSocket调用accept()方法,使之处于阻塞状态。
- 创建客户端Socket,并设置服务器的IP及端口。
- 客户端发出连接请求,建立连接。
- 分别取得服务器和客户端Socket的InputStream和OutputStream。
- 利用Socket和ServerSocket进行数据传输。
- 关闭流及Socket。
单向通信的服务端代码:
public class BasicSocketServer {
public static void main(String[] args) {
Socket socket = null;
BufferedWriter bw =null;
try {
// 建立服务器端套接字,指定监听的接口
ServerSocket serverScoket = new ServerSocket(8888);
System.out.println("服务端建立监听");
// 监听,等待客户端请求,并愿意接受链接
socket = serverScoket.accept();
// 获取Scoket的输出流,并用缓冲流进行包装
bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
// 向客户端发送反馈信息
bw.write("客户端你好,我是服务端!");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
// 关闭流及Scoket连接
if(bw!=null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket!=null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
接下来是客户端代码:
public class BasicSocketClient {
public static void main(String[] args) {
Socket socket = null;
BufferedReader br = null;
try {
/**
* 创建Socket对象,指定要连接的服务器IP和端口,而不是自己机器的端口。
* 发送端口是随机的。
*/
socket = new Socket(InetAddress.getLocalHost(), 8888);
// 获取Socket的输入流,并用缓冲流进行包装
br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// 接收服务端发送的信息
System.out.println(br.readLine());
} catch (IOException e) {
e.printStackTrace();
}finally {
// 关闭流和Socket
if(br!=null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket!=null) {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
注意:程序运行要先运行服务端代码,再运行客户端代码。单向通信的意思是指信息只能由服务端传送到客户端。这里需要了解整个服务端到客户端通信的Socket建立过程,以及两者是如何进行的通信。
输出控制台:
双向通信服务端客户端演示
- 多线程实现的双向通讯。
- 服务器端/客户端:一个线程专门发送消息,一个线程专门接收消息。
最终运行效果,客户端输出台:
服务端输出台:
这样就完成了双向通信。
客户端代码;
public class ChartClient {
public static void main(String[] args) {
Socket socket = null;
BufferedReader in = null;
try {
socket = new Socket(InetAddress.getByName("127.0.1.1"), 8888);
// 创建向服务器端发送信息的线程,并启动
new ClientThread(socket).start();
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// main线程负责接收服务器发来的信息
while (true) {
System.out.println("服务器说:" + in.readLine());
}
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 用于向服务器发送消息
*
* @author Administrator
*
*/
class ClientThread extends Thread {
Socket s;
BufferedWriter out;
BufferedReader wt;
public ClientThread(Socket s) {
this.s = s;
try {
out = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
wt = new BufferedReader(new InputStreamReader(System.in));
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
try {
while (true) {
String str = wt.readLine();
out.write(str + "\n");
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (wt != null) {
wt.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
服务端代码;
public class ChartServer {
public static void main(String[] args) {
ServerSocket server = null;
Socket socket = null;
BufferedReader in = null;
try {
server = new ServerSocket(8888);
socket = server.accept();
//创建向客户端发送消息的线程,并启动
new ServerThread(socket).start();
// main线程负责读取客户端发来的信息
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
while (true) {
String str = in.readLine();
System.out.println("客户端说:" + str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (in != null) {
in.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 专门向客户端发送消息的线程
*
* @author Administrator
*
*/
class ServerThread extends Thread {
Socket ss;
BufferedWriter out;
BufferedReader br;
public ServerThread(Socket ss) {
this.ss = ss;
try {
out = new BufferedWriter(new OutputStreamWriter(ss.getOutputStream()));
br = new BufferedReader(new InputStreamReader(System.in));
} catch (IOException e) {
e.printStackTrace();
}
}
public void run() {
try {
while (true) {
String str2 = br.readLine();
out.write(str2 + "\n");
out.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(out != null){
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(br != null){
br.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
这里注意的是,运行时要先启动服务端,后启动客户端。在彼此交流通信时,最好开两个控制输出台来分别控制输出。
- Eclipse开多个控制台的方法
运行单个程序,在窗口右下角找到下图标:
选择新建一个console,之后点击左边的那个按钮:
即可设置控制台所属的程序。
UDP通讯的实现
- DatagramSocket:用于发送或接收数据报包
- 当服务器要向客户端发送数据时,需要在服务器端产生一个DatagramSocket对象,在客户端产生一个DatagramSocket对象。
- 服务器端的DatagramSocket将DatagramPacket发送到网络上,然后被客户端的DatagramSocket接收。
- DatagramSocket有两种常用的构造函数.
- DatagramSocket() :构造数据报套接字并将其绑定到本地主机上任何可用的端口。
- DatagramSocket(int port) :创建数据报套接字并将其绑定到本地主机上的指定端口。
- 常用方法
- send(DatagramPacket p) :从此套接字发送数据报包。
- receive(DatagramPacket p) :从此套接字接收数据报包。
- close() :关闭此数据报套接字。
- DatagramPacket:数据容器(封包)的作用
- 常用方法
- DatagramPacket(byte[] buf, int length) :构造数据报包,用来接收长度为 length 的数据包。
- DatagramPacket(byte[] buf, int length, InetAddress address, int port) :构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
- getAddress() :获取发送或接收方计算机的IP地址,此数据报将要发往该机器或者是从该机器接收到的。
- getData() :获取发送或接收的数据。
- setData(byte[] buf) :设置发送的数据。
- 常用方法
- UDP通信编程基本步骤:
- 创建客户端的DatagramSocket,创建时,定义客户端的监听端口。
- 创建服务器端的DatagramSocket,创建时,定义服务器端的监听端口。
- 在服务器端定义DatagramPacket对象,封装待发送的数据包。
- 客户端将数据报包发送出去。
- 服务器端接收数据报包。
单向通信示例,从客户端到服务端:
客户端:
public class Client {
public static void main(String[] args) throws SocketException {
byte[] b = "从前有个小和尚".getBytes();
// 封装待发送的数据包,指定发送的计算机及其端口号
DatagramPacket dp = new DatagramPacket(b, b.length, new InetSocketAddress("localhost", 8888));
// 创建DatagramSocket数据报套接字,用于发送数据
DatagramSocket ds = new DatagramSocket(9999);
try {
// 发送数据报
ds.send(dp);
} catch (IOException e) {
e.printStackTrace();
}
// 关闭资源
ds.close();
}
}
服务端:
public class Server {
public static void main(String[] args) throws IOException {
// 创建数据报套接字,指定接收信息的端口
DatagramSocket ds = new DatagramSocket(8888);
// 创建封装数据报包,指定长度与缓存地
byte[] b = new byte[1024];
DatagramPacket dp = new DatagramPacket(b, b.length);
// 阻塞接收数据报
ds.receive(dp);
String string = new String(dp.getData(), 0, dp.getLength());
System.out.println(string);
// 关闭资源
ds.close();
}
}
注意这里要先运行服务端代码,后运行客户端,实现效果如下: