黑马程序员--GUI和网络编程

---------------------- android培训java培训、期待与您交流! ----------------------

 

一、GUI

java提供了一套图形化界面的系统GUI

 

1、创建图形化界面流程

(1)创建frame窗体。

(2)对窗体进行基本设置。比如大小,位置,布局。

(3)定义组件。

(4)将组件通过窗体的add方法添加到窗体中。

(5)让窗体显示,通过setVisible(true)

 

2、事件监听机制的特点

(1)事件源。

    就是awt包或者swing包中的那些图形界面组件。

(1)事件。

    每一个事件源都有自己特有的对应事件和共性事件。

(3)监听器。

    将可以触发某一个事件的动作(不只一个动作)都已经封装到了监听器中。

以上三者,在java中都已经定义好了。直接获取其对象来用就可以了。

(4)事件处理。

    这一步需要我们自己具体定义,就是对产生的动作进行处理。

 

3、让按钮具备退出程序的功能

按钮就是事件源。

那么选择哪个监听器呢?

通过关闭窗体示例了解到,想要知道哪个组件具备什么样的特有监听器。

需要查看该组件对象的功能。

通过查阅button的描述。发现按钮支持一个特有监听addActionListener。

 

GUI的操作都十分直观、简单。可以很快掌握。

java中的图形化编程

图形化用户接口:两种交互方式:

1命令行形式:DOS操作系统,有局限性需要记录命令

CLI:需要记住命令,而且可以进行图形化界面不能的操作。而且命令行很麻烦。

2图形用户化界面的形式:windows视窗操作

GUI:Graphical User Interface.(图形用户界面)

图形化界面的包是java.Awt和javax.swing两个组件

java.awt是抽象的工具包,需要调用本地方法,属于重量级控件

javax.SWing在AWt的基础上,建立一套图形界面系统,其中提供更多的组件,而且完全有java实现,增强了移植术性,属于轻量级控件。

还有swt在eclipse和一样

标签对象的作用是封装文字。

GUI组件分为两大类:

基本组件和容器。

java.awt.Componen文本

java.awt.Container窗口是个特殊的子类,他作为一个组件他可以添加组件,而其他组件是不可以的

创建from
      容器也算是组件,只是特殊的组件。所以java.awt.Container也是java.awt.Component的子类。

程序的GUI部分由AWT线程管理。

Frame对象可以调用方法dispose()释放由此 Window、其子组件及其拥有的所有子组件所使用的所有本机屏幕资源。
即这些 Component 的资源将被破坏,它们使用的所有内存都将返回到操作系统,并将它们标记为不可显示。
通过随后调用 pack() 或 setVisible(true) 重新构造本机资源,可以再次显示 Window 及其子组件。
重新创建的 Window 及其子组件的状态与释放 Window 时这些对象的状态一致(不考虑这些操作之间的其他更改)。
注:当 Java 虚拟机 (VM) 中最后的可显示窗口被释放后,虚拟机可能会终止。

一般关闭一个窗口的顺序是:setVisible(false)---隐藏窗口; dispose()---释放窗口资源; System.exit(0)---退出程序;

WindowEvent对象可以调用方法getWindow()来返回一个Window对象。可以调用getSource()来返回一个Object对象,
可以调用getComponent()来返回一个Component对象,这样可以通过强制类型转换((Window)we.getSource(),(Window)we.getComponent())来得到Window对象。

面向对象的编程思想:编程人员脑海里面想的应该是程序运行时各个时刻各个对象的内存布局和变化状态,以及每行代码执行后对这些对象产生的影响,
而不是仅仅盯住静止的代码和表象,孤立的去琢磨每条语句的语法是否正确。

用户界面操作在触发低级事件(如:MouseEvent)的同时,有可能同时触发高级事件(语义事件(如:ActionEvent))。

一个组件上的一个动作可以产生多种不同类型的事件;
一个事件监听器对象可以注册到多个事件源上;
在一个事件源上也可以注册对同一类事件进行处理的多个事件监听器对象。

我们还可以修改组件的默认事件处理方式。
当一个组件上发生了某种事件以后,系统就会调用组件对象的processEvent()方法,默认的processEvent()方法将根据事件的类型去调用相应的processXxxEvent()方法。
processXxxEvent()方法再去调用事件监听器中的方法,并将事件对象传递过去。
如果我们想改变某个组件的默认的事件处理方式,需要覆盖组件的processEvent()方法或者processXxxEvent()方法。
processEvent()方法是处理所有事件的总入口,processXxxEvent()方法则是专门处理某种事件的分叉路口。

如果没有在组件上注册某某事件的事件监听器,组件上就不会发生某某事件,当然也就不会产生某某事件的事件对象。
所以组件中的processXxxEvent()方法也就不可能被调用,但是如果程序调用了组件的enableEvents((long eventsToEnable))方法,
那么就可以在即使没有注册事件监听器的情况下,组件也能够对某些类型的事件进行响应和产生相应的事件对象。
enableEvents((long eventsToEnable))方法中的eventsToEnable参数,指定了代表某种或者某几种事件类型的数值。
代表事件类型的数值中的每一个bit位都代表一种事件类型。对应的bit位位置上的数值为1的话,就表示对某类事件进行相应。
表示不同事件类型的long类型整数是AWTEvent类中的一些常量。

我们看如下例子程序:
import java.awt.*;
import java.awt.event.*;
class MyButton extends Button
{
private MyButton friend;
public void setFriend(MyButton friend)
{
this.friend = friend;
}
public MyButton(String name)
{
super(name);
enableEvents(AWTEvent.MOUSE_MOTION_EVENT_MASK);
}
protected void processMouseMotionEvent(MouseEvent e)
{
setVisible(false);
friend.setVisible(true);
}
}
public class TestMyButton extends Frame
{
public TestMyButton() {
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
dispose();
System.exit(0);
}
});
}
public static void main(String [] args)
{
TestMyButton f =new TestMyButton();
MyButton btn1 =new MyButton("你来抓我呀!");
MyButton btn2 =new MyButton("你来抓我呀!");
btn1.setFriend(btn2);
btn2.setFriend(btn1);
f.add(btn1, "North");//将btn1增加到f的北部
f.add(btn2, "South");//将btn2增加到f的南部
f.setSize(300,300);
f.setVisible(true);
btn1.setVisible(false);
}
}


Component提供了getGraphics方法返回一个Graphics类对象。
getGraphics()为组件创建一个图形上下文。如果组件当前是不可显示的,则此方法返回 null。所以Component对象首先是可显示的,
才会返回实际的Graphics对象,否则将返回null。

Graphics类提供了在组件上绘制图形,打印文字和显示图像等操作的方法。

new Font(null, Font.ITALIC|Font.BOLD, 30); 中的Font.ITALIC|Font.BOLD表示字体的样式是斜体和加粗效果。

窗口的重绘:其实一个窗口在最小化以后,这个窗口在显存中已经消失,只是在内存中保存了窗口本身相关的参数,
在还原窗口的时候,是根据内存的数据把窗口重新画出来,但是窗口表面用Graphics类所绘画的东西不会重新绘出来。
这个过程称之为曝光。
窗口每次被重绘以后,AWT线程会调用组件的paint(Graphics g)方法。
AWT线程对组件重绘的调用过程:当曝光(窗口改变大小,程序需要在显卡中重新绘制窗口)的时候,
AWT线程会调用组件的paint(Graphics g)方法(paint(Graphics g)方法是由AWT线程调度和管理的,我们不应该在程序中直接调用paint(Graphics g)方法。)
如果我们想让窗口的内容刷新,我们可以调用repaint()方法,repaint()会自动调用组件的update()方法,update()方法会清除组件表面内容,
然后调用paint(Graphics g)方法。

使用Component对象.getToolkit().getImage(String path)语句可以获得Image实例对象。
创建Image类对象的时候,并没有真正的在内存中创建图像数据,而是在需要的时候才去加载Image类对象所表示的图像数据。
又因为drawImage方法是异步方法,也就是说图像还没加载完的时候drawImage的方法已经返回了。
所以我们要用如下的方法来画一幅图像:while(!g.drawImage(img, 0, 0, frame)); 这是一个空循环,直到drawImage返回true的时候就结束循环。
看如下例子:
ex1:这里没有重写paint()方法实现重绘,所以需要while(!g.drawImage(img, 0, 0, frame));来画出图像
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;

public class ImageDraw extends Frame{

public ImageDraw() {
setSize(500, 500);
setVisible(true);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
ImageDraw imgD = new ImageDraw();
Image img = imgD.getToolkit().getImage("caidai.jpg");
Graphics g = imgD.getGraphics();
while(!g.drawImage(img, 0, 0, imgD));
}
}

ex2:这里重写了paint()方法实现重绘,在paint方法里面使用g.drawImage(img, 0, 0, this)即可画出图像。
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;

public class ImageDraw extends Frame{

public ImageDraw() {
setSize(500, 500);
setVisible(true);
}
public void paint(Graphics g) {
Image img = getToolkit().getImage("caidai.jpg");
g.drawImage(img, 0, 0, this);
}
public static void main(String[] args) {
ImageDraw imgD = new ImageDraw();
}
}

使用双缓冲技术将会非常方便和快速的实现对组件表面所有图形的重绘。
使用双缓冲技术的过程:
1,Component对象.createImage(int width, int height)方法创建内存Image对象;
(如:Dimension d = getSize(); img = createImage(d.width, d.height))
注意:在调用createImage(int width, int height)方法之前,首先Component对象是可见的,也就是先setVisible(true);才能返回一个Image对象,否则将返回null。
2,在Image对象上进行绘制的结果就成了一幅图像;(如:g = img.getGraphics(); 用g对象即可进行绘制。)
3,在Image对象上执行与组件表面同样的绘制,那么Image对象中的图像就是组件表面内容的复制,当组件重绘时,只需将内存中的Image对象在组件上画出。

一个使用了双缓冲技术的例子:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

public class LineDraw extends Frame {
Image oImg;
public LineDraw() {
setSize(500, 500);
setVisible(true);
Dimension size = getSize();
oImg = createImage(size.width, size.height);
addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
dispose();
System.exit(0);
}
});
addMouseListener(new MouseAdapter() {
int orgX;
int orgY;
@Override
public void mousePressed(MouseEvent e) {
orgX = e.getX();
orgY = e.getY();
}
@Override
public void mouseReleased(MouseEvent e) {
/*Graphics g = getGraphics();
g.setColor(Color.RED);
g.drawLine(orgX, orgY, e.getX(), e.getY());
Graphics oG = oImg.getGraphics();
oG.setColor(Color.RED);
oG.drawLine(orgX, orgY, e.getX(), e.getY());*/

Graphics oG = oImg.getGraphics();
oG.setColor(Color.RED);
oG.drawLine(orgX, orgY, e.getX(), e.getY());

repaint();
}
});
}

@Override
public void paint(Graphics g) {
if(oImg != null) {
g.drawImage(oImg, 0, 0, this);
}
}
public static void main(String[] args) {
new LineDraw();
}

}


当要设计自定制的具有GUI功能的组件类时,继承Canvas将会大大的简化编程难度。可以自定义外观(覆盖父类的update()方法或者paint()方法)和功能。
要设置Canvas的背景颜色,用public void fill3DRect(int x, int y, int width, int height, boolean raised)方法。会以当前的显示颜色填充矩形区域。

一般情况下,我们需要定制自己的组件的时候才需要修改组件的默认事件处理方式。在子类中重写processEvent()方法或者processXxxEvent()方法,
再调用enableEvents((long eventsToEnable))方法。

一个完整的菜单系统由菜单条(MenuBar),菜单(Menu),菜单项(MenuItem)组成。CheckboxMenuItem是具有复选功能的菜单项。
Menu类有一个方法addSeparator()可以在菜单项上添加分隔条。
调用Frame类中的setMenuBar(MenuBar mb)方法即可将MenuBar放在一个Frame窗口中。

ActionEvent类中的方法getActionCommand()可获得与此动作相关的命令字符串;
对于能够触发出ActionEvent事件的组件都能够调用setActionCommand(String command)来设置由此菜单项引发的动作事件的命令名。
默认情况下,动作命令设置为菜单项的标题文本。但是在国际化应用中,不同的语言版本会显示不同的标题文本,
所以此时只能通过setActionCommand(String command)来设置命令名。


Window类是可自由拼驳的自由窗口,没有标题栏,一般很少用。他的常用子类有Frame(带有标题栏的窗口)和Dialog(对话框)。


模态对话框和非模态对话框:模态对话框显示的时候,用户不能操作同一个程序中的其他窗口。
Dialog类用于产生对话框。方法getOwner()返回此对话框的拥有者对象,
注意:返回的是Window类对象,此时需要用返回的这个对象调用里面的方法的话,记得进行强制转换。
如:((MyFrame)getOwner()).getInfo();(假如你所继承Frame类里面有getInfo()这个成员方法。)
方法isModal()返回此对话框是否为模态对话框。
对话框本身也是一个容器类,可以添加如TextField,Button之类的组件。
记住,一般关闭对话框用dispose()方法来释放窗口资源。

FileDialog是Dialog的子类,在打开文件或者存储文件的时候可以用FileDialog类。


Checkbox用来创建单选按钮和多选按钮(复选框)。单选按钮必须为它指定一个组CheckboxGroup。
单选按钮和多选按钮的语义事件是ItemEvent,对应的监听器接口为ItemListener。

Choice用来制作单选下拉列表框。语义事件是ItemEvent,对应的监听器接口为ItemListener。

Panel类是一个容器类,用于产生一种特殊的空白面板,可以容纳其他组件,但不能独立存在。通常用于集合若干个组件,方便布局。

ScrollPanel类也是一个容器类,用于产生滚动窗口,通过滚动条在一个较小的容器窗口中显示较大的子部件。也不能独立存在。
ScrollPanel类本身没有布局管理器,他只能容纳一个组件。如果要添加多个组件,可以先将多个组件添加到Panel容器中先,然后添加Panel容器。

setLayout()方法用来设置容器的布局管理器。

BorderLayout布局管理器。是Window类默认的布局管理器,BorderLayout分为“东南西北中”五个区域。
FlowLayout布局管理器。 是Panel类默认的布局管理器,FlowLayout是流式布局。
GridLayout布局管理器。 将容器分为行列来布局。
GridBagLayout布局管理器。 功能比较强大,使用也比较复杂。一般较少使用。在swing中有更简单的替代办法。
CardLayout布局管理器。 使用这种布局的时候,添加组件要为组件指定一个标识字符串。如:add(new Button("hello"), "one");
BoxLayout布局管理器。 是在Swing中新增加的一种布局管理器,它允许多个组件全部垂直摆放或水平摆放。

setLayout(null)可取消布局管理器,然后使用setBounds()来设置组件的大小和绝对位置。


所有的Swing组件都位于javax.swing包中,他们是构筑在awt上层的GUI组件,Swing中所有的组件都是JComponent的子类,JComponent又是java.awt.Container的子类。
为了保证可移植性,Swing完全用java语言编写。

JFC(java Foundation Class)是指Sun对早期的JDK进行扩展的部分,集合了Swing组件和其他能简化开发的API类,
包括Swing,java 2D,accessibility,internationalization。

JFrame上面只能有一个唯一的组件,这个组件为JRootPane,调用JFrame.getContentPane()方法可获得JFrame中内置的JRootPane对象。

应用程序不能直接在JFrame实例对象上增加组件和设置布局管理器,而应该在JRootPane对象上增加子组件和设置布局管理器。

调用JFrame的setDefaultCloseOperation(int)方法,可以设置单击窗口上的关闭按钮时的处理方式,例如:当设置值为JFrame.EXIT_ON_CLOSE时,
单击JFrame窗口上的关闭按钮,将直接关闭JFrame框架窗口并结束程序运行。默认的行为只是简单地隐藏JFrame。

最基本的JScrollPane由水平和垂直方向上的JScrollBar以及一个JViewport组成。JViewport代表滚动窗口中的视图区域,JScrollBar代表滚动条。
调用JScrollPane.getViewport()方法,可以获得代表滚动窗口中的视图区域的JViewport对象。
调用JScrollPane.setViewport(JViewport viewport)方法,可以将滚动窗口中要显示的内容作为子组件增加到JViewport上。

JOptionPane类提供了若干个showXxxDialog()静态方法,可以用来产生简单的标准对话框。

JFileChooser类为用户选择文件提供了一种简单的机制,专门用来实现文件存取对话框。

在GUI程序开发中,应尽量使用Swing组件。

要快速掌握新组件的用法,最好找一个例子程序看看,并查看API文档。JDK的demo程序目录中有一些例子程序,java指南中也有。


javascript和Applet可以互相访问。例子程序如下:

//Test.java
package com.heima;

import java.applet.*;
import java.awt.*;
import java.awt.event.*;

public class Test extends Applet {

Image oImg;
public String text;

public void init() {

setBackground(new Color(200, 200, 200));

Dimension size = getSize();
oImg = createImage(size.width, size.height);

text = getParameter("param1");
addMouseListener(new MouseAdapter() {
int orgX;
int orgY;
@Override
public void mousePressed(MouseEvent e) {
orgX = e.getX();
orgY = e.getY();
}
@Override
public void mouseReleased(MouseEvent e) {
Graphics oG = oImg.getGraphics();
oG.setColor(Color.RED);
oG.drawLine(orgX, orgY, e.getX(), e.getY());
repaint();
}
});
}

public void paint(Graphics g) {
if(oImg != null) {
g.drawImage(oImg, 0, 0, this);
}
g.drawString(text, 100, 100);
}
}

<!--这里是html代码-->
<applet code="com.heima.Test" name="app" width="100%" height="700">
<param name="param1" value="哈哈哈,我是网页传递过来的参数。" />
</applet>
<script>
var setText = function() {
document.app.text = content.value;
document.app.repaint();
}
</script>
<input type="text" name="content" />
<input type="button" value="改变文本" οnclick="setText()" />

W3C标准建议用<object></object>标记来代替<applet></applet>
我们可以用JDK自带的HtmlConverter命令来转换。
使用<object>标记,执行Applet小程序的java解释器被作为一个ActiveX插件引用。

转换后的代码如下:
<object
classid = "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"
codebase = "http://java.sun.com/update/1.6.0/jinstall-6u18-windows-i586.cab#Version=6,0,0,7"
WIDTH = "100%" HEIGHT = "700" NAME = "app" >
<PARAM NAME = CODE VALUE = "com.heima.Test" >
<PARAM NAME = NAME VALUE = "app" >
<param name = "type" value = "application/x-java-applet;version=1.6">
<param name = "scriptable" value = "false">
<PARAM NAME = "param1" VALUE="哈哈哈,我是网页传递过来的参数。" />

<comment>
<embed
type = "application/x-java-applet;version=1.6" \
CODE = "com.heima.Test" \
NAME = "app" \
WIDTH = "100%" \
HEIGHT = "700" \
param1 ="?" /
scriptable = false
pluginspage = "http://java.sun.com/products/plugin/index.html#download">
<noembed>
</noembed>
</embed>
</comment>
</object>

网络编程:
网络协议与TCP/IP,TCP/IP是目前网络应用程序的首选协议。
我们现在所学的网络编程也是基于TCP/IP协议的。
编写基于基于TCP/IP协议的网络程序,必须先了解两个重要的概念:IP地址和Port(端口号)
IP地址占用4个字节,Port是一个占用两个字节的整数(范围在0~65535之间,0~1023之间的端口数是用于一些知名的网络服务和应用)。
本地回路IP地址是127.0.0.1
在TCP/IP协议栈中有两个高级协议是网络应用程序编写应该了解的协议。分别是UDP与TCP
TCP:传输控制协议(Transmission Control Protocol),是面向连接的通信协议。在正式通信之前必须先与对方建立连接。可靠性较好。
UDP:用户数据报协议(User Datagram Protocol),是无连接的通信协议。在正式通信之前不必与对方先建立连接,直接通过数据报传输数据。可靠性较差。

TCP,UDP的数据帧格式简单图例: 协议类型--源IP--目标IP--源端口--目标端口--帧序号--帧数据

Socket是网络驱动层提供给应用程序编程的接口和一种机制。

Socket在应用程序中创建,通过一种绑定机制与驱动程序建立关系,告诉自己所对应的IP和Port。

Socket数据发送过程:(图例)
应用程序-(1)----(产生Socket)----|
| | |
(2) (应用程序将要 |
| 发送的数据传送 |
| 给Socket) |
| | |
| |----(3)------------- Socket
| |
(调用bind将Socket |
的信息通知给驱动 (4)
程序,其中包括 |
IP和Port) (驱动程序从Socket取出数据
| 并通过网卡发送出去,就是打包成
| 上面所说的数据帧格式,逐帧的发送。)
| |
驱动程序------------------------|


Socket数据接收过程:(图例)
应用程序-(1)----(产生Socket)----|
| | |
(2) (应用程序从 |
| Socket中取出数据) |
| | |
| | |
| |----(4)------------- Socket
| |
(调用bind将Socket |
的信息通知给驱动 (3)
程序) |
| (驱动程序根据从网卡传送过来的
| 数据报中的指定目标端口号,将
| 处理后的数据传送到相应的Socket中)
| |
驱动程序------------------------|


java中的网络编程类都位于java.net包中。

DatagramSocket类用于UDP通信。
ServerSocket类用于TCP通信的服务器端。
Socket类用于TCP通信的服务器和客户端。


DatagramSocket类有三个主要构造函数:
DatagramSocket():如果UDP程序不用事先接收对方发送的数据,而是主动先给对方发送数据,那最好就用这个构造函数来创建对象。
系统会自动将其绑定到本地主机上任何可用的端口。
DatagramSocket(int port):如果UDP程序有可能先接收别人发送的数据,那就必须得用这个构造函数来创建对象。
DatagramSocket(int port, InetAddress laddr):如果UDP程序要在有多个IP地址的计算机上运行,那么就得用这个构造函数来创建对象。

DatagramSocket类中的方法close()关闭此数据报套接字并通知驱动程序释放与此对象所关联的资源。
DatagramSocket类中的方法send(DatagramPacket p)从此套接字发送UDP数据报包。
DatagramSocket类中的方法receive(DatagramPacket p)从此套接字接收UDP数据报包。

DatagramPacket类相当于一个集装箱,有两个主要构造函数:
DatagramPacket(byte[] buf, int length):创建用来接收数据的DatagramPacket对象。
DatagramPacket(byte[] buf, int length, InetAddress address, int port):创建用来发送数据的DatagramPacket对象。其中buf就是要发送的数据的字节数组。

DatagramPacket类中的方法getAddress()返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。
DatagramPacket类中的方法getPort()返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。
DatagramPacket类中的方法getData()返回数据缓冲区。
DatagramPacket类中的方法getLength()返回将要发送或接收到的数据的长度。


InetAddress是用于表示计算机IP地址的一个类。
InetAddress类中的静态方法getByName(String host)根据字符串格式的计算机地址来返回一个相应的InetAddress对象。
InetAddress类中的方法getHostAddress()返回 IP 地址字符串(以文本表现形式)。


UDP接收程序必须先启动运行,才能接受UDP发送程序发送的数据。

看一个最简单的UDP程序:

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPSend {

public static void main(String[] args) throws Exception {

DatagramSocket ds = new DatagramSocket();
String message = "hello 黑马 www.csdn.net";
ds.send(new DatagramPacket(message.getBytes(), message.getBytes().length,
InetAddress.getByName("127.0.0.1"), 3000));
ds.close();
}
}


import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UDPReceive {

public static void main(String[] args) throws Exception {

DatagramSocket ds = new DatagramSocket(3000);
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, 1024);
ds.receive(dp);
System.out.println(new String(dp.getData(), 0, dp.getLength()));
System.out.println("address: " + dp.getAddress().getHostAddress()
+ " port: " + dp.getPort());
ds.close();
}
}


如何发送广播数据?当我们把数据发送到广播地址(假如是:192.168.0.255)的时候,就表示把数据发送到(承上所假如:192.168.0.*)下的所有用户。
不过只有UDP程序才能够发送和接收广播地址的数据。

对于IP地址为192.168.0.123这台计算机所在的广播地址不一定就是192.168.0.255。这需要结合计算机的子网掩码来计算,假如你的子网掩码是255.255.254.0,
那么这台计算机所对应的广播地址应该是:192.168.1.255。

私有IP通过网关代理上网的原理:
假如有一个使用私有IP地址的内部网络,这个内部网络中的计算机不能与internet网络上的计算机按照正常的路由方式进行通信,
因为内部网络上的计算机不具有合法有效的internet的IP地址,也没有连接到internet上的路由设备,在内部网络中有一台计算机除了能跟内部网络的连接外,
还具有与internet网络的连接,具有合法有效的IP地址。如果这台计算机打开了IP网络数据转发功能,或者是安装了具有相应功能的软件,
那么这个内部网络中的其他计算机只要将他们的网关地址设置为与internet网络具有连接的那台计算机的内部网络的IP地址。
就可以借助这台计算机访问internet网络上的计算机了。那么具有与internet网络具有连接的那台计算机就称为这个内部网络的网关。
当内部网络的计算机要访问不属于这个内部网络的IP地址的时候,计算机中的TCP/IP协议模块就会将数据发送到网关上,然后再由网关发送出去,
所以内部网络中的计算机无法将数据直接发送到内部网络以外的网络设备上。


我们分析一下内部网络中的使用私有IP的计算机是如何使用网关来访问internet网络上的计算机的。
假如内部网络中有一台计算机IP为192.168.0.3,需要访问internet网络上IP为221.101.121.57计算机,
那么计算机所发送的数据报包的形式应该是:(如下图)
----------------------------------------------------------------
| 192.168.0.3 | 221.101.121.57 | 3000 | 3000 | hello |
----------------------------------------------------------------
(源IP地址) (目标IP地址) (源端口) (目标端口) (数据)
由于源IP和目标IP不属于同一个网络,那么源IP所在的计算机的TCP/IP协议模块就会将这个数据报包发送给网关所绑定的网络设备上,
网关从数据报包中的目标IP地址上就可以知道这个数据需要经过这里转发的,
网关就用(由于网关有一个在internet网络上具有合法有效的IP地址,假如是:166.111.111.10)这个IP地址所对应的网络设备向internet网络发送出去。
发送数据的时候,数据源是以166.111.111.10的身份来发送的。在向外发送数据之前,网关要修改数据报包中的源IP地址和源端口号,
也就是将源数据报包中的源IP地址修改为166.111.111.10, 将3000修改为1027(假如),然后通过166.111.111.10所对应的网络设备将修改后的数据报包发送出去。

(私有IP通过网关代理上网的原理详细请看java基础\10.传智播客JAVA高级视频05_网络编程\传智播客JAVA高级视频05_网络编程\JAVA高级04_04视频)


TCP客户端程序与TCP服务器端程序的交互过程:
1,服务器程序创建一个ServerSocket,然后调用accept()方法等待客户端来连接;
2,客户端程序创建一个Socket并请求与服务器建立连接;
3,服务器接收客户端的连接请求,并创建一个新的Socket与该客户端建立专线连接;
4,建立了连接的两个Socket在一个单独的线程(由服务器程序创建)上对话;
5,服务器开始等待新的连接请求,当新的连接请求到达时,重复步骤2到步骤5的过程。

当我们为需要指定端口的对象指定端口的时候一定要注意不能使用已经被别的程序占用了的端口,否则程序将不能正常的运行。


我们每次编写完服务器程序以后,都没有必要马上编写一个客户端程序来对它进行测试,Windows自带的telnet程序就是一个简单实用的TCP客户端程序。
我们可以直接使用telnet程序来测试服务器。只要在运行telnet程序的时候指定所要连接服务器程序的IP地址和端口号。telnet程序就会与这个服务器
建立连接,连接建立以后,在telnet程序窗口中就会显示出服务器端发送过来的数据。而在telnet窗口中用键盘输入的内容都会被发送到服务器端。

连接形式:telnet IP号 端口号

一般情况下我们在telnet窗口中输入的字符不会显示出来,这时我们需要打开telnet程序的本地回显功能,方法如下:
先直接运行telnet命令,进入telnet的命令窗口中,然后输入help,我们就可以查看telnet内部可使用的命令,我们可以看到有一个命令是set,
然后我们输入set ? 看能设什么值,分别又代表什么,我们可以看到LOCAL_ECHO就是打开本地的回显功能。
这时我们就可以输入set LOCAL_ECHO 这样就打开了telnet的本地回显功能,此时用quit退出telnet命令窗口,再执行 telnet IP号 端口号
这时我们在telnet窗口中输入的字符就可以显示出来了,当我们按回车键的时候就会把内容发送到所连接的服务器上。


使用netstat命令查看当前正在被使用的TCP端口号。netstat -help可以查看使用帮助。

我们最好通过一个配置参数来指定TCP服务程序所使用的端口号。

我们还可以在TCP网络连接上传递对象。
ObjectInputStream和ObjectOutputStream可以从底层输入流中读取对象类型的数据和将对象类型的数据写入到底层输出流。
使用ObjectInputStream和ObjectOutputStream来包装底层网络字节流,TCP服务器和TCP客户端之间就可以传递对象类型的数据。


其实很多应用程序协议都是基于TCP网络通信协议的,比如HTTP协议,FTP协议,STMP协议,POP3协议等。


访问internet网络资源主要的类有java.net包中的URL,URLDecoder,URLEncoder,URLConnection,HttoURLConnection等类。

URI(Uniform Resource Identifier)表示统一资源标识符。

URL(Uniform Resource Locator)表示统一资源定位符,用于表示internet网络资源的地址。网络资源可以是一个文件,一个目录,或者是一个复杂的对象。
URL的基本组成:协议、主机名、端口号、资源名。如:http://www.it315.org:8080/index.html

在Java中,URI类不包含用于访问通用资源标识符设定的任何方法,它的唯一作用是进行分析。相反,URL类则可以打开到达资源的一个字符串。

HTTP协议定义了web浏览器和web服务器之间交换数据的会话过程以及数据本身的格式。
我们可以简单的认为HTTP协议规定了浏览器从3W服务器上获取网页文件的方式。

URL中的端口号部分用来指定客户端程序要连接的网络服务器程序的监听端口号,每一种标准的网络协议都有一个默认的端口号,
HTTP协议的默认端口号就是80,如果某种协议的服务器程序使用的监听端口号是默认端口号,那么在连接网络服务器的时候URL中就可以不指定端口号,
客户端程序就会使用协议的默认端口号去连接网络服务器。


一个完整的URL可以由一个基准URL和一个相对URL组合而成。
基准URL就是当前网页所在的目录路径;
相对URL的表示:
/a.html-------以/开头表示主机上的某种协议的根目录。
../a.html-------以../开头表示当前资源所在目录的父目录。
a.html或者./a.html-------直接使用文件名、目录名或者以./开头则表示当前资源目录下的文件或者子目录。

浏览器访问3W服务器上的网页文件时,需要使用HTTP协议进行通信,在HTTP协议中,浏览器不能向服务器直接传送某些特殊的字符,必须对这些字符进行URL编码后再传送。
URL编码规则:
将空格转换为加号(+);
对0~9,a~z,A~Z之间的字符保持不变;
对于所有其他的字符,用这个字符的当前字符集编码在内存中的十六进制格式表示,并在每个字节前加上一个百分号(%)。如:字符“+”用%2B表示,字符“=”用%3D表示,
字符“&”用%26表示,每个中文字符在内存中占两个字节,字符“中”用%D6%D0表示,字符“国”用%B9%FA表示。
对于空格也可以直接使用其十六进制的编码方式,而不是将它转换成加号(+)。

java.net包中提供了URLEncoder和URLDecoder这两个类,来实现URL编码和解码。


HTTP消息分为请求消息和响应消息。

HTTP请求消息:一个完整的请求消息包括:一个请求行、若干个消息头、以及实体内容。
例如:
GET /articles/news/today.asp HTTP/1.1
Accept: */*
Accept-Language: zh-cn
Connection: Keep-Alive
Host: localhost
Referer: http://localhost/links.asp
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; Trident/4.0; SLCC2; .NET CLR 2.0.50727;
.NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C; MALC; TheWorld)
Accept-Encoding: gzip, deflate

(请求消息中的第一行就是请求行,后面紧跟的内容(直到一个空行跟实体内容分隔)都属于消息头部分,消息头和实体内容是可选的,消息头和实体内容要用空行隔开)

HTTP响应消息:一个完整的响应消息包括:一个状态行、若干个消息头、以及实体内容。
例如:
HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Thu, 13 Jul 2000 05:50:53 GMT
Content-Length: 2291
Content-Type: text/html
Set-Cookie: ASPSESSIONIDQQGGGNCG=LKLDFFKCINFLDMFHCBCBMFLJ;path=/
Cache-control: private

<html>
<head>
......

(响应消息中的第一行就是状态行,后面紧跟的内容(直到一个空行跟实体内容分隔)都属于消息头部分,消息头和实体内容也是可选的,消息头和实体内容要用空行隔开)


HTTP消息中的消息头是用来描述这个HTTP消息中的一些特性,这种用来描述信息的信息被称为元信息。

了解几个HTTP消息头:
Connection: 用于指定处理完本次请求/响应后,客户端与服务器是否继续保持连接,设置值可以为Keep-Alive和close。默认是Keep-Alive。
Accept-Language: 用于指定客户机期望服务器返回的文档所使用的国家语言,可以指定多个以逗号分隔的国家语言。
Content-Length: 用于表示实体内容的长度(字节数)。
Range: (请求消息头)用于指定服务器只需返回文档中的部分内容及内容范围,有以下几种使用格式。
(1)Range: bytes=100-599 表示只返回从第100个字节到第599个字节之间的内容,包括第100个字节和第599个字节,字节的顺序是从0开始计算。
(2)Range: bytes=100- 表示请求服务器只返回第100个字节以后的全部内容。
(3)Range: bytes=-100 表示请求服务器只返回最后100个字节的内容。
这个头字段,对于较大文档的断点续传非常有用,如果客户端在一次的请求中只接收到了服务器返回的部分内容就中断了,
那么它可以在第二次的请求中使用Range头字段要求服务器只返回从上次中断位置以后的内容。这样就节省了网络下载的时间。
Content-Range: (响应消息头)用于指定服务器返回的部分实体内容的位置信息,例如:Content-Range: bytes 2543-4532/7898(起始位置-终止位置/整个实体内容的大小)。

一个使用GET方式的请求消息中不能包含实体内容,只有使用POST等方式的请求消息中才可以包含实体内容。

如果一个HTTP消息中包含了实体内容,那么在它的消息头部分必须要有Content-Length这个字段,用来告诉接收方实体内容的长度。

使用telnet来访问网络资源,体会HTTP协议的会话过程:
使用 telnet www.it315.org 80 连接上web服务器
然后输入 GET /index.html HTTP/1.1
Host:

然后我们可以继续访问此服务器上的资源,如:
GET /images/logo.gif HTTP/1.1
Host:
Connection: close ------表示访问完后断开连接。


URL类,URLConnection类,HttpURLConnection类。

工厂设计模式(XxxFactory-----getXxx())

一个获取网络资源的例子:

package com.heima.exam;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class GetWeb {
public static void getContent(String urlString, String language) throws Exception {
URL url = new URL(urlString);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestProperty("Accept-Language", language);
Map<String, List<String>> requests = connection.getRequestProperties();
Set<String> reqFields = requests.keySet();
for(Iterator itereq = reqFields.iterator(); itereq.hasNext(); ) {
String field = (String) itereq.next();
System.out.println(field + ":" + connection.getRequestProperty(field));
}
/*
* 这个方法我们可以不调用,因为我们下面调用了getXxxxx()方法
* 的时候就会自动跟服务器建立连接。
* */
//connection.connect();
Map<String, List<String>> responses = connection.getHeaderFields();
Set<String> resFields = responses.keySet();
for(Iterator iteres = resFields.iterator(); iteres.hasNext(); ) {
String field = (String) iteres.next();
System.out.println(field + ":" + connection.getHeaderField(field));
}
BufferedReader bfr = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String strLine = null;
while((strLine = bfr.readLine()) != null) {
System.out.println(strLine);
}
bfr.close();
connection.disconnect();
}

/**
* @param args
*/
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub

getContent("http://www.baidu.com", "zh-HK");
getContent("http://www.baidu.com", "ar-BH");
}

}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值