网络编程
-------- android培训、java培训、期待与您交流! ---------
一.概述
1.定义
计算机网络,是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。而网络编程则是通过使用套接字来达到进程间通信目的的编程。
网络编程最主要的工作就是在发送端把信息通过规定好的协议进行组装包,在接收端按照规定好的协议把包进行解析,从而提取出对应的信息,达到通信的目的。而中间最主要的步骤就是数据包的组装、数据包的过滤、数据包的捕获以及数据包的分析……
2.网络编程三要素
① IP地址:
IP地址是网络中计算机的唯一标识,其特点是由32个二进制位组成,即4个字节。每两个字节之间用(.)来分割。其 组成是:网络号段和主机号段。网络号段是用于识别主机所在的网络/网段,而主机号是用于识别网络中的主机。
注意:
127.0.0.1回环地址(表示本机),即为localhost;X.X.X.255是指广播地址;X.X.X.0是指网络地址。
② 端口:
端口是指正在运行的程序的标识,其有效端口:0-65535,其中0-1024系统使用或保留端口。
③ 协议:
协议是指通信的规则。包括:UDP协议和TCP协议
(1)UDP协议:
① 将数据及源和目的封装成数据包,不需要建立连接
② 每个数据包的大小都限制在64k内
③ 因为是无连接,所以是不可靠协议
④ 因为不需要建立连接,所以传输速率快
(2)TCP协议:
① 首先需要建立连接,形成传输数据的通道
② 在连接中进行大数据量传输
③ 通过三次握手完成连接,是可靠协议
④ 因为必须建立连接,所以传输效率会稍低
3.网络模型
通过以下模型图,进一步了解OSI参考模型和TCP/IP参考模型的结构以及他们的关系:
4.TCP和UDP简单介绍及应用
① UDP协议的详细介绍
UDP是用户数据包协议,是非面向连接的,而且也是不可靠的数据传输协议。当我们使用UDP发送数据的时候,并不知道它在发送过程中是否已经到达安全目的地,所以它存在丢失数据的可能性。但它也是有优点的。它不用预先建立连接,将数据和目的IP地址封装就可以进行数据的传送,所以传输速度快。
② TCP协议的详细介绍
TCP是传输控制协议。是面向连接的,而且是可靠的基于字节流的数据传输协议。当我们使用TCP发送数据的时候,必须建立连接,形成一个传输数据的安全通道。与UDP不同,它通过三次握手完成连接方式,保证数据能传一端发送到另一端,是一种可靠的传输协议。但由于这一过程要建立连接,传输开销比较大,所以传输速度比较慢。
③ UDP和TCP的差别
UDP的特点:
(1)将数据,源和目的封装成包,且包的大小限制在64K以内
(2)通信双方不需要建立连接
(3)数据传输速度较快
(4)数据传输不可靠,传输过程易丢包
TCP协议的特点:
(1)通信双方必须建立连接
(2)在连接中进行大数据量的传输
(3)数据传输可靠
(4)数据传输速率稍慢
④ UDP的使用场景:
如果是对传输的可靠性要求并不高,或者希望传输速度比较快的情况下,建议使用UTP协议。它适用于安全要求并不高,误传影响并不大的即时通信,比如qq聊天(要求传输速度要快,但数据准确性和丢包要求比较低)、在线视频(要求传输速度快,偶尔丢失一些帧数不影响画质也没关系)、网络通话(同样传输速度要快,但音质要求并不高)等。
⑤ TCP的使用场景:
如果是要求对数据的可靠性和完整性较高的话,建议使用TCP协议。它适用于安全要求高的情况下,比如文件传输(比如FTP,HTTP,要求数据的准确性高)、收发邮件(要求信息准确性高,非即时需要)、远程网络管理(要求准确性高,不允许出现误差)等。
二.Java在网络编程中的运用
1.InetAddress类
InetAddress类表示互联网协议的(IP)地址,并将IP地址封装为特定的对象。其相关方法为:
① 创建并实例化InetAddress对象:
InetAddress i = InetAddress.getLocalHost() ;
② 获得本机IP地址:
i.getHostAddress() ;
③ 获得本机名称:
i.getHostName() ;
④ 获取指定域名的IP地址:
InetAddress ia =InetAddress.getByName(“www.baidu.com”) ;
⑤ 获取指定域名的全部IP地址:
InetAddress[] ia = InetAddress.getAllByName(“www.baidu.com”) ;
2.Socket编程
(1)UDP传输
UDP传输使用的主要是:DatagramSocket类(用于创建UDP/Socket)和DatagramPacket类(用于创建数据包)
① 创建DatagramSocket对象:
DatagramSocket ds = new DatagramSocket(8888) ;
// (此为发送时的定义)需要指定端口号
DatagramSocket ds = new DatagramSocket(10000) ;
// (此为接受时的定义)其端口号的制定需要与数据包传输时的端口号一致
② 创建DatagramPacket对象:
DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.100"),10000) ;
// (此为发送时的定义)需要传入存放数据的字节数组,以及对方主机的主机名和端口号
DatagramPacket dp = new DatagramPacket(buf,buf.length) ;
// (此为接受时的定义)需要传入一个指定的字节数组用于存放数据
③ 使用send()方法将数据进行发送:
ds.send(dp) ; // 通过udp的socket服务将数据包发送出去
④ 使用接收方法将数据存储到数据包中:
ds.receive(dp) ; // 该方法是阻塞式的方法,如果没有结束标志会一直等待数据到来
⑤ 使用UDP协议发送数据的步骤:
☆ 建立udp的socket服务
☆ 将要发送的数据封装到数据包中
☆ 通过udp的socket服务将数据包发送出去
☆ 关闭socket服务
⑥ 使用UDP协议接收数据的步骤:
☆ 建立udp socket服务。因为是要接收数据,所以必须要明确一个端口号
☆ 创建数据包,用于存储接收到的数据。方便用数据包对象的方法解析这些数据
☆ 使用socket服务的receive方法将接收的数据存储到数据包中
☆ 通过数据包的方法解析数据包中的数据
☆ 关闭socket服务
通过以下一个模范QQ聊天软件的程序来进一步了解UDP传输协议以及多线程: (注意:注释中包含重要知识点和注意点)
实现发送端:
package cn.itzixue.udp;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/*
* 通过实现Runnable接口来解决线程安全问题
* 将发送数据的操作定义在run()方法中完成对其的覆写,并通过Send类的构造函数传入一个socket服务对象
*/
public class Send implements Runnable {
private DatagramSocket ds;
public Send(DatagramSocket ds) {
this.ds = ds;
}
public void run() {
try {
//对从键盘录入的数据进行高效的封装
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));
String line = null;
//将从键盘录入的信息通过BufferedReader对象特有的readLine()方法进行一行一行的读取
while ((line = bufr.readLine()) != null) {
//将读取到的每一行存入字节数组中
byte[] buf = line.getBytes();
//创建数据包对象,将要发送的数据封装到数据包中,并指定接收方主机和端口
DatagramPacket dp = new DatagramPacket(buf, buf.length,
InetAddress.getByName("192.168.1.255"), 10000);
//通过udp的socket服务(使用send()方法)将数据包发送出去。
ds.send(dp);
//定义结束标志
if ("886".equals(line))
break;
}
//关闭Socket资源
ds.close();
} catch (Exception e) {
e.printStackTrace() ;
}
}
}
实现接收端:
package cn.itzixue.udp;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
/*
* 通过实现Runnable接口来解决线程安全问题
* 将接受数据的操作定义在run()方法中完成对其的覆写,并通过Rece类的构造函数传入一个socket服务对象
*/
public class Rece implements Runnable {
private DatagramSocket ds;
public Rece(DatagramSocket ds) {
this.ds = ds;
}
public void run() {
try {
while (true) {
// 创建数据包对象
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
// 使用udp的socket服务的接收方法将数据存储到数据包中。(该方法是阻塞式的)
ds.receive(dp);
// 通过数据包对象的方法,解析其中的数据,比如:地址、端口、数据内容……
String ip = dp.getAddress().getHostAddress();
// 获取IP地址
int port = dp.getPort();
// 获取端口号
String text = new String(dp.getData(), 0, dp.getLength());
// 将接收到的数据写入一个字符串中
System.out.println(ip + "----" + text);
// 检测结束标志
if (text.equals("886")) {
System.out.println(ip + "....退出聊天室");
}
}
} catch (Exception e) {
e.printStackTrace() ;
}
}
}
启动两个线程:
package cn.itzixue.udp;
import java.io.IOException;
import java.net.DatagramSocket;
public class ChatDemo {
public static void main(String[] args) throws IOException {
//创建并实例化对象
DatagramSocket send = new DatagramSocket();
//需要与发送端的数据包对象中传入的端口号一致
DatagramSocket rece = new DatagramSocket(10000);
//启动两个线程
new Thread(new Send(send)).start();
new Thread(new Rece(rece)).start();
}
}
(2)TCP传输
TC
P传输使用的主要是:Socket类(用于创建客户端Socket服务)和ServerSocket类(用于创建服务器端Socket服务)
① 创建Socket对象:
Socket socket = new Socket("192.168.1.100",1000) ;
// 需要指定端口号和主机地址
② 创建ServerSocket对象:
ServerSocket s_s = new ServerSocket(10002) ;
// 需要传入一个与Socket对象所指定的端口号一致的端口号
③ 服务器端获取连接过来的客户端对象:
Socket s = s_s.accept() ;
④ 使用TCP协议创建发送端发送数据的步骤:
☆ 创建TCP客户端socket服务。使用的是Socket对象(建议该对象一创建就明确目的地,即要连接的主机)
☆ 如果连接建立成功,说明数据传输通道已建立。该通道就是socket流 ,是底层建立好的。
☆ 如果想要输入输出流对象,则通过Scoket对象的getOutputStream()和getInputStream()来获取字节流对象
☆ 使用输出流,将数据写出
☆ 接受有服务器端发过来的反馈信息
☆ 关闭Socket资源
⑤ 使用TCP协议创建服务器端接收数据的步骤:
☆ 通过ServerSocket对象,创建服务端socket服务
☆ 服务端必须对外提供一个端口,否则客户端兼顾将会无法连接
☆ 通过客户端对象获取socket流读取客户端发来的数据
通过以下一个上传图片的程序来进一步了解UDP传输协议以及多线程: (注意:注释中包含重要知识点和注意点) 创建客户端对象:
package cn.itzixue.tcp;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
/*
* 步骤:
* ① 通过IO流读取本地文件信息
* ② 通过创建的Socket对象获取输出流,并将读取到的信息写入输出流中
* ③ 将数据发送到服务器端,并接收服务器端发回来的反馈信息,并进行显示
* ④ 关闭流对象
*/
public class UploadPicClient {
public static void main(String[] args) throws UnknownHostException, IOException {
// 创建客户端socket,并指定传输端口
Socket s = new Socket("192.168.1.100",10006);
// 创建FileInputStream对象用于读取客户端要上传的图片文件
FileInputStream fis = new FileInputStream("c:\\0.bmp");
// 获取socket输出流,将读到图片数据发送给服务端。
OutputStream out = s.getOutputStream();
byte[] buf = new byte[1024];
int len = 0;
// 将读取到的数据写入Socket流中
while((len=fis.read(buf))!=-1){
out.write(buf,0,len);
}
// 通知服务端:数据已经发送完毕,让服务端停止对数据的读取,进而关闭流对象
s.shutdownOutput();
// 读取服务端发回的反馈信息
InputStream in = s.getInputStream();
byte[] bufIn = new byte[1024];
// 将反馈信息读取到控制台
int lenIn = in.read(buf);
String text = new String(buf,0,lenIn);
System.out.println(text);
//关闭资源
fis.close();
s.close();
}
}
创建服务器端对象:
package cn.itzixue.tcp;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/*
* 利用accept()方法的阻塞特性,与多线程同时使用实现多客户端向服务器端传送数据
*/
public class UploadPicServer {
public static void main(String[] args) throws IOException {
//创建TCP的socket服务端。
ServerSocket s_s = new ServerSocket(10000);
//利用accept()的阻塞特性与多个客户端进行链接
while(true){
Socket s = s_s.accept();
//通过多线程技术实现多个客户端同时传输数据
new Thread(new UploadTask(s)).start();
}
}
}
使用多线程技术实现多客户端同时传输数据:
package cn.itzixue.tcp;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/*
* 利用多线程技术,实现多客户端向服务器端发送数据
*/
public class UploadTask implements Runnable {
private static final int SIZE = 1024 * 1024 * 2;
private Socket s;
//接受一个客户端对象
public UploadTask(Socket s) {
this.s = s;
}
public void run() {
int count = 0;
// 获取客户端对象的IP地址
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip + ".....connected");
try {
// 读取客户端发来的数据
InputStream in = s.getInputStream();
// 将读取到数据存储到一个文件中。
File dir = new File("c:\\pic");
// 如果目录不存在则进行创建
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dir, ip + ".jpg");
// 如果文件已经存在于服务端,并封装成对象
while (file.exists()) {
file = new File(dir, ip + "(" + (++count) + ").jpg");
}
// 创建文件输出流对象
FileOutputStream fos = new FileOutputStream(file);
byte[] buf = new byte[1024];
int len = 0;
while ((len = in.read(buf)) != -1) {
fos.write(buf, 0, len);
//判断文件体积是否合适
if (file.length() > SIZE) {
System.out.println(ip + "文件体积过大");
fos.close();
s.close();
System.out.println(ip + "...." + file.delete());
return;
}
}
// 获取socket输出流,将上传成功字样反馈给客户端
OutputStream out = s.getOutputStream();
out.write("上传成功".getBytes());
fos.close();
s.close();
} catch (IOException e) {
e.printStackTrace() ;
}
}
}
总结:
① TCP协议是指客户端(Client)首先与服务端(Server)建立连接,形成通道(其实就是IO流),然后,数据就可以在通道之间进行传输,并且单个Server端可以同时与多个Client端建立连接。
② TCP客户端:客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。连接成功,说明客户端与服务端建立了通道,那么通过IO流就可以进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通过getInputStream(),getOutputStream()获取即可。与服务端通讯结束后,关闭Socket。
③ TCP服务端:服务端需要明确它要处理的数据是从哪个端口进入的。当有客户端访问时,要明确是哪个客户端,可通过accept()方法获取已连接的客户端对象,并通过该对象与客户端通过IO流进行数据传输。当该客户端访问结束,关闭该客户端。
三.客户端与服务器端的传输原理
1.常见客户端和服务器
常见客户端:浏览器;常见服务器。
2.客户端与服务器的通信原理
客户端向服务器端发送一系列请求,服务器端响应客户端发出的请求
3.客户端所发送的请求
GET / HTTP/1.1 ---- 请求行,请求方式:GET;请求的资源路径:/;HTTP协议版本:1.1
/myweb/1.html ---- 请求的资源路径 http协议版本
(请求消息头、属性名:属性值)
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash,
application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
// 通知服务器端客户端所支持的文件类型
Accept: */*
Accept-Language: zh-cn,zu;q=0.5 // 通知服务器端客户端所支持的语言
Accept-Encoding: gzip, deflate // 通知服务器端客户端所支持的压缩格式
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.2)
// 判断IE版本
Host: 192.168.1.100:9090 // 所访问的主机地址
Connection: Keep-Alive // 连接的状态,此为持续存活
//空行
请求内容(请求体)……