23.JAVA核心技术-网络编程
1. 网络的基础知识
ip:主机在网络中的唯一标识,是一个逻辑地址。127.0.0.1表示本机地址
端口:是一个为了便于实现服务器与客户端之间通信所使用的抽象概念。
协议:通讯双方为了完成预先制定好的功能而达成的约定。
TCP/IP网络7层模型:物理层Physical(硬件)、数据链路层DataLink(二进制)、网络层Network(IP协议:寻址和路由)、
传输层Transport(TCP、UDP协议)、会话层Session(端口)、表示层Presentation、
应用层Application(HTTP、FTP、SMTP、POPS、DNS)
TCP协议和UDP协议:
TCP:开销大,用于可靠性要求高的场合,TCP的过程就是像打电话的过程。面向连接,可靠,低效
UDP:用在对实时性要求比较高的场合。UDP过程相当于写信的过程。无连接,不可靠,效率高。
2.网络套接字Socket(TCP)
客户端要创建的对象Socket 服务器端要创建的对象ServerSocket。
创建一个TCP服务器端程序的步骤:
a.创建一个ServerSocket b.从ServerSocket接受客户连接请求
c.创建一个服务线程处理新的连接 d. 在服务线程中,从socket中获得I/O流
e. 对I/O流进行读写操作,完成与客户的交互 f.关闭I/O流 g.关闭Socket
创建一个TCP客户端程序的步骤:
a. 创建Socket b. 获得I/O流 c.对I/O流进行读写操作
d. 关闭I/O流 e. 关闭Socket
3.为多个客户端服务
服务器端可以处理多个客户端的请求,那么服务器端要有类似以下的代码实现:
while(true){
Socket socket=serverSocket.accept();
Runnable r=new TestThread(socket);
Thread t=new Thread(r);
t.start(); }
TestThread实现了Runnable接口,在它的run方法中包含了与客户端循环通信的代码。
class TestThread implements Runnable{........
public void run(){
try{ ..........;}
catch(IOException e){handle exception}
} }
4. 建立URL连接
1)URL和URI URL和URLConnection类封装了大量复杂的实现细节,这些细节涉及如何从远程站点获取信息。可以通过传递一个字符串来构建一个URL对象,URL url=new URL(urlString);获得资源内容 InputStream in=url.openStream();
URL:统一资源定位符 URI:统一资源标示符
在JAVA类库中URI类不包含任何用于访问资源的方法,它的唯一作用就是解析;URL类可以打开一个到达资源的流,因此URL类只能作用于Java类库知道该如何处理的模式,如http:、https:、ftp:、本地文件系统file:、JAR文件jar:
2)使用URLConnection获取资源
操作URLConnection对象的步骤:
a.调用URL类中的openConnection()获得URLConnection对象。
URLCOnnection connection=url.openConnection();
b.设置任意的请求属性
c.调用connect()连接远程资源 connection.connect();//除了与服务器建立套接字连接外,还可向服务器查询头信息.
d.与服务器建立连接后,可以查询头信息。getHeaderFieldKey和getHeaderField()两个方法列举消息头所有字段。
e.最后访问资源
URLConnectin类的方法:a.先介绍几个可以与服务器建立连接之前设置连接属性的方法
setDoInput()和setDoOutput(),默认情况下建立的连接只有从服务器读取信息的输入流,并没有任何执行写操作的输出流。要想获得输出流(如想服务器提交一个数据),既要调用connection.setDoOutput(true);
setIfModifiedSince()用于告诉连接你只对自某个特定日期依赖修改过的数据感兴趣;
setUseCaches和setAllowUserInteraction():之用于applet。前一个方法命令浏览器首先检查它的缓存,后一个则用于在访问有密码保护的资源是弹出对话框,以便查询用户名和密码。
setRequestProperty():用来设置对特定协议起作用的任何“名-值对”//如访问一个有密码保护的Web页connection.
setRequestProperty("Authorization","Basic"+new sun.misc.BASE64Encoder().encode(input.getBytes));
b.与服务器建立连接后使用的方法:
getHeaderFieldKey(n):获得响应头的第n个值其中n从1开始。
getHeaderFields(n):得到第n个值。
3)base64编码:用于将字节流编码成可打印的ASCII字符流。
代码如下:{ByteArrayOutputStream bOut=new ByteArrayOutputStream();
Base64OutputStream out=new Base64OutputStream(bOut);
try{out.write(s.getBytes());out.flush();}
catch(IOException e){}
return bOut.toString(); }
关于编码,我们看看这两个类URLEncoder和URLDecoder
URLEncoder.encode(String s,String encoding)采用指定的字符编码模式对字符串s进行编码,如按UTF-8编码;
URLDecoder.decode(String s,String encoding)采用指定编码模式对已编码字符串s进行解码,并返回结果。
5 高级套接字编程
1)套接字超时:在实际应用中,我们可能并不想从套接字读取信息,因为在数据可以被访问之前,读操作将会被阻塞,如果此时主机不可达,那么我们的应用将要等待很长的时间,并且因为受底层操作系统的限制而最终会导致超时。
解决方案:在构造socket对象时设置超时值
Socket s=new Socket(String host,int port); s.setSoTimeout(10000);或
Socket s=new Socket(); s.connect(new InetSocketAddress(host,port),timeout);
注意:如果设置了超时值,并且以后的读操作和写操作在没有被完成之前就超过了时间限制,那么这些操作就会抛
出SocketTimeoutException异常。
2)可中断套接字:当连接一个套接字,当前线程会被阻塞知道建立连接或超时为止,同样当通过套接字读取数据或写时,当前线程也会被阻塞知道操作成功或超时为止,这个时候我们就要有一个功能用以取消那些看似不会成功的连接,即中断套接字。
为了中断套接字操作,可以使用java.nio包提供的一个特性—SocketChannel类;
SocketChannel channel=SocketChannel.open(new InetSocketAddress(host,port));
因为channel(通道)并没有与之相关联的流,它所拥有的read和write方法时通过Buffer对象来实现的ReadableByteChannel和WriteableByteChannel接口都声明者两个方法。
如果不想处理缓存,可以使用Scanner类来读取信息,因为Scanner有一个带ReadableByteChannel参数的构造器:
Scanner in =new Scanner(channel);
通过调用静态方法Channels.newOutputStream(channel)获得输出流。
3)半关闭:
当客户端程序发送一个请求给服务器时,服务器必须能够确定该请求何时结束。断开与服务器的连接不像写一个文件时只需在数据写完后关闭文件即可,如果关闭一个套接字,那么僵立即断开与服务器的连接。
使用半关闭的方法就可以解决上述问题,不会立即与服务器断开。
半关闭:通过关闭一个套接字的输出流来表示发送给服务器的请求数据已经结束,保留输入流打开用以读取服务器返回的响应信息。
在客户端使用半关闭的代码如下:Socket socket=new Socket(host,port);
Scanner in=new Scanner(scoket.getInputStream);
PrintWriter writer=new PrintWriter(socket.getOutputStream())
writer.print(......); writer.flush();
socket.shutdownOutput();//现在套接字是半关闭状态了
while(in.hasNextLine()!=null){String line=in.nextLine();......}
socket.close();