x
一、网络模型
在这个参考模型中,信息信息从一台主机到网络中的另一台主机需要经过从应用层到物理层的的层层封装,然后到达另一台主机后在层层拆包.当我们面向Socket编程时主要是在传输层混,面向URLConnection编程时就是在应用层混啦.
二、网络通讯要素
1、IP地址:用于标识网络中的设备,在java中用InetAddress类进行封装。
(1)、IP地址有两种格式:
IPv4---由四个字节构成,格式是用四个八位地址段的十进制值用点分隔,对应于Inet4Address类。
IPv6---由十六个字节构成,首选格式是用八个十六位地址段的十六进制值用点分隔,对应于Inet6Address类。
(2)、Inet4Address和Inet6Address都是InetAddress的子类,下面是InetAddress类的一些方法列举:
getAddress()返回此 InetAddress 对象的原始 IP 地址。
getHostAddress()返回 IP 地址字符串(以文本表现形式)。
getHostName()获取此 IP 地址的主机名。
getAllByName(String host)在给定主机名的情况下,根据系统上配置的名称服务返回其 IP 地址所组成的数组。
getByName(String host)在给定主机名的情况下确定主机的 IP 地址。
getLocalHost()返回本地主机。
isReachable(int timeout)测试是否可以达到该地址。
(3)、有一个比较特殊的IP地址封装类,它同时封装了IP地址和端口,这个类就是InetSocketAddress。
(4)、IP地址的字节形式不便于记忆可以用主机名来标识,一个特殊地址127.0.0.1为本地回环地址,其主机名为localhost。
2、端口号:用于标识进程的逻辑地址,有效端口:0~65535,其中0~1024系统使用或保留端口。
3、传输协议:传输协议定义的其实就是网络中不同设备间共同遵循的通讯规则,常见协议有TCP,UDP。
(1)、UDP协议及其特点:将数据及源和目的封装成数据包中,不需要建立连接,因无连接,所以速度快但是不可靠,而且每个数据报的大小在限制在64k内。
(2)、TCP协议及其特点:通过三次握手完成连接,因要建立连接所以效率会稍低,但是传输可靠,而且允许进行大数据量传输。
三、面向Socket编程
Socket就是为网络服务提供的一种机制,对应于网络中的传输层,通信的两端都有Socket,网络通信其实就是Socket间的通信,数据在两个Socket间通过IO传输。
无论是UDP的面向连接还是TCP的面向连接,我们面向的其实都是Socket。下面是Socket中的一些常用方法:
bind(SocketAddress bindpoint)将套接字绑定到本地地址。
connect(SocketAddress endpoint)&connect(SocketAddress endpoint, int timeout)将此套接字连接到服务器,且后者可以指定一个超时值。
getInetAddress()&getPort() 返回套接字连接的远程地址及端口。
getLocalAddress()&getLocalPort()获取套接字绑定的本地地址即端口。
getInputStream()&getOutputStream()返回此套接字的输入输出流。
shutdownInput()&shutdownOutput()禁用此套接字的输入流与输出流,即使同时调用了两方法也不能导致Socket关闭,另外shutdownOutput()方法通常用于告知服务端客户端发送结束。
四、UDP传输
1、使用DatagramSocket与DatagramPacket进行网络编程的步骤:
(1)、建立发送端,接收端,接收端一般会指定地址或者默认地址,且一定要指定端口,发送端一般没有要求,所以创建发送端一般用构造器
DatagramSocket(SocketAddress bindaddr),DatagramSocket(int port),DatagramSocket(int port,InetAddress laddr)。
(2)、建立数据包,通常接受包不需要往构造函数中传递地址参数,发送包则一定要传递目的地址参数,且一定要明确锁封装数组的长度。
对于接受到的数据包以后可以通过下列方法获得许多实用信息:
getAddress()返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。
getData()返回数据缓冲区,即内部封装的数组。
getLength()返回将要发送或接收到的数据的长度。
getPort()返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。
getSocketAddress()获取要将此包发送到的或发出此数据报的远程主机的 SocketAddress(通常为 IP 地址 + 端口号)。
setAddress(InetAddress iaddr)设置要将此数据报发往的那台机器的 IP 地址。
对于要发送的数据包,可以通过以下方法对包信息进行设置:
setData(byte[] buf)&setData(byte[] buf, int offset, int length)为此包设置数据缓冲区。
setLength(int length)为此包设置长度。
setPort(int iport)设置要将此数据报发往的远程主机上的端口号。
setSocketAddress(SocketAddress address)设置要将此数据报发往的远程主机的 SocketAddress(通常为 IP 地址 + 端口号)。
(4)、调用Socket的send()和receive()方法发送或接收数据包,他们都是阻塞式方法。
(5)、关闭Socket。
2、下面用一个小程序来对上面的步骤进行演示
//客户端程序
public class UdpClient
{
public static void main(String[] args)
{
DatagramSocket ds = null;
byte[] buf = new byte[1024];
DatagramPacket dp = null;
BufferedReader bfr = null;
try
{
ds = new DatagramSocket();
//要在发送数据包对象中明确目的地IP及端口
dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.255"),10001);
bfr = new BufferedReader(new InputStreamReader(System.in));
String line = null,data = null;
while((line = bfr.readLine()) != null){
buf = line.getBytes();
dp.setData(buf);
ds.send(dp);
if(line.equals("886")){
break;
}
}
}
catch (Exception e)
{
throw new RuntimeException("发送异常!");
}finally{
try
{
if(bfr != null)
bfr.close();
}
catch (IOException e)
{
throw new RuntimeException("输出流关闭异常!");
}finally{
if(ds != null)
ds.close();
}
}
}
}
//服务端程序
public class UdpServer
{
public static void main(String[] args) throws IOException
{
DatagramSocket ds = new DatagramSocket(10001);
System.out.println(ds.getLocalSocketAddress().toString());
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);
String data = null;
while(true){
ds.receive(dp);
data = new String(dp.getData(),0,dp.getLength());
//使用DatagramPacket中方法来提取数据信息
String result = dp.getAddress()+"::"+dp.getPort()+"::"+data;
System.out.println(result);
if(data.equals("886"))
break;
}
ds.close();
}
}
五、TCP传输
1、使用Socket和ServerSocket进行网络编程的步骤:
(1)、建立客户端和服务器端,服务器端要明确自己的监听地址,客户端要明确自己的请求地址。另外服务器端可以通过构造函数指定最高的并发访问数量。
(2)、建立连接后,通过Socket中的IO流进行数据的传输,对于文本文件,输入流建议用BufferedReader包装,输出流建议用PrintWriter包装并指定自动刷新,输出时调用println()方法。
(3)、关闭socket,要注意其中的shutdownInput()&shutdownOutput()方法,如果使用BufferedWriter包装输出流容易造成阻塞,可以在输出完毕后使用shutdownOutput()方法通知接收方,但局限性太大,只适用于一站式应用。
2、基本思路
(1)、客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。连接成功,说明客
户端与服务端建立了通道,那么 通过IO流就可以进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通过 getInputStream(),getOutputStream()获取即可。
与服务端通讯结束后,关闭Socket。
(2)、服务端需要明确它要处理的数据是从哪个端口进入的。当有客户端访问时,要明确是哪个客户端,可通过accept()获取已
连接的客户端对象,并通过该对象与客户端通过IO流进行数据传输。当该客户端访问结束,关闭该客户端。
3、下面用一个小程序来对上面的步骤进行演示
//客户端程序
public class TcpClient {
public static void main(String[] args) throws IOException {
Socket s = new Socket("192.168.1.206",20000);
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
// BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);
BufferedReader brs = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null ,line2 = null;
while((line = br.readLine()) != null){
if(line.equals("886")){
pw.println(line);
break;
}
// bw.write(line); 如果使用此方法会导致服务器端阻塞,因为readLine()返回内容不包含结束符
pw.println(line);
System.out.println(brs.readLine());
}
System.out.println(brs.readLine());
br.close();
s.close();
}
}
public class TcpServer {
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(20000);
Socket s = ss.accept();
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
// BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
PrintWriter pw = new PrintWriter(s.getOutputStream(),true);//更简单
String line = null;
//readLine()只有读到回车符时才返回读取内容----newLine()方法或者shutdownOutput()方法(后者局限性太大)
while((line = br.readLine()) != null){
pw.println(line.toUpperCase());
// bw.write(line.toUpperCase());
// bw.newLine(); readLine()返回的内容不包括回车符,导致客户端readLine()端阻塞
// bw.flush();
}
s.close();
ss.close();
}
}
六、URL&URLConnection
1、类URL代表一个统一资源定位符,它是指向互联网“资源”的指针。使用该类的openConnection()方法即可获取对应的URLConnection对象,也可以通过openStream()方法直接获取其所指向资源的输入流。
它也提供了一系列的get方法来提取封装在URL对象中的信息
getFile()获取此 URL 的文件名。
getHost()获取此 URL 的主机名(如果适用)。
getPath()获取此 URL 的路径部分。
getPort()获取此 URL 的端口号。
getProtocol()获取此 URL 的协议名称。
getQuery()获取此 URL 的查询部分。
2、URLConnection它代表应用程序和 URL 之间的通信链接。此类的实例可用于读取和写入此 URL 引用的资源。通常,创建一个
到URL的连接需要几个步骤:
(1)、通过在 URL 上调用 openConnection 方法创建连接对象。
(2)、处理设置参数和一般请求属性。
(3)、使用 connect 方法建立到远程对象的实际连接。
(4)、远程对象变为可用,远程对象的头字段和内容变为可访问。
通过下面这段程序来演示一下使用URLConnection的最基本步骤:
class URLConnectionDemo
{
public static void main(String[] args) throws Exception
{
URL url = new URL("http://192.168.1.206:8080/");
URLConnection conn = url.openConnection();
InputStream in = conn.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
System.out.println(new String(buf,0,len));
}
}
3、URLConnection的一些方法的列举与讲解
(1)、使用以下方法修改设置参数:
setAllowUserInteraction
setDoInput
setDoOutput
setIfModifiedSince
setUseCaches
(2)、使用以下方法修改一般请求属性:
setRequestProperty
(3)、使用 setDefaultAllowUserInteraction 和 setDefaultUseCaches 可设置 AllowUserInteraction 和 UseCaches 参数的默认值。
(4)、上面每个 set 方法都有一个用于获取参数值或一般请求属性值的对应 get 方法。适用的具体参数和一般请求属性取决于协议。
(5)、在建立到远程对象的连接后,以下方法用于访问头字段和内容:
getContent
getHeaderField
getInputStream
getOutputStream
(6)、某些头字段需要经常访问。以下方法:
getContentEncoding
getContentLength
getContentType
getDate
getExpiration
getLastModifed
提供对这些字段的便捷访问。
(7)、getContent方法使用getContentType方法以确定远程对象类型,子类重写getContentType方法很容易。
(8)、一般情况下,预连接参数和一般请求属性默认为敏感值,对于大多数客户端而言,只有两个需要的方法getInputStream和getContent,它们通过便捷方法镜像到URL类中。