计算机网络基础
什么是计算机网络
把分布在不同地理区域的计算机与专门的外部设备用通信线路互联成一个规模大,功能强的网络系统,从而使众多的计算机可以方便的相互传递信息,共享硬件,软件,数据信息等资源.
计算机网络的主要功能
资源共享
信息传输与集中处理
均衡负荷与分布处理
综合信息服务
计算机网络分类
按规模大小和延伸范围划分:
局域网(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[]){
-
-
- 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编程
- 两个进程间可以通过一个双向的网络通信连接实现数据交换,这种通信链路的端点被称为"套接字"(Socket)
- Socket通常用来实现Client-Server连接.
- 建立连接时所需的寻址信息:1远程计算机的机器名或ip地址;2试图连接的端口号(Port number)
java.net包中定义了两个类Socket和ServerSocket,分别用来实现双向连接的client和server端.
Socket通信模型
Socket编程基本步骤
- 建立网络连接
- 打开连接到Socket的输入/输出流;
- 通过已打开的i/o流进行数据读/写操作;
- 关闭已打开的i/o流和Socket
图像用户界面自由聊天程序
多用户聊天室程序
- 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.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();
-
- ssc = ServerSocketChannel.open();
-
- 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 {
-
- SelectionKey acceptKey = ssc.register(selector, SelectionKey.OP_ACCEPT );
- SocketChannel sc;
- int n;
- String name;
- String msg;
- while ( true ){
-
- n = selector.select();
- if (n > 0 ){
-
- Set readyKeys = selector.selectedKeys();
- Iterator it = readyKeys.iterator();
-
- while (it.hasNext()) {
- SelectionKey key = (SelectionKey)it.next();
-
- it.remove();
-
- if (key.isAcceptable()) {
-
- ssc = (ServerSocketChannel) key.channel();
-
- sc = (SocketChannel) ssc.accept();
-
- if (sc != null ){
-
- name = readMessage(sc);
-
- sc.configureBlocking(false );
-
-
- SelectionKey newKey = sc.register(selector,SelectionKey.OP_WRITE,name);
-
- userList.put(name,sc);
-
- transmitMessage(name + " in!" , "--Server Info--" );
- }
- }
-
- else if (key.isWritable()) {
-
- 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();
- }
- }