八、Java网络编程技术
1、网络通信基础知识
网络通信指网络中的计算机通过网络互相传递信息。
通信协议是网络通信的基础,通信协议是网络中计算机之间进行通信时共同遵守的规定
常用的通信协议:HTTP、FTP、TCP/IP等
常用的网络通信模型:客户/服务器(Client/Server,c/s,胖客户模型)结构;浏览器/服务器(Browser/Server,B/S,瘦客户)结构。
IP地址:网络中任意一台计算机地址的唯一标识。
域名地址:可看做是IP地址的助记名。
端口号:端口是一台主机上标识多个进程的一种手段,主机IP和端口组合能唯一确定网络通信的主体——进程。0—65535,其中0—1024是系统使用或保留端口,1024—65535则为用户使用端口。
网络编程中有两个主要的问题:如何准确地定位网络上一台或多台主机;找到主机后如何可靠高效地进行数据传输。
三种常用的Java网络程序设计:URL编程(支持多种协议,获取远端计算机上的资源信息),TCP编程(使用套接字Socket机制,建立稳定连接,使用流传输数据,C/S构架的主要方式),UDP编程(无连接的快速通信技术,传递数据大且非关键性的数据)。
2、URL编程技术
URL对象通常包含最基本的三个部分信息:协议(常用HTTP、FTP)、地址、资源。
URL:Uniform Resource Locator,是网络资源的指针。
URL格式:传输协议名://主机名:端口号/文件名#引用,其中端口号、文件名和引用是可选的,传输协议名和主机名是必须的。
URL类、URLConnection类:URLConnection类用于应用程序与URL之间的连接。该类对象可以读写URL对象所表示的Internet上的数据。一般使用方法如下:
① 通过在URL上调用openConnection方法创建连接对象;
② 处理设置参数和一般请求属性;
③ 使用connect方法连接到远程对象的实际连接;
④ 远程对象变为可用,远程对象的头字段和内容变为可访问。
TestURL.java(两个实例,分别为URL和URL综合实例)
package blog8;
import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
/**
* URL类和URLConnection类的使用
* 利用URL下载网络资源:以百度首页logo为例
*/
public class TestURL {
public static void main(String[] args) throws IOException {
//TestURL.URLTest();
TestURL.URLDownload();
}
public static void URLTest() throws IOException{
//通过URL类获取远端主机上指定的文件内容
//1、创建URL对象
URL url = new URL("http://www.baidu.com/index.html");
//2、创建InputStream对象
InputStreamReader isr = new InputStreamReader(url.openStream());
System.out.println("协议:" + url.getProtocol());
System.out.println("主机:" + url.getHost());
System.out.println("端口:" + url.getPort());
System.out.println("路径:" + url.getPath());
System.out.println("文件:" + url.getFile());
BufferedReader br = new BufferedReader(isr);
String inputLine;
System.out.println("文件内容:");
while( (inputLine = br.readLine()) != null ){
System.out.println(inputLine);//按行从缓冲输入流循环读字符,直到读完所有行,输出屏幕
}
br.close();
}
//通过URLConnection下载URL指向的资源
public static void URLDownload() throws IOException{
URL url = new URL("http://www.baidu.com/img/bdlogo.gif");
URLConnection urlConnection = url.openConnection();
//建立IO流进行数据传递:下载
InputStream in = urlConnection.getInputStream();
OutputStream out = new FileOutputStream("baiduLogo.gif");
//数据传递
byte [] buffer = new byte[1024];
int len;
while( (len = in.read(buffer)) != -1){
out.write(buffer,0,len);
}
//关闭资源,百度logo已保存到本地
out.flush();
in.close();
out.close();
}
}
3、TCP和UDP协议:两种协议的特点,一个类InetAddress
TCP与UDP的比较
① 使用UDP(User Datagram Protocol)协议时,每个数据包中都给出完整的地址信息,因此无需建立发送方和接收方的连接,而使用TCP(Transfer Control Protocol)时,是一个面向连接的协议,所以在socket之间进行数据传输前需要建立连接。
② UDP传输数据有大小限制,每个传输包必须在64KB之内,而TCP没有限制。
③ UDP是一个不可靠的协议,发送的数据包不一定以相同顺序到达接收方,有可能丢失。而TCP是一个可靠的协议,确保接收方完全正确获取发送方发送的全部数据,包括顺序。
InetAddress类:该类用来表示主机地址,常用方法:InetAddress getByAddress(param),该方法参数可以ip的byte数组,可以是提供的主机名的String字符串加ip地址byte数组,返回一个InetAddress对象。
TestInetAddress.java(InetAddress类的使用实例)
package blog8;
import java.io.IOException;
import java.net.InetAddress;
public class TestInetAddress {
public static void main(String[] args) throws IOException {
// 1、获取给定域名的地址,该类没有public构造器,只能通过静态获取
InetAddress inetAddress = InetAddress.getByName("www.baidu.com");
System.out.println(inetAddress.getHostName()); //显示主机名
System.out.println(inetAddress.getHostAddress());//显示IP地址
System.out.println(inetAddress);//显示地址的字符串描述
/*
www.baidu.com
119.75.217.56
www.baidu.com/119.75.217.56
*/
// 2、获取本机地址
InetAddress inetAddress1 = InetAddress.getLocalHost();
System.out.println(inetAddress1.getHostName()); //显示主机名
System.out.println(inetAddress1.getHostAddress());//显示IP地址
System.out.println(inetAddress1);//显示地址的字符串描述
/*
2014-0325-1004
192.168.1.102
2014-0325-1004/192.168.1.102
*/
//获取给定IP的主机地址(202.108.22.5百度)
byte [] ip = new byte[]{(byte)202,(byte)108,(byte)22,(byte)5};
InetAddress inetAddress3 = InetAddress.getByAddress(ip);
InetAddress inetAddress4 = InetAddress.getByAddress("百度",ip);
System.out.println(inetAddress3);//: /202.108.22.5,主机名为给定,为空
System.out.println(inetAddress4);//: 百度/202.108.22.5,主机名为百度
}
}
4、TCP编程技术
Socket:网络通信两端都有Socket,网络通信也就是socket之间的通信,数据在两个socket之间通过IO传输。一个套接字有IP地址和端口号唯一确定。常分为客户机套接字:socket;服务器端套接字:ServerSocket。
TestTCPBySocket.java(基于socket的Tcp编程,c/s模型;一个完整的聊天室实例)
package blog8;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import org.junit.Test;
/**
* 基于socket的Tcp编程,c/s模型;一个服务器与客户端的通信实例
* 注:此处有待提高,对c/s的模型还是不够清晰,需要一个完整的c/s多人聊天室实例学习!!!!!!
* 注:关闭资源都是先关闭客户端,再关闭服务器端
*/
// Tcp编程,网络套接字(socket)通信的一般步骤:
// 1.连接到远程主机;
// 2.绑定到端口;
// 3.接收从远程来的连接请求;
// 4.监听到达的数据;
// 5.发送数据;
// 6.接收数据;
// 7.关闭连接。
public class TestTCPBySocket {
@Test//服务器端的建立
public void createServerSocket() throws IOException{
//1.建立服务器端
ServerSocket serverSocket = new ServerSocket(8080);
//2.服务器端获取一个socket作为通信用,通过接受连接返回,接受客户端的连接请求,返回一个socket,这是客户端的socket
Socket socket = serverSocket.accept();
//3.建立Socket的输出流,进行通信,已发送一句话为例:向客户端的socket发送数据用输出流
OutputStream out = socket.getOutputStream();
PrintWriter printWriter = new PrintWriter(out);
printWriter.println("来自服务端的问候...");
//4.关闭资源,由内到外逐步关闭
printWriter.close();
out.close();
socket.close();
serverSocket.close();
}
@Test//客户端的建立
public void createClintSocket() throws IOException{
//1.获取主机的地址,本例用本机,所以127.0.0.1
InetAddress host = InetAddress.getByName("127.0.0.1");
//2.建立服务器的socket套接字,参数为,主机address和连接的端口,这里获取的Socket是客户端的
Socket socket = new Socket(host,8080);
//3.获取服务器socket的IO流,进行通信,这里是读取客户端的socket的数据流,服务器已向客户端Socket输出数据了
InputStream in = socket.getInputStream();
//先转换.过程:inputstream -> inputstreamReader -> bufferedReader
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
System.out.println(reader.readLine()); //选用BufferedReader对象的行读方式读取
//4.关闭资源
reader.close();
socket.close();
}
}
TestChart.java(服务器与客户端双向通信实例)
package blog8;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import org.junit.Test;
/**
* 服务器客户端双向通信实例
*/
public class TestChart {
@Test//1、服务器端
public void MyServer() throws IOException{
ServerSocket server = new ServerSocket(2008);
Socket socket = server.accept();
String ip = socket.getInetAddress().getHostAddress();
System.out.println(ip + "...connected..");
InputStream in = socket.getInputStream();
byte [] buffer = new byte[1024];
int len;
System.out.println("来自客户端的信息:");
while( (len=in.read(buffer)) != -1 ){
System.out.println(new String(buffer,0,len));
}
OutputStream out = socket.getOutputStream();
out.write("hello,这里是服务器...".getBytes());
socket.close();
server.close(); //注意先关闭客户端资源,子关闭服务器端资源
}
@Test//2、客户端
public void MyClient() throws IOException{
Socket socket = new Socket("localhost",2008);//create a connection
OutputStream out = socket.getOutputStream();
out.write("这里是客户A,我正在连接服务器...".getBytes());
socket.shutdownOutput(); // 注意,关闭标签!!!!
InputStream in = socket.getInputStream();
System.out.println("这是服务器给我的回信:");
byte [] buffer = new byte[1024];
int len;
while( (len = in.read(buffer)) != -1 ){
System.out.println(new String(buffer,0,len));
}
socket.close();
}
}
TestUpload.java(Tcp编程应用:文件上传功能)
package blog8;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class TestUpload {
public static void main(String[] args) throws IOException {
@SuppressWarnings("resource")
ServerSocket server = new ServerSocket(8888);
while(true){ //服务器永远执行、等待
Socket socket = server.accept(); //服务器接收到客户连接请求
new Thread(new MyServer(socket)).start();
}
}
}
class MyClient{
public void upload() throws IOException{
Socket socket = new Socket("localhost",8888);
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();
//out be used to upload files
BufferedReader read = new BufferedReader(new FileReader(".\\src\\blog7\\青春.txt"));
PrintWriter writer = new PrintWriter(out,true);
String line;
while( (line=read.readLine()) != null ){
writer.write(line);
}
read.close();
socket.shutdownOutput();
//in be used to receive information from Server
BufferedReader bin = new BufferedReader(new InputStreamReader(in));
String str;
str = bin.readLine();
System.out.println(str);//打印服务器回执的信息
socket.close();
}
}
class MyServer implements Runnable{ //并不是真的服务器,而是通过传递进来的Socket进行的服务器的业务处理类
private Socket socket;
public MyServer(Socket s) {
super();
socket = s;
}
@Override
public void run() {
String ip = socket.getInetAddress().getHostAddress();
System.out.println(ip + "...connected..");
int count = 0; // used to check file whether is repeated.
try {
BufferedReader read = new BufferedReader(new InputStreamReader(socket.getInputStream()));
File file = new File("FileFromClient" + count + ".txt");
while(file.exists()){
file = new File("FileFromClient" + (++count) + ".txt");
}
PrintWriter write = new PrintWriter(new FileWriter(file));
String line;
while( (line=read.readLine()) != null ){
System.out.println(line);
}
//upload completed,send a massage
BufferedWriter send = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
send.write("上传成功啦...");
send.flush();
write.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
5、UDP编程技术
DatagramPacket类:将数据打包,即创建一个对象,称为数据包。
DatagramSocket类:用来发送和接收数据包的套接字,负责将打包好的数据包发送到目的地,或从目的地接收数据包。
例如:发送“你好”两字到主机“www.baidu.com”,端口2016,步骤如下
byte[] buffer = “你好”.getByte();
InetAddress destAdd = InetAddress.getByName(“www.baidu.com”);
//打包IP地址、端口、数据、长度,成一个数据包对象
DatagramPacket dataPacket = new DatagramPacket(buff,buff.length,destAdd,2016);
DatagramSocket sendSocket = new DatagramSocket();
sendSocket.send(dataPacket);//用套接字发送已打包好的数据对象。
又例如,接收外界发送到本机2016号端口的数据包,步骤如下:
byte[] buff = new byte[8192];
//将这个缓冲区绑定给数据包
DatagramPacket recPacket = new DatagramPacket(buff,buff.length);
//本机IP,指定端口2016建立Socket
DatagramSocket recSocket = new DatagramSocket(2016);
//通过套接字接收到一个数据包放到recPacket空包缓冲中
recSocket.receive(recPacket);
//获取接收到的数据包的长度
Int length = recPacket.getLength();
//获取接收到的数据包的内容getData
String msg = new String(recPacket.getData(),0,length);
System.out.println(msg);
TestUDP.java(UDP聊天程序实例)
package blog8;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/**
* UDP服务器与客户机双向通信实例
*/
public class TestUDP {
public static void main(String[] args) throws SocketException {
DatagramSocket send = new DatagramSocket();
DatagramSocket rece = new DatagramSocket(6666);
new Thread(new Send(send)).start();
new Thread(new Rec(rece)).start();
}
}
//客户机:发送端
class Send implements Runnable{
private DatagramSocket ds;
public Send(DatagramSocket ds) {
super();
this.ds = ds;
}
@Override
public void run() {
BufferedReader read = new BufferedReader(new InputStreamReader(System.in));
String line;
try {
while( (line=read.readLine()) != null ){
byte [] buffer = line.getBytes();
//打包数据
DatagramPacket dp = new DatagramPacket(buffer,buffer.length,
InetAddress.getByName("localhost"),6666);
ds.send(dp);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
//服务器,接收端
class Rec implements Runnable{
private DatagramSocket socket;
public Rec(DatagramSocket socket) {
super();
this.socket = socket;
}
@Override
public void run() {
while(true){
byte [] buffer = new byte[1024];
DatagramPacket dp = new DatagramPacket(buffer,0,buffer.length);//空包用来存放接收的数据包
try {
socket.receive(dp);
} catch (IOException e) {
e.printStackTrace();
}
String ip = dp.getAddress().getHostAddress();
String data = new String( dp.getData(),0,dp.getLength());
System.out.println(ip + " " + data );
}
}
}