------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------
网络编程
一、概述
1.1 网络参考模型
OSI参考模型
TCP/IP参考模型
1.2 网络通讯要素
1. IP地址:InetAddress
网络中设备的标识。
不易记忆,可用主机名。
本地回环地址:127.0.0.1 主机名:localhost。
2. 端口号
用于标识进程(应用程序)的逻辑地址,不同进程的标识。
有效端口:0~65535,其中0~1024系统使用或保留端口。
3. 传输协议
通讯的规则。
常见协议:UDP、TCP。
UDP
将数据及源和目的封装成数据包中,不需要建立连接。
每个数据报的大小在限制在64k内。
因无连接,是不可靠协议。
不需要建立连接,速度快。
TCP
建立连接,形成传输数据的通道。
在连接中进行大数据量传输。
通过三次握手完成连接,是可靠协议。
必须建立连接,效率会稍低。
- import java.net.*;
- class IPDemo
- {
- public static void main(String[] args) throws Exception
- {
- InetAddress i = InetAddress.getLocalHost();
- //System.out.println(i.toString());
- System.out.println(i.getHostName());
- System.out.println(i.getHostAddress());
- }
- }
二、传输协议
2.1 Socket
Socket就是为网络服务提供的一种机制。
通信的两端都有Socket。
网络通信其实就是Socket间的通信。
数据在两个Socket间通过IO传输。
2.2 UDP传输协议
方法:
创建 UDPSocket发送服务对象:
DatagramSocket(),不指定端口。DatagramSocket(int port),指定端口。
发送:void send(DatagramPacket p)
接收:void receive(DatagramPacket p)
定义UDP发送端
方法:
1.建立UDP socket服务
2.提供数据,并将数据封装到数据包中
3.通过socket服务的发送功能,并将数据包发送出去
4.关闭资源
- import java.net.*;
- import java.io.*;
- class UDPSend
- {
- public static void main(String[] args) throws Exception
- {
- //1.创建UDP服务,通过DatagramSocket对象
- DatagramSocket ds = new DatagramSocket(8888);
- //2.确定数据,并封装成数据包。(键盘录入)
- //DatagramPacket(byte[] buf, int length, InetAddress address, int port)
- //构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
- BufferedReader bfr =
- new BufferedReader(new InputStreamReader(System.in));
- String line = null;
- while((line=bfr.readLine())!=null)
- {
- if("over".equals(line))
- break;
- byte[] buf = line.getBytes();
- DatagramPacket dp =
- new DatagramPacket(buf,buf.length,InetAddress.getByName("10.0.0.2"),10000);
- //3.通过Socket服务将已有数据包发送出去,通过send方法
- ds.send(dp);
- }
- //4.关闭资源
- ds.close();
- }
- }
定义UDP接收端
方法:
1.建立UDP socket服务。通常会监听一个端口,其实就是给这个接受网络应用程序
定义数字标识,方便于明确哪些数据过来该应用可以处理
2.定义一个数据包,用来存储接收到的字节数据。
因为数据包对象中有更多功能可以提取字节数据中的不同数据信息
3.通过socket服务的接收功能将收到的数据存入已定义好的数据包中
4.通过数据包的特有功能,将这些不同的数据取出,打印在控制台上
5.关闭资源
- import java.net.*;
- class UDPRec
- {
- public static void main(String[] args) throws Exception
- {
- //1.创建UDP socket,建立端点
- DatagramSocket ds = new DatagramSocket(10000);
- while(true)
- {
- //2.定义数据包,用于存储数据
- byte[] buf = new byte[1024];
- DatagramPacket dp = new DatagramPacket(buf,buf.length);
- //3.通过receive方法将数据存入到数据包中
- ds.receive(dp); //阻塞式方法
- //4.通过数据包的方法获取其中的数据
- String ip = dp.getAddress().getHostAddress();
- String data = new String(dp.getData(),0,dp.getLength());
- int port = dp.getPort();
- System.out.println(ip+"+"+data+"+"+port);
- ds.close();
- }
- }
- }
由于UDP协议传输数据,只管发送数据,而不管接收端是否能够接收到数据。因此,应该首先启动接收端程序,再启动发送端程序。
练习:编写一个聊天程序
- import java.io.*;
- import java.net.*;
- import java.util.*;
- class ChatDemo
- {
- public static void main(String[] args) throws Exception
- {
- DatagramSocket sendSocket = new DatagramSocket(9999);
- DatagramSocket recSocket = new DatagramSocket(10003);
- new Thread(new Send(sendSocket)).start();
- new Thread(new Rec(recSocket)).start();
- }
- }
- class Send implements Runnable
- {
- private DatagramSocket ds;
- Send(DatagramSocket ds)
- {
- this.ds = ds;
- }
- public void run()
- {
- try
- {
- BufferedReader bur =
- new BufferedReader(new InputStreamReader(System.in));
- String line = null;
- while((line=bur.readLine())!=null)
- {
- if("over".equals(line))
- break;
- byte[] buf = line.getBytes();
- DatagramPacket dp =
- new DatagramPacket(buf,buf.length,InetAddress.getByName("10.0.0.2"),10003);
- ds.send(dp);
- }
- }
- catch (Exception e)
- {
- throw new RuntimeException("发送失败");
- }
- }
- }
- class Rec implements Runnable
- {
- private DatagramSocket ds;
- Rec(DatagramSocket ds)
- {
- this.ds = ds;
- }
- public void run()
- {
- try
- {
- while(true)
- {
- byte[] buf = new byte[1024];
- DatagramPacket dp =
- new DatagramPacket(buf,buf.length);
- ds.receive(dp);
- String ip = dp.getAddress().getHostAddress();
- String data = new String(dp.getData(),0,dp.getLength());
- System.out.println(ip+":"+data);
- }
- }
- catch (Exception e)
- {
- throw new RuntimeException("接收失败");
- }
- }
- }
2.3 TCP传输协议
客户端(Client)首先与服务端(Server)建立连接,形成通道(其实就是IO流),然后,数据就可以在通道之间进行传输,并且单个Server端可以同时与多个Client端建立连接。
方法:
创建客户端对象:
Socket():创建空参数的客户端对象,一般用于服务端接收数据
Socket(String host,int port),指定要接收的IP地址和端口号
创建服务端对象:ServerSocket(int port):指定接收的客户端的端口
Socket accept():监听并接受到此套接字的连接
void shutdownInput():此套接字的输入流至于“流的末尾”
void shutdownOutput():禁用此套接字的输出流
InputStream getInputStream():返回此套接字的输入流,Socket对象调用
OutputStream getOutputStream():返回套接字的输出流,Socket对象调用
TCP客户端
客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。连接成功,说明客户端与服务端建立了通道,那么通过IO流就可以进行数据的传输,而Socket
对象已经提供了输入流和输出流对象,通过 getInputStream(),getOutputStream()获取即可。与服务端通讯结束后,关闭Socket。
- import java.net.*;
- import java.io.*;
- class TCPClient
- {
- public static void main(String[] args) throws Exception
- {
- //创建客户端socket,一旦建立就有了输入流和输出流
- Socket s = new Socket("10.0.0.2",10004);
- //获取socket流中的输出流
- OutputStream out = s.getOutputStream();
- out.write("TCP coming".getBytes());
- s.close();
- }
- }
TCP服务端
服务端需要明确它要处理的数据是从哪个端口进入的。当有客户端访问时,要明确是哪个客户端,可通过accept()获取已连接的客户端对象,并通过该对象与客户端通过IO流进行数据传输。当该客户端访问结束,关闭该客户端。
- class TCPServer
- {
- public static void main(String[] args) throws Exception
- {
- ServerSocket ss = new ServerSocket(10004);
- Socket s = ss.accept();
- String ip = s.getLocalAddress().getHostAddress();
- System.out.println(ip);
- InputStream in = s.getInputStream();
- byte[] buf = new byte[1024];
- int len = in.read(buf);
- String data = new String(buf,0,len);
- System.out.println(data);
- s.close();//关闭客户端
- ss.close();
- }
- }
TCP协议-服务端和客户端交互
- import java.io.*;
- import java.net.*;
- class Send
- {
- public static void main(String[] args) throws Exception
- {
- Socket s = new Socket("10.0.0.2",10006);
- //发送数据
- OutputStream out = s.getOutputStream();
- out.write("HELLO HOST".getBytes());
- //接受反馈
- InputStream in = s.getInputStream();
- byte[] buf = new byte[1024];
- int len = in.read(buf);
- System.out.println(new String(buf,0,len));
- s.close();
- }
- }
- class Server
- {
- public static void main(String[] args) throws Exception
- {
- ServerSocket ss = new ServerSocket(10006);
- Socket s = ss.accept();
- String ip = s.getLocalAddress().getHostAddress();
- System.out.println(ip+" is connecting");
- //接受数据
- InputStream in = s.getInputStream();
- byte[] buf = new byte[1024];
- int len = in.read(buf);
- System.out.println(new String(buf,0,len));
- //发送反馈
- OutputStream out = s.getOutputStream();
- Thread.sleep(1000);
- out.write("RECIVED".getBytes());
- s.close();
- ss.close();
- }
- }
TCP练习:需求:建立一个文本转换服务器,客户端给服务端发送文本,服务端会将文本转成大写再返回给客户端。而且客户端可以不断的进行文本转换,当输入over时结束。
- import java.io.*;
- import java.net.*;
- class TransSend
- {
- public static void main(String[] args) throws Exception
- {
- //定义socket服务
- Socket s = new Socket("10.0.0.2",10007);
- //键盘录入流
- BufferedReader bfr =
- new BufferedReader(new InputStreamReader(System.in));
- //定义目的,将数据写入socket输出流发送给服务端
- //BufferedWriter bfwOut =
- // new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
- PrintWriter out = new PrintWriter(s.getOutputStream(),true);
- //定义一个socket读取流,读取服务端返回的信息
- BufferedReader bfrIn =
- new BufferedReader(new InputStreamReader(s.getInputStream()));
- String line =null;
- while((line=bfr.readLine())!=null)
- {
- if("over".equals(line))
- break;
- out.println(line);
- //bfwOut.write(line);
- //bfwOut.newLine();
- //bfwOut.flush();
- String str = bfrIn.readLine();
- System.out.println("返回:"+str);
- }
- bfr.close();
- s.close();
- }
- }
- class TransServer
- {
- public static void main(String[] args) throws Exception
- {
- ServerSocket ss = new ServerSocket(10007);
- Socket s = ss.accept();
- String ip = s.getInetAddress().getHostAddress();
- int port = s.getPort();
- System.out.println(ip+" "+port);
- BufferedReader bfr =
- new BufferedReader(new InputStreamReader(s.getInputStream()));
- //BufferedWriter bfw =
- // new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
- PrintWriter out = new PrintWriter(s.getOutputStream(),true);
- String line = null;
- while((line=bfr.readLine())!=null)
- {
- System.out.println(line);
- out.println(line.toUpperCase());
- //bfw.write(line.toUpperCase());
- //bfw.newLine();
- //bfw.flush();
- }
- s.close();
- ss.close();
- }
- }
常见问题:
1、上面练习中之所以客户端结束后,服务端也随之结束的原因在于:客户端的socket关闭后,服务端获取的客户端socket读取流也关闭了,因此读取不到数据,line = bfr.readLine()为null,循环结束,ServerSocket的close方法也就
执行关闭了。
2、上面练习中的客户端和服务端的PrintWriter对象out获取到数据后,一定要刷新,否则对方(服务端或客户端)就获取不到数据,程序便无法正常执行。刷新操作可以通过PrintWriter类的println()方法实现,也可以通过PrintWriter类的flush()方法实现。但是,由于获取数据的方法是BufferedReader对象bfr的readLine()方法(阻塞式方法),此方法只有遇到“\r\n”标记时,才认为数据读取完毕,赋值给String对象line。所以,使用PrintWriter类的flush()方法刷新数据时一定要记得追加“\r\n”。
三、了解客户端和服务器端原理
最常见的客户端:浏览器,IE/chrome。
最常见的服务端:服务器,Tomcat。
客户端发送的请求是:
(请求行,请求方式:GET;请求的资源路径:/;HTTP协议版本:1.1。)
GET / HTTP/1.1
(请求消息头,属性名:属性值。)
Host: localhost:9090
Connection: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like
Gecko) Chrome/43.0.2357.81 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: zh-CN,zh;q=0.8
HTTP服务端发回的应答消息:
(应答行,HTTP的协议版本:1.1;应答状态码:200;应答状态描述信息:OK。)
HTTP/1.1 200 OK
(应答消息属性信息,属性名:属性值。)
Server: Apache-Coyote/1.1
Accept-Ranges: bytes
ETag: W/"211-1433908112666"
Last-Modified: Wed, 10 Jun 2015 03:48:32 GMT
Content-Type: text/html
Content-Length: 211
Date: Wed, 10 Jun 2015 03:52:16 GMT
Connection: close
四、URL&URLConnection
URI:统一资源标示符。
URL:统一资源定位符,也就是说根据URL能够定位到网络上的某个资源,它是指向互联网“资源”的指针。每个URL都是URI,但不一定每个URI都是URL。这是因为URI还包括一个子类,即统一资源名称(URN),它命名资源但不指定如何定位资源。
URL方法:
构造函数:URL(String protocol,String host,int port,String file);//根据指定 protocol、host、port号和 file 创建 URL对象。
String getProtocol();//获取协议名称
String getHost();//获取主机名
int getPort();//获取端口号
String getFile();//获取URL文件名
String getPath();//获取此URL的路径部分
String getQuery();//获取此URL的查询部,客户端传输的特定信息
注:一般输入网址,是不带端口号的,此时可进行获取,通过获取网址返回的port,若port为-1,则分配一个默认的80端口
URLConnection方法:
URLConnection openConnection();//用URL调用此方法,返回一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接。
InputStream getInputStream();//获取输入流
OutputStream getOutputStream();//获取输出流