计算机网络基础
什么是计算机网络
把分布在不同地理区域的计算机与专门的外部设备用通信线路互联成一个规模大,功能强的网络系统,从而使众多的计算机可以方便的相互传递信息,共享硬件,软件,数据信息等资源.
计算机网络的主要功能
资源共享
信息传输与集中处理
均衡负荷与分布处理
综合信息服务
计算机网络分类
按规模大小和延伸范围划分:
局域网(LAN-local area network)
城域网(MAN-metropolitan area network)
广域网(WAN-wide area network)
按照网络的拓扑(Topology)结构划分:
环形网,星形网,总线型网等
按照通信,传输的介质来划分:
双绞线网,同轴电缆网,光纤网,卫星网等.
按照信号频带占方式划分:
基带网和宽带网
计算机网络工作模式
专用服务器结构(Server-Based)
又称为"工作站/文件服务器"结构,由若干台微机工作站与一台或多台文件服务器通过通信线路连接起来组成工作站存取服务器文件,共享存储设备.
客户机/服务器模式(Client/Server,C/S)
其中一台或几台较大的计算机集中进行共享数据库的管理和存取,称为服务器,而将其它的应用处理工作分散到网络中其他微机上去做,构成分布式的处理系统.
对等式网络(Peer-to-Peer,P2P)
在拓扑结构上与专用Server与C/S相同,在对等式网络结构中,没有专用服务器,每一个工作站既可以起客户机作用也可以做1服务器作用.
网络通信协议及接口
什么事网络通信协议
计算机网络中实现通信必须有一些约定即通信协议,对速率,传输代码,代码结构,传输控制步骤,出错控制等制定标准.
网络通信接口
为了使两个结点之间能进行对话,必须在它们之间建立通信工具(即接口),使彼此之间,能进行信息交换,接口包括两部分:
硬件装置:实现结点之间的信息传送
软件装置:规定双方进行通信的约定协议
通信协议分层的思想
为什么要分层
由于结点之间联系很复杂,在制定协议时,把复杂成份分解成一些简单的成份,再将它们复合起来,最常用的复合方式是层次方式,即同层间可以通信,上一层可以调用下一层,而与再下一层不发生关系.
通信协议的分层规定
把用户应用程序作为最高层,把物理通信线路作为最底层,将其间的协议处理分为若干层,规定每层处理的任务,也规定每层的接口标准.
数据的封装与拆封
封装:发送方数据在网络模型的各层传送过程中加入头尾的过程
拆封:接受方收到书记后去除相应的头尾的过程.
常用网络通信协议
TCP/IP协议
TCP(Transmission Control Protocol,传输控制协议)
IP(Internet Protocol,网际协议)
HTTP协议
HTTP(Hypertext Transfeer Protocol,超文本传输协议)
FTP协议
FTP(File Transfer Protocol,文本传输协议)
SMTP协议
SMTP(Simple Mail Transfer Protocol,简单邮件传输协议)
POP3/IMAP协议
pop3(Post office Protoco-version3,邮局协议版本3)
IMAP(Internet Message Access Protocol,Internet消息访问协议)
IP地址/域名/端口
IP地址
Tcp/ip使用ip地址来标识源地址和目的地地址
ip地址格式:192.111.12.43
目前正在普遍使用的ip协议时第4版(Version 4)的称为IPv4,新版本(IPV6)协议已开始推广.
域名(Domain Address)
便于记忆的,字符串形式,如:baidu.com
与ip地址间存在映射关系,由位于网络中的域名服务器(DNS,Domain Name Server)负责将域名解析为相应的ip地址.
端口(Port)
逻辑协议上的数据传输通道,或者说模拟通道,Tcp/ip协议约定,每台计算机拥有65536个这种逻辑通信端口.
端口号:用于标识这些端口的整数编号,其取值范围为0-65535
相关API
JDK的java.net包中定义了与ip地址/域名有关的类
java.net.InetAddress
32位或128位无符号数字表示的ip地址
java.net.Inet4Address
继承了InetAddress类,以32位无符号数字表示的IPv4地址,其典型表示形式是由圆点分隔开的4段,取值范围0-255的十进制数值
java.net.Inet6Address
继承了InetAddress,以128位无符号数字表示的ipv6地址,其典型表示形式是由冒号分隔开的8段,取值范围0000-ffff的十六进制数值,例如"1090:2:2:2:1:311:123C:123A"
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("----------------"); } }
URL
URL(Uniform Resource Locator统一资源定位器)用于表示Internet上资源的地址。
URL格式:
<协议名><资源所在主机名>[:<端口号>]<资源名>
例如:http://www.baidu.com/map/index.htm
java.net包定义了对应的URL类常用方法:
public URL(String spec);
public final InputStream openStream() throws IOException
使用URL读取网络资源
import java.io.InputStream; import java.io.InputStreamReader; import java.io.BufferedReader; import java.io.IOException; import java.net.URL; import java.net.MalformedURLException; 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())); 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); } } }
Socket编程
java.net包中定义了两个类Socket和ServerSocket,分别用来实现双向连接的client和server端.
Socket通信模型
Socket编程基本步骤
图像用户界面自由聊天程序
import java.io.*; import java.net.*; import java.awt.*; import java.awt.event.*; public class TestServer { private ServerSocket ss; private Socket s; private DataInputStream dis; private DataOutputStream dos; private TextArea ta; private TextField tf; public static void main(String args[]) { TestServer ts = new TestServer(); ts.createUI(); ts.connect(); ts.createThread(); } public void connect(){ try { ss = new ServerSocket(8888); s = ss.accept(); dis = new DataInputStream(s.getInputStream()); dos = new DataOutputStream(s.getOutputStream()); }catch (IOException e) { e.printStackTrace(); //13701303436 } } public void createUI(){ Frame f = new Frame("Server"); ta = new TextArea(); tf = new TextField(); Button send = new Button("发送"); Panel p = new Panel(); p.setLayout(new BorderLayout()); p.add(tf,"Center"); p.add(send,"East"); f.add(ta,"Center"); f.add(p,"South"); MyServerListener listener = new MyServerListener(this); send.addActionListener(listener); tf.addActionListener(listener); f.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ System.exit(0); } }); f.setSize(400,400); f.setVisible(true); } public void createThread(){ MyServerReader reader = new MyServerReader(this); reader.start(); } public void close(){ try{ dis.close(); dos.close(); s.close(); ss.close(); }catch (IOException e) { e.printStackTrace(); } } public DataInputStream getDataInputStream(){ return dis; } public DataOutputStream getDataOutputStream(){ return dos; } public TextArea getTextArea(){ return ta; } public TextField getTextField(){ return tf; } } class MyServerListener implements ActionListener{ private TestServer server; public MyServerListener(TestServer server){ this.server = server; } public void actionPerformed(ActionEvent e){ TextField tf = server.getTextField(); String info = tf.getText(); server.getTextArea().append("自己说: " + info + "\n"); try{ server.getDataOutputStream().writeUTF(info); }catch (IOException e1) { e1.printStackTrace(); } if(info.equals("bye")){ server.close(); System.exit(0); } tf.setText(""); tf.requestFocus(); } } class MyServerReader extends Thread{ private TestServer server; public MyServerReader(TestServer server){ this.server = server; } public void run(){ String info; DataInputStream dis = server.getDataInputStream(); TextArea ta = server.getTextArea(); try{ while(true){ info = dis.readUTF(); ta.append("对方说: " + info + "\n"); if(info.equals("bye")){ server.close(); System.exit(0); } } }catch (IOException e) { e.printStackTrace(); } } }
import java.io.*; import java.net.*; import java.awt.*; import java.awt.event.*; public class TestClient { private Socket s; private DataInputStream dis; private DataOutputStream dos; private TextArea ta; private TextField tf; public static void main(String args[]) { TestClient tc = new TestClient(); tc.createUI(); tc.connect(); tc.createThread(); } public void connect(){ try { s = new Socket("127.0.0.1",8888); dos = new DataOutputStream(s.getOutputStream()); dis = new DataInputStream(s.getInputStream()); }catch (IOException e) { e.printStackTrace(); } } public void createUI(){ Frame f = new Frame("Client"); ta = new TextArea(); tf = new TextField(); Button send = new Button("发送"); Panel p = new Panel(); p.setLayout(new BorderLayout()); p.add(tf,"Center"); p.add(send,"East"); f.add(ta,"Center"); f.add(p,"South"); MyClientListener listener = new MyClientListener(this); send.addActionListener(listener); tf.addActionListener(listener); f.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ System.exit(0); } }); f.setSize(400,400); f.setLocation(600,0); f.setVisible(true); } public void createThread(){ MyClientReader reader = new MyClientReader(this); reader.start(); } public void close(){ try{ dis.close(); dos.close(); s.close(); }catch (IOException e) { e.printStackTrace(); } } public DataInputStream getDataInputStream(){ return dis; } public DataOutputStream getDataOutputStream(){ return dos; } public TextArea getTextArea(){ return ta; } public TextField getTextField(){ return tf; } } class MyClientListener implements ActionListener{ private TestClient client; public MyClientListener(TestClient client){ this.client = client; } public void actionPerformed(ActionEvent e){ TextField tf = client.getTextField(); String info = tf.getText(); client.getTextArea().append("自己说: " + info + "\n"); try{ client.getDataOutputStream().writeUTF(info); }catch (IOException e1) { e1.printStackTrace(); } if(info.equals("bye")){ client.close(); System.exit(0); } tf.setText(""); tf.requestFocus(); } } class MyClientReader extends Thread{ private TestClient client; public MyClientReader(TestClient client){ this.client = client; } public void run(){ String info; DataInputStream dis = client.getDataInputStream(); TextArea ta = client.getTextArea(); try{ while(true){ info = dis.readUTF(); ta.append("对方说: " + info + "\n"); if(info.equals("bye")){ client.close(); System.exit(0); } } }catch (IOException e) { e.printStackTrace(); } } }
多用户聊天室程序
import java.io.*; import java.net.*; import java.awt.*; import java.awt.event.*; public class ChatClient { private String name; private Socket s; private DataInputStream dis; private DataOutputStream dos; private Frame f; private TextArea ta; private TextField tf; private boolean runnable = true; public static void main(String args[]) { ChatClient cc = new ChatClient(); cc.createUI(); cc.inputName(); cc.connect(); cc.createThread(); } public void createUI(){ f = new Frame("Client"); ta = new TextArea(); ta.setEditable(false); tf = new TextField(); Button send = new Button("Send"); Panel p = new Panel(); p.setLayout(new BorderLayout()); p.add(tf,"Center"); p.add(send,"East"); f.add(ta,"Center"); f.add(p,"South"); MyClientListener listener = new MyClientListener(this); send.addActionListener(listener); tf.addActionListener(listener); f.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ ChatClient.this.shutDown(); } }); f.setSize(400,400); f.setLocation(600,0); f.setVisible(true); tf.requestFocus(); } public void inputName(){ String name = javax.swing.JOptionPane.showInputDialog("Input Your Name:"); this.setName(name); f.setTitle(name); } public void connect(){ try { s = new Socket("127.0.0.1",9999); dos = new DataOutputStream(s.getOutputStream()); dis = new DataInputStream(s.getInputStream()); dos.writeUTF(name); }catch (IOException e) { e.printStackTrace(); } } public void createThread(){ MyClientReader reader = new MyClientReader(this); reader.start(); } public void stop(){ runnable = false; } public void shutDown(){ try{ dos.writeUTF("bye"); ta.append("Exit in 5 seconds!"); this.stop(); Thread.sleep(5000); dis.close(); dos.close(); s.close(); }catch(Exception e){ } System.exit(0); } public boolean getRunnable(){ return runnable; } public void setName(String name){ this.name = name; } public DataInputStream getDataInputStream(){ return dis; } public DataOutputStream getDataOutputStream(){ return dos; } public TextArea getTextArea(){ return ta; } public TextField getTextField(){ return tf; } } class MyClientListener implements ActionListener{ private ChatClient client; public MyClientListener(ChatClient client){ this.client = client; } public void actionPerformed(ActionEvent e){ TextField tf = client.getTextField(); String info = tf.getText(); try{ client.getDataOutputStream().writeUTF(info); }catch (IOException e1) { e1.printStackTrace(); } if(info.equals("bye")){ client.shutDown(); } tf.setText(""); tf.requestFocus(); } } class MyClientReader extends Thread{ private ChatClient client; public MyClientReader(ChatClient client){ this.client = client; } public void run(){ String info; DataInputStream dis = client.getDataInputStream(); TextArea ta = client.getTextArea(); try{ while(client.getRunnable()){ info = dis.readUTF(); ta.append(info + "\n"); } }catch (IOException e) { } } }
import java.io.*; import java.net.*; import java.util.*; public class ChatServer { public static void main(String args[]) { Hashtable<String,DataOutputStream> userList = new Hashtable<String,DataOutputStream>(); String name; DataInputStream dis; DataOutputStream dos; try{ ServerSocket ss = new ServerSocket(9999); while(true){ Socket s = ss.accept(); dis = new DataInputStream(s.getInputStream()); dos = new DataOutputStream(s.getOutputStream()); name = dis.readUTF(); userList.put(name,dos); new MyServerReader(name,dis,userList).start(); } }catch(Exception e){ e.printStackTrace(); } } } class MyServerReader extends Thread{ private String name; private DataInputStream dis; private Hashtable<String,DataOutputStream> userList; public MyServerReader(String name,DataInputStream dis,Hashtable<String,DataOutputStream> userList ){ this.name = name; this.dis = dis; this.userList = userList; } public void run(){ String info; try{ transmitMessage(name + " in!","--Server Info--"); while(true){ info = dis.readUTF(); if(info.equals("bye")){ DataOutputStream dos = (DataOutputStream)(userList.get(name)); Thread.sleep(1000); dos.close(); dis.close(); userList.remove(name); transmitMessage(name + " out!","--Server Info--"); break; }else if(info.length()>0){ transmitMessage(info,name); } } }catch (Exception e) { } } public void transmitMessage(String msg,String name){ Collection doses = userList.values(); DataOutputStream dos; for(Object o: doses){ dos = (DataOutputStream)o; try{ dos.writeUTF(name + ":" + msg); }catch(Exception e){ } } } }
非阻塞式Socket通信
阻塞通信意味着通信方法在尝试访问套接字或者读写数据时阻塞了对套接字的访问。在 JDK 1.4 之前,绕过 阻塞限制的方法是无限制地使用线程,但这样常常会造成大量的线程开销,对系统的性能和可伸缩性产生影响。java.nio 包改变了这种状况,允许服务器有效地使用 I/O 流,在合理的时间内处理所服务的客户请求。
没有非阻塞通信,这个过程就像我所喜欢说的“为所欲为”那样。基本上,这个过程就是发送和读取任何能够发送/读取的东西。如果没有可以读取的东西,它就中止读操作,做其他的事情直到能够读取为止。当发送数据时,该过程将试图发送所有的数据,但返回实际发送出的内容。可能是全部数据、部分数据或者根本没有发送数据。
阻塞与非阻塞相比确实有一些优点,特别是遇到错误控制问题的时候。在阻塞套接字通信中,如果出现错误,该访问会自动返回标志错误的代码。错误可能是由于网络超时、套接字关闭或者任何类型的 I/O 错误造成的。在非阻塞套接字通信中,该方法能够处理的唯一错误是网络超时。为了检测使用非阻塞通信的网络超时,需要编写稍微多一点的代码,以确定自从上一次收到数据以来已经多长时间了
哪种方式更好取决于应用程序。如果使用的是同步通信,如果数据不必在读取任何数据之前处理的话,阻塞通信更好一些,而非阻塞通信则提供了处理任何已经读取的数据的机会。而异步通信,如 IRC 和聊天客户机则要求非阻塞通信以避免冻结套接字。
使用非阻塞实现I/O实现多用户聊天.
import java.net.*; import java.nio.*; import java.nio.channels.*; import java.nio.charset.*; import java.awt.*; import java.awt.event.*; public class ChatClient { private SocketChannel sc = null; private String name = null; private Frame f; private TextArea ta; private TextField tf; private boolean runnable = true; public static void main(String[] args){ ChatClient cc = new ChatClient(); cc.createUI(); cc.inputName(); cc.connect(); new ReceiveThread(cc,cc.getTextArea()).start(); } public SocketChannel getSc(){ return sc; } public void setName(String name){ this.name = name; } public TextArea getTextArea(){ return ta; } public TextField getTextField(){ return tf; } public boolean getRunnable(){ return runnable; } public void stop(){ runnable = false; } public void shutDown(){ try{ sc.write(ByteBuffer.wrap("bye".getBytes("GBK"))); ta.append("Exit in 5 seconds!"); this.stop(); Thread.sleep(5000); sc.close(); }catch(Exception e){ e.printStackTrace(); } System.exit(0); } public void createUI(){ f = new Frame("Client"); ta = new TextArea(); ta.setEditable(false); tf = new TextField(); Button send = new Button("Send"); Panel p = new Panel(); p.setLayout(new BorderLayout()); p.add(tf,"Center"); p.add(send,"East"); f.add(ta,"Center"); f.add(p,"South"); MyClientListener listener = new MyClientListener(this); send.addActionListener(listener); tf.addActionListener(listener); f.addWindowListener(new WindowAdapter(){ public void windowClosing(WindowEvent e){ ChatClient.this.shutDown(); } }); f.setSize(400,400); f.setLocation(600,0); f.setVisible(true); tf.requestFocus(); } public boolean connect(){ try{ sc = SocketChannel.open(); //"zlg"为目标计算机名 InetSocketAddress isa = new InetSocketAddress("zlg",8888); sc.connect(isa); sc.configureBlocking(false); sc.write(ByteBuffer.wrap(name.getBytes("GBK"))); }catch(Exception e){ e.printStackTrace(); } return true; } public void inputName(){ String name = javax.swing.JOptionPane.showInputDialog("Input Your Name:"); this.setName(name); f.setTitle(name); } } class MyClientListener implements ActionListener{ private ChatClient client; public MyClientListener(ChatClient client){ this.client = client; } public void actionPerformed(ActionEvent e){ TextField tf = client.getTextField(); String info = tf.getText(); if(info.equals("bye")){ client.shutDown(); }else{ try{ client.getSc().write(ByteBuffer.wrap(info.getBytes("GBK"))); }catch (Exception e1) { e1.printStackTrace(); } } tf.setText(""); tf.requestFocus(); } } class ReceiveThread extends Thread{ private ChatClient client; private TextArea ta; public ReceiveThread(ChatClient client,TextArea ta){ this.client = client; this.ta = ta; } public void run(){ SocketChannel sc = client.getSc(); ByteBuffer byteBuffer = ByteBuffer.allocate(2048); CharBuffer charBuffer = null; Charset charset = Charset.forName("GBK"); CharsetDecoder decoder = charset.newDecoder(); String msg = null; int n = 0; try{ while(client.getRunnable()){ n = sc.read(byteBuffer); if(n>0){ byteBuffer.flip(); charBuffer = decoder.decode(byteBuffer); msg = charBuffer.toString(); ta.append(msg + "\n"); } byteBuffer.clear(); Thread.sleep(500); } }catch(Exception e){ e.printStackTrace(); System.exit(0); } } }
import java.io.*; import java.nio.*; import java.nio.channels.*; import java.nio.charset.*; import java.net.*; import java.util.*; public class ChatServer{ private Selector selector = null; private ServerSocketChannel ssc = null; //服务器端通信端口号 private int port = 8888; //在线用户列表 private Hashtable<String,SocketChannel> userList = null; public ChatServer(){} public ChatServer(int port){ this.port = port; } //初始化服务器 public void init(){ try{ //创建选择器对象 selector = Selector.open(); //创建ServerSocketChannel ssc = ServerSocketChannel.open(); //设置ServerSocketChannel为非阻塞模式 ssc.configureBlocking(false); InetAddress ia = InetAddress.getLocalHost(); InetSocketAddress isa = new InetSocketAddress(ia,port); //将与本通道相关的服务器套接字对象帮定到指定地址和端口 ssc.socket().bind(isa); //创建在线用户列表 userList = new Hashtable<String,SocketChannel>(); }catch(IOException e){ e.printStackTrace(); } } //启动服务器 public void start(){ try{ //将ServerSocketChannel注册到Selector上,准备接收新连接请求 SelectionKey acceptKey = ssc.register(selector, SelectionKey.OP_ACCEPT ); SocketChannel sc; int n; String name; //用户名 String msg; //用户发言信息 while (true){ //选择当前所有处于就绪状态的通道所对应的选择键,并将这些键组成已选择键集 n = selector.select(); //n为已选择键集中键的个数 if(n > 0 ){ //获取此选择器的已选择键集。 Set readyKeys = selector.selectedKeys(); Iterator it = readyKeys.iterator(); //遍历当前已选择键集 while (it.hasNext()) { SelectionKey key = (SelectionKey)it.next(); //从当前已选择键集中移除当前键,避免重复处理 it.remove(); //如果当前键对应的通道已准备好接受新的套接字连接 if (key.isAcceptable()) { //获取当前键对应的可选择通道(ServerSocketChannel) ssc = (ServerSocketChannel) key.channel(); //接收新的套接字连接请求,返回新建的SocketChannel sc = (SocketChannel) ssc.accept(); //如果有新用户接入 if(sc != null){ //接收新上线用户姓名 name = readMessage(sc); //设置新建的SocketChannel为非阻塞模式 sc.configureBlocking(false); //将新建的SocketChannel注册到Selector上,准备进行数据"写"操作, //并将当前用户名以附件的方式附带记录到新建的选择键上。 SelectionKey newKey = sc.register(selector,SelectionKey.OP_WRITE,name); //将新上线用户信息加入到在线用户列表 userList.put(name,sc); //发送"新用户上线"通知 transmitMessage(name + " in!","--Server Info--"); } } //否则,如果当前键对应的通道已准备好进行"写"操作 else if (key.isWritable()) { //获取当前键对应的可选择通道(SocketChannel) sc = (SocketChannel)key.channel(); //接收该通道相应用户的发言信息 msg = readMessage(sc); //获取选择键上附带记录的当前用户名 name = key.attachment().toString(); //如果用户提出要下线 if(msg.equals("bye")){ //从在线用户列表中移除当前用户 userList.remove(name); //注销当前选择键对应的注册关系 key.cancel(); //关闭当前可选择通道 sc.close(); //发送"用户下线"通知 transmitMessage(name + " out!","--Server Info--"); } //否则,如果接收到的用户发言信息非空("") else if(msg.length() > 0){ //转发用户发言信息 transmitMessage(msg,name); } } } } //延时循环,降低服务器端处理负荷 Thread.sleep(500); } }catch(Exception e){ e.printStackTrace(); } } //转发用户发言信息 public void transmitMessage(String msg,String name){ try{ ByteBuffer buffer = ByteBuffer.wrap((name + ":" + msg).getBytes("GBK")); Collection channels = userList.values(); SocketChannel sc; for(Object o:channels){ sc = (SocketChannel)o; sc.write(buffer); buffer.flip(); } }catch(Exception e){ e.printStackTrace(); } } //接收用户发言信息 public String readMessage(SocketChannel sc){ String result = null; int n = 0; ByteBuffer buf = ByteBuffer.allocate(1024); try{ n = sc.read(buf); buf.flip(); Charset charset = Charset.forName("GBK"); CharsetDecoder decoder = charset.newDecoder(); CharBuffer charBuffer = decoder.decode(buf); result = charBuffer.toString(); }catch(IOException e){ e.printStackTrace(); } return result; } public static void main(String args[]){ ChatServer server = new ChatServer(); server.init(); server.start(); } }