1.计算机网络工作模式 和 通信协议参考模型
专用服务器结构(Server-Based)
又称为“工作站/文件服务器”结构,由若干台微机工作站与一台或多台文件服务器通过通信线路连接起来组成工作站
存取服务器文件,共享存储设备。
客户机/服务器模式(Client/Server,C/S)
其中一台或几台较大的计算机集中进行共享数据库的管理和存取,称为服务器,而将其它的应用处理工作分散到网络中其它微机上去做,构成分布式的处理系统。
对等式网络(Peer-to-Peer,P2P)
在拓扑结构上与专用Server与C/S相同。在对等式网络结构中,没有专用服务器每一个工作站既可以起客户机作用也可以起服务器作用。
协议分层:
2.常用网络通信协议 和 IP地址/域名/端口
TCP/IP协议
TCP (Transmission Control Protocol,传输控制协议)
IP (Internet Protocol,网际协议)
HTTP协议
HTTP(Hypertext Transfer Protocol,超文本传输协议)
FTP协议
FTP(File Transfer Protocol,文件传输协议)
SMTP协议
SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)
POP3/IMAP协议
POP3(Post Office Protocol - Version 3,邮局协议版本3)
IMAP(Internet Message Access Protocol,Internet消息访问协议)
IP地址
TCP/IP使用IP地址来标识源地址和目的地址
IP地址格式:166.111.78.98。
目 前 正 在 普 遍 使 用 的 IP 协 议 是 第 4 版 ( Version 4)的 , 称 为“IPv4”,新版本的(IPv6)协议也已开始推广。
域名(Domain Address)
便于记忆的、字符串形式,如“news.sina.com.cn”。
与IP地址间存在映射关系,由位于网络中的域名服务器(DNS,Domain Name Server)负责将域名解析为相应的IP地址。
端口(Port)
逻辑意义上的数据传输通道、或者说模拟通道。TCP/IP 协议约定,每台计算机拥有65536个这种逻辑通信端口。
端口号:用于标识这些端口的整数编号,其取值范围为0~65535。
3.相关API
JDK的java.net包中定义了与IP地址/域名有关的类
java.net.InetAddress
32位或128位无符号数字表示的IP地址
java.net.Inet4Address
继承了InetAddress类,以32位无符号数字表示的IPv4地址。其典型
表示形式是由圆点分隔开的4段、取值范围0~255的十进制数值,
例如"166.111.78.98"。
java.net.Inet6Address
继承了InetAddress类,以128位无符号数字表示的IPv6地址。其典
型表示形式是由冒号分隔开的8段、取值范围0000~FFFF的十六进
制数值,例如"1080:0:0:0:8:800:200C:417A"。
用法举例:
package v512;
import java.net.InetAddress;
public class TestInetAddress {
public static void main( String args[]) {
try{
InetAddress ia = InetAddress.getLocalHost();
showInfo(ia);
ia = InetAddress.getByName("www.sina.com.cn");
showInfo(ia);
}catch(java.net.UnknownHostException e){
e.printStackTrace();
}
}
public static void showInfo(InetAddress ia){
String name = ia.getHostName();
String address = ia.getHostAddress();
System.out.println(name);
System.out.println(address);
System.out.println("----------------");
}
}
输出结果:
HJW
169.254.175.55
----------------
www.sina.com.cn
59.175.132.70
----------------
4.URL
URL(Uniform Resource Locator,统一资源定位器)用于表示Internet上资源的地址。
URL格式:<协议名><资源所在主机名>[:<端口号>]<资源名>
http://home.netscape.com/home/welcome.html
http://www.sun.com
http://java.cs.tsinghua.edu.cn:8888/
java.net包定义了对应的URL类,常用方法如下:
public URL(String spec);
public final InputStream openStream() throws IOException
用法举例:
package v512;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
public class URLReader {
public static void main(String args[]) {
// System.setProperty("http.proxyHost","192.168.1.1");
// System.setProperty("http.proxyPort","11080");
try {
URL tirc = new URL("http://www.google.cn/");
BufferedReader in = new BufferedReader(new InputStreamReader(
tirc.openStream(), "utf-8"));//InputStreamReader字节流,读取的是远程资源(网页的文本内容)
String s;
while ((s = in.readLine()) != null)
System.out.println(s);
in.close();
} catch (MalformedURLException e) {
System.out.println(e);
} catch (IOException e) {
System.out.println(e);
}
}
}
输出结果就是一个HTML文件的内容:
<!DOCTYPE html>
<html lang="zh">
<meta charset="utf-8">
<title>Google</title>
<style>
html { background: #fff; margin: 0 1em; }
body { font: .8125em/1.5 arial, sans-serif; text-align: center; }
h1 { font-size: 1.5em; font-weight: normal; margin: 1em 0 0; }
p#footer { color: #767676; font-size: .77em; }
p#footer a { background: url(http://www.google.cn/intl/zh-CN_cn/images/cn_icp.gif) top right no-repeat; padding: 5px 20px 5px 0; }
ul { margin: 2em; padding: 0; }
li { display: inline; padding: 0 2em; }
div { -moz-border-radius: 20px; -webkit-border-radius: 20px; border: 1px solid #ccc; border-radius: 20px; margin: 2em auto 1em; max-width: 650px; min-width: 544px; }
div:hover, div:hover * { cursor: pointer; }
div:hover { border-color: #999; }
div p { margin: .5em 0 1.5em; }
img { border: 0; }
</style>
<div>
<a href="http://www.google.com.hk/webhp?hl=zh-CN&sourceid=cnhp"><img src="http://www.google.cn/landing/cnexp/google-search.png" alt="Google"></a>
<h1><a href="http://www.google.com.hk/webhp?hl=zh-CN&sourceid=cnhp"><strong id="target">google.com.hk</strong></a></h1>
<p>请收藏我们的网址
</div>
<ul>
<li><a href="http://www.google.cn/music?sourceid=cnhp">音乐</a>
<li><a href="http://translate.google.cn/?sourceid=cnhp">翻译</a>
<li><a href="http://www.google.cn/products?sourceid=cnhp">购物</a>
</ul>
<p id="footer">©2011 - <a href="http://www.miibeian.gov.cn/">ICP证合字B2-20070004号</a>
<script>
var gcn=gcn||{};gcn.IS_IMAGES=(/images\.google\.cn/.exec(window.location)||window.location.hash=='#images'||window.location.hash=='images');
gcn.HOMEPAGE_DEST='http://www.google.com.hk/webhp?hl=zh-CN&sourceid=cnhp';gcn.IMAGES_DEST='http://images.google.com.hk/imgcat/imghp?'+'hl=zh-CN&sourceid=cnhp';
gcn.DEST_URL=gcn.IS_IMAGES?gcn.IMAGES_DEST:gcn.HOMEPAGE_DEST;gcn.READABLE_HOMEPAGE_URL='google.com.hk';
gcn.READABLE_IMAGES_URL='images.google.com.hk';gcn.redirectIfLocationHasQueryParams=function(){if(window.location.search&&/google\.cn/.
exec(window.location)&&!/webhp/.exec(window.location)){window.location=String(window.location).replace('google.cn','google.com.hk')}}();
gcn.replaceHrefsWithImagesUrl=function(){if(gcn.IS_IMAGES){var a=document.getElementsByTagName('a');for(var i=0,len=a.length;i<len;i++)
{if(a[i].href==gcn.HOMEPAGE_DEST){a[i].href=gcn.IMAGES_DEST}}}}();gcn.listen=function(a,e,b){if(a.addEventListener){a.addEventListener(e,b,false)}
else if(a.attachEvent){var r=a.attachEvent('on'+e,b);return r}};gcn.stopDefaultAndProp=function(e){if(e&&e.preventDefault){e.preventDefault()}
else if(window.event&&window.event.returnValue){window.eventReturnValue=false;return false}if(e&&e.stopPropagation){e.stopPropagation()}
else if(window.event&&window.event.cancelBubble){window.event.cancelBubble=true;return false}};gcn.resetChildElements=function(a)
{var b=a.childNodes;for(var i=0,len=b.length;i<len;i++){gcn.listen(b[i],'click',gcn.stopDefaultAndProp)}};gcn.redirect=function()
{window.location=gcn.DEST_URL};gcn.setInnerHtmlInEl=function(a){if(gcn.IS_IMAGES){var b=document.getElementById(a);
if(b){b.innerHTML=b.innerHTML.replace(gcn.READABLE_HOMEPAGE_URL,gcn.READABLE_IMAGES_URL)}}};
gcn.listen(document, 'click', gcn.redirect);
gcn.setInnerHtmlInEl('target');
</script>
5.Socket 网络编程
两个进程间可以通过一个双向的网络通信连接实现数据交换,这种通信链路的端点被称为“套接字”(Socket)。
Socket通常用来实现Client-Server连接。
建立连接时所需的寻址信息:
①远程计算机的机器名或IP地址
②试图连接的端口号(Port number)
java.net包中定义的两个类Socket和ServerSocket,分别用来实现双向连接的client和server端。
Socket通信模型
编程步骤:
- 建立网络连接;
- 打开连接到Socket的输入/输出流;
- 通过已打开的I/O流进行数据读/写操作;
- 关闭已打开的I/O流和Socket。
用法举例:
服务器端:
package v512;
import java.io.*;
import java.net.Socket;
import java.net.ServerSocket;
public class TestServer {
public static void main(String args[]) {
try {
ServerSocket s = new ServerSocket(8888);
while (true) {
Socket s1 = s.accept();
OutputStream os = s1.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
dos.writeUTF("你好,客户端地址信息: " + s1.getInetAddress()
+ "\t客户端通信端口号: " + s1.getPort());
dos.writeUTF("再见!");
dos.close();
s1.close();
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
客户端:
package v512;
import java.io.*;
import java.net.Socket;
public class TestClient {
public static void main(String args[]) {
try {
Socket s1 = new Socket("127.0.0.1", 8888);
InputStream is = s1.getInputStream();
DataInputStream dis = new DataInputStream(is);
System.out.println(dis.readUTF());
System.out.println(dis.readUTF());
dis.close();
s1.close();
}catch (IOException e) {
e.printStackTrace();
}
}
}
输出结果:
你好,客户端地址信息: /127.0.0.1 客户端通信端口号: 53036
再见!
升级版本:两端可以自由的发送信息
服务器端:
package v512.chp17.test5;
import java.io.*;
import java.net.*;
public class TestServer {
public static void main(String args[]) {
try {
ServerSocket s = new ServerSocket(8888);
Socket s1 = s.accept();
OutputStream os = s1.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
InputStream is = s1.getInputStream();
DataInputStream dis = new DataInputStream(is);
new MyServerReader(dis).start();
new MyServerWriter(dos).start();
}catch (IOException e) {
e.printStackTrace();
}
}
}
class MyServerReader extends Thread{
private DataInputStream dis;
public MyServerReader(DataInputStream dis ){
this.dis = dis;
}
public void run(){
String info;
try{
while(true){
info = dis.readUTF();
System.out.println("对方说: " + info);
if(info.equals("bye")){
System.out.println("对方下线,程序退出!");
System.exit(0);
}
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
class MyServerWriter extends Thread{
private DataOutputStream dos;
public MyServerWriter(DataOutputStream dos){
this.dos = dos;
}
public void run(){
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
String info;
try{
while(true){
info = br.readLine();
dos.writeUTF(info);
if(info.equals("bye")){
System.out.println("自己下线,程序退出!");
System.exit(0);
}
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
客户端:
package v512.chp17.test5;
import java.io.*;
import java.net.*;
public class TestClient {
public static void main(String args[]) {
try {
Socket s1 = new Socket("127.0.0.1",8888);
InputStream is = s1.getInputStream();
DataInputStream dis = new DataInputStream(is);
OutputStream os = s1.getOutputStream();
DataOutputStream dos = new DataOutputStream(os);
new MyClientReader(dis).start();
new MyClientWriter(dos).start();
}catch (IOException e) {
e.printStackTrace();
}
}
}
class MyClientReader extends Thread{
private DataInputStream dis;
public MyClientReader(DataInputStream dis ){
this.dis = dis;
}
public void run(){
String info;
try{
while(true){
info = dis.readUTF();
System.out.println("对方说: " + info);
if(info.equals("bye")){
System.out.println("对方下线,程序退出!");
System.exit(0);
}
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
class MyClientWriter extends Thread{
private DataOutputStream dos;
public MyClientWriter(DataOutputStream dos){
this.dos = dos;
}
public void run(){
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
String info;
try{
while(true){
info = br.readLine();
dos.writeUTF(info);
if(info.equals("bye")){
System.out.println("自己下线,程序退出!");
System.exit(0);
}
}
}catch (IOException e) {
e.printStackTrace();
}
}
}
测试结果:
这是dataOutputStream 的方法~~使用utf-8编码 其实就是从unicode变过来的,utf8编码把其中的ASC编码变成1个字节,其他其他字符2到3个字节!因为dataInput(output)Stream 属于字节流,所以用这个编码可以把其他形式的转化过来
- writeUTF(String str);
- write(int b) ;
- writeBytes(String s) ;
- 这几个方法可以传的参数不一样。
- writeUTF和writeBytes都可以传String类型的参数,而write就不行了。
- public final void writeUTF(String str)
- throws IOException以与机器无关方式使用 UTF-8 修改版编码将一个字符串写入基础输出流。
- 首先,通过 writeShort 方法将两个字节写入输出流,表示后跟的字节数。该值是实际写出的字节数,不是字符串的长度。根据此长度,使用字符的 UTF-8 修改版编码按顺序输出字符串的每个字符。如果没有抛出异常,则计数器 written 增加写入输出流的字节总数。该值至少是 2 加 str 的长度,最多是 2 加 str 的三倍长度。
- String readUTF()
- throws IOException读入一个已使用 UTF-8 修改版格式编码的字符串。readUTF 的常规协定是:该方法读取使用 UTF-8 修改版格式编码的 Unicode 字符串的表示形式;然后以 String 的形式返回此字符串。
- 首先读取两个字节,并使用它们构造一个无符号 16 位整数,构造方式与 readUnsignedShort 方法的方式完全相同。该整数值被称为 UTF 长度,它指定要读取的额外字节数。然后成组地将这些字节转换为字符。每组的长度根据该组第一个字节的值计算。紧跟在某个组后面的字节(如果有)是下一组的第一个字节。
- 如果组的第一个字节与位模式 0xxxxxxx(其中 x 表示“可能为 0 或 1”)匹配,则该组只有这一个字节。该字节被左侧补零,转换成一个字符。
- 如果组的第一个字节与位模式 110xxxxx 匹配,则该组只由字节 a 和另一个字节 b 组成。如果没有字节 b(因为字节 a 是要读取的最后一个字节),或者字节 b 与位模式 10xxxxxx 不匹配,则抛出 UTFDataFormatException。否则,将该组转换成字符:
- (char)(((a& 0x1F) << 6) | (b & 0x3F))
- 如果组的第一个字节与位模式 1110xxxx 匹配,则该组由字节 a 和另外两个字节 b 和 c 组成。如果没有字节 c(因为字节 a 是要读取的最后两个字节之一),或者字节 b 或字节 c 与位模式 10xxxxxx 不匹配,则抛出 UTFDataFormatException。否则,将该组转换成字符:
- (char)(((a & 0x0F) << 12) | ((b & 0x3F) << 6) | (c & 0x3F))
- 如果组的第一个字节与模式 1111xxxx 或模式 10xxxxxx 匹配,则抛出 UTFDataFormatException。
- 如果在执行整个过程中的任意时间到达文件末尾,则抛出 EOFException。
- 在通过此过程将每个组转换成字符后,按照从输入流中读取相应组的顺序,将这些字符收集在一起,形成一个 String,然后该字符串将被返回。
- 可以使用 DataOutput 接口的 writeUTF 方法写入适合此方法读取的数据。
6.非阻塞式IO
暂未研究。。。