-------android培训、java培训、期待与您交流! ----------
一、网络模型分为
OSI参考模型 :应用层--表示层--会话层--(UDP及TCP)传输层--(IP地址)网络层--数据链路层--(例如网线、网卡、光纤)物理层。
TCP/IP参考模型:(应用层--表示层--会话层 :将三层变成一层)应用层--传输层--网际层--主机至网络层。
(一):数据的封包和数据的拆包过程:
(1)数据封包:应用层--表示层--会话层--传输层--网络层--数据链路层--物理层。
(2)数据拆包:物理层--数据链路层--网络层--传输层--会话层--表示层--应用层。
二、网络通讯三要素:
(一)IP地址:(InetAddress)网络中设备的标识,每一个IP地址分为四段,每段为一个字节每个字节最大值为255;
本机的回环地址:(ping 127.0.0.1);主机名:localhost
(二)端口号:数据要发送到对方的应用程序上,为了标识这些应用程序,所以给这些不同的网络应用程序进行标识,配置这些端口
号由0--65535,其中0--1024系统使用或保留端口号,这就是逻辑端口。
(三)常见的传输协议:
(1)UDP协议:
1:UDP协议是面向无连接的、不可靠的、无序的、无流量控制的传输层协议,UDP发送的每个数据包是记录型的数据包,所谓
的记录型数据包就是接收进程可以识别接收到的数据包的记录边界;
2:发送进程在发送每个数据包的时候并不等待多个数据包集中在一起以一个较大数据包发送出去,而是立即发送出去,它是
记录型协议,因此要将数据及源和目的封装成数据包中,而不需要建立连接每个数据包的大小限制64k内;并且接收进程每
次通过read或receive获得数据包必定是发送进程所发送的那个数据包不可能数多个数据包,接收进程可以识别到发送进
程所发送的每个数据包的记录边界。
3:因为连接是不可靠的协议的所以容易造成数据丢失、无序的、无流量控制的传输层协议;
4:不需要建立连接,传输速度快;
例如:QQ聊天所用的的协议就应该是有记录边界的,聊天过程中是以“消息”为单位,消息可以看成一个记录,所以QQ聊
天协议采取的是UDP协议而不是TCP协议。
(2)TCP/IP协议是国际组织定义的通用协议:
1:TCP协议是面向连接的、可靠的、有序的、拥有流量控制的传输层协议,它是字节流的协议,无记录边界;
2:建立连接形成传输数据的通道,在连接中进行大数据的传输,因此在发送进程发送每个数据包的时候在内核处理过程中有
可能并不立即发送出去,而是在会将多个数据包集中在一个形成一个较大的数据包来发送,它是字节流的协议,而且接收
进程每次通过read来读取发送进程的数据包并不一定是数据源发送来的数据包;接收进程无法识别每个数据包的记录边界
,所以TCP协议就是字节流的,无记录边界的协议。
例如:打电话所用到的就是TCP协议,因为他要通过三次握手完成连接;
3:什么是三次握手呢:
例如:1次:张三问:李四在吗? 2次:李四回答:在;3:次:张三:你在啊我知道了;
4:必须建立连接,且效率会稍低。
三、Socket的概念:
(一) Socket就是为网络服务提供的一种机制
(二)通信的两端都要有Socket才能实现通信;其实网络通信就是Socket间的通信,来实现数据在两个Socket间通信是通过IO传输的。
四、UDP传输:
UDP使用的对象是DatagramSocket.
(1)DatagramPacket的构造函数:
接收: DatagramPacket(byte[] buf, int length) 构造 DatagramPacket,用来接收长度为 length 的数据包。
发送:DatagramPacket(byte[] buf, int length, InetAddress address, int port) 构造数据报包,用来将长度为 length
的包发送到指定主机上的指定端口号。
(2) DatagramSocket:此类表示用来发送和接收数据报包的套接字。
DatagramSocket:的接收数据的方法:receive(DatagramPacket p) 从此套接字接收数据报包。
DatagramSocket:的发送数据的方法:send(DatagramPacket p) 从此套接字发送数据报包。
(3)有两个IP地址是用于广播的190.168.1.0;和198.168.1.255;就是指一个网络段;
(四)DatagramPacket是用于将数据封装成包来发送出去,
数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一
台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递做出保证。
(五)UDP:协议的示例
(一)创建发送端的步骤:
1:建立UDP服务:DatagramSocket();
2:提供数据,并将数据封装到数据包中;
3:通过Socket服务的发送功能,将数据包发出去;
4:关闭资源;
(二)创建接收端的步骤:
1:定义udpSocket服务;
2:顶一个数据包,因为要存储接收到的字节数据;因为数据包对象中有更多功能可以提取字节数据中的不同数据信息;
3:通过socket服务的receive方法将接收到的数据存入到已定义好的数据包中;
4:通过数据包对象的特有功能,将这些不同的数据提取出来打印到控制台中;
5:关闭资源;
如示例:
public static void main(String[] args) throws IOException {
//通过DatagramSocket对象,建立udp服务。
DatagramSocket dgs=new DatagramSocket();
//创建键盘录入流
BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
String len=null;
while((len=br.readLine())!=null){
if(len.equals("over")){
break;
}
//将字符串转换成字节数组
byte[] by=len.getBytes();
//DatagramPacket(byte[] buf, int length, InetAddress address, int port)
//构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
DatagramPacket dap=new DatagramPacket(by,by.length,InetAddress.getByName("172.168.40.28"),10000);
//通过socket服务,将已有的数据包发送出去,通过send方法
dgs.send(dap);
}
//关闭资源
dgs.close();
}
}
class UdpRece{
public static void main(String[] args) throws IOException {
// TODO Auto-generated method stub
//创建udp socket,建立端点
DatagramSocket dgs=new DatagramSocket(10000);
//使用循环来接收数据
while(true){
//定义数据包,用于存储数据
byte[] by=new byte[1024];
//创建DatagramPacket的对象,并在构造函数中传入数据,用来接收长度为 length 的数据包。
DatagramPacket dgp=new DatagramPacket(by,by.length);
//通过服务的receive方法将收到数据存入到数据包中
dgs.receive(dgp);
//获取Ip地址
String ip=dgp.getAddress().getHostAddress();
//获取接收到的数据
String data=new String(by,0,dgp.getLength());
System.out.println(ip+"...."+data);
}
}
}
五、TCP传输:
(一):TCP传输分为客户端和服务端:
(1)客户端对应的对象是Socket;
Socket对象,在对象建立时,就可以去建立连接指定的主机,因为Tcp是面向连接的所以在建立Socket服务时就要有服务端
存在,并建立连接成功,形成通路后,在该通道进行数据的传输;
其步骤为:
1:创建socket服务,并指定要连接的主机和端口;
构造函数:
Socket(InetAddress address, int port) 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
2:通过socket对象的getOutputStream方法获取OutputStream输出流;
getOutputStream() 返回此套接字的输出流。
getInputStream() 返回此套接字的输入流。
(2)服务端对应的对象是ServerSocket;
此类实现服务器套接字。服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。
服务端步骤为:
1:建立服务端的socket服务:ServerSocket(),并监听一个端口;
构造函数: ServerSocket(int port) 创建绑定到特定端口的服务器套接字。
2:获取连接过来的客户端对象:
通过ServerSocket的accept方法,没有连接就会等,所以这个方法是阻塞式的;
accept() 侦听并接受到此套接字的连接。该方法返回一个socket;
3:客户端如果发过来数据,那么服务端要使用对应的客户端对象,并获取到该客户端对象的读取流来读取发过来的数据;
通过accept方法返回的socket对象来获取流:
getOutputStream() 返回此套接字的输出流。
getInputStream() 返回此套接字的输入流。
4:关闭服务端;(可选操作)
5:shutdownOutput() 禁用此套接字的输出流。判断结束的标记方法;
如示例:
//客户端
public static void main(String[] args) {
// TODO Auto-generated method stub
//获取键盘录入方法1
Scanner in=new Scanner(System.in);
String name=null;//定义客户端的socket对象
Socket s=null;
BufferedReader br=null;
try {
s=new Socket("172.168.40.28",10005);
//获取键盘录入方法2
//br=new BufferedReader(new InputStreamReader(System.in));
//获取字符的读取写入流使用转换流OutputStreamWriter将字节流转换成字符
BufferedWriter bwin=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
//还可以使用PrintWriter pw=new PrintWriter(s.getOutputStream(),true);读取流pw.println(name);
BufferedReader brin=new BufferedReader(new InputStreamReader(s.getInputStream()));
//String len=null;(len=br.readLine())!=null
while(true){
name=in.next();
if(name.equals("over")){
break;
}
bwin.write(name);
/*
* 如果这里没有使用换行符就会出现一个异常就是客户端和服务端莫名的等待:为什么呢?
* 是因为客户端和服务端都有阻塞式的方法,这些方法么没有读到结束标记就会一直等待导致两端都在等待
*/
bwin.newLine();
//这里要刷新将数据从缓冲区中读取写出来
bwin.flush();
//读取流将数据读取出来
String str=brin.readLine();
System.out.println(str);
}
s.close();
} catch (IOException e) {
throw new RuntimeException("数据异常");
}
if(s!=null){
try {
s.close();
} catch (IOException e) {
throw new RuntimeException("Socket关闭异常");
}
}
if(br!=null){
try {
br.close();
} catch (IOException e) {
throw new RuntimeException("键盘写入关闭异常");
}
}
}
//服务端
public static void main(String[] args){
// TODO Auto-generated method stub
ServerSocket ss=null;
Socket s=null;
try {
//创建服务端对象
ss=new ServerSocket(10005);
s=ss.accept();//通过服务端对象的accept方法获取Socket对象
//获取字符读取流和写入流
BufferedReader buou=new BufferedReader(new InputStreamReader(s.getInputStream()));
BufferedWriter bwou=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
String len=null;
//读取客户端的数据
while((len=buou.readLine())!=null){
System.out.println(len);
//将客户端的数据转换成大写在返还
bwou.write(len.toUpperCase());
bwou.newLine();
bwou.flush();
}
} catch (IOException e) {
throw new RuntimeException("数据异常");
}
if(s!=null){
try {
s.close();
} catch (Exception e) {
throw new RuntimeException("Socket关闭异常");
}
}
if(ss!=null){
try {
ss.close();
} catch (Exception e) {
throw new RuntimeException("ServerSocket关闭异常");
}
}
}
示例二:判断用户登陆:
public static void main(String[] args) {
// TODO Auto-generated method stub
//客户端
Socket s=null;
Scanner in=new Scanner(System.in);
String name=null;
try {
s=new Socket("172.168.40.28",10000);//创建客户端socket
//获取客户端的写入流 和读取流
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
for (int i = 0; i <3; i++) {
name=in.next();
if (name==null) {
break;
}
bw.write(name);//将客户端写入的数据发送到服务端并且要换行刷新
bw.newLine();
bw.flush();
String user=br.readLine();//读取服务端发回来的数据
System.out.println(user);
//使用String类的contains方法判断发回来的数据是否有(欢迎)两个字有说明登陆成功
if (user.contains("欢迎")) {
break;
}
}
}catch (UnknownHostException e) {
throw new RuntimeException("Socket连接失败");
}catch(IOException e){
throw new RuntimeException("文件读取失败");
}finally{
if (s!=null) {
try {
s.close();
} catch (IOException e) {
throw new RuntimeException("Socket关闭失败");
}
}
}
show();
}
//服务端方法
public static void show() {
ServerSocket ss=null;
Socket s=null;
try {
ss=new ServerSocket(10000);//创建服务端
while(true){
s=ss.accept();
new Thread(new TcpUser(s)).start();//启动多线程
}
} catch (IOException e) {
throw new RuntimeException("文件读取失败");
}
}
}
//创建多线程类
class TcpUser implements Runnable{
private Socket s;
public TcpUser(Socket s){
this.s=s;
}
public void run() {
//获取要登陆的用户ip
String ip=s.getInetAddress().getHostAddress();
System.out.println("socket"+ip);
BufferedReader br=null;
try {
for (int i = 0; i <3; i++) {
//创建服务端的读取流来读取客户端发来的数据
BufferedReader brs=new BufferedReader(new InputStreamReader(s.getInputStream()));
String user=brs.readLine();
//读取以经注册的用户数据
br=new BufferedReader(new FileReader("D:\\user.txt"));
String name=null;
boolean falg=false;
while((name=br.readLine())!=null){
//循环读取并做判断用户是否存在
if (name.equals(user)) {
falg=true;
break;
}
}
//创建服务端的写入流将用户的登陆状况发送会客户端并且要换行刷新
BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
if (falg) {
System.out.println(user+"已登录");
bw.write(user+"欢迎光临");
bw.newLine();
bw.flush();
break;
}else{
System.out.println(user+"正在尝试登陆!");
bw.write(user+"用户不存在");
bw.newLine();
bw.flush();
System.out.println("用户不存在");
}
}
} catch (IOException e) {
throw new RuntimeException("登陆异常");
}finally{
if (br!=null) {
try {
br.close();
}catch (IOException e) {
throw new RuntimeException("读取流关闭异常");
}
}
if (s!=null) {
try {
s.close();
}catch (IOException e) {
throw new RuntimeException("socket关闭异常");
}
}
}
}
}
六 URL对象概述:
类 URL 代表一个统一资源定位符,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,
例如对数据库或搜索引擎的查询。
getFile() 获取此 URL 的文件名。
getHost() 获取此 URL 的主机名(如果适用)。
getPath() 获取此 URL 的路径部分。
getPort() 获取此 URL 的端口号。
getProtocol() 获取此 URL 的协议名称。
getQuery() 获取此 URL 的查询部分。
openConnection() 返回一个 URLConnection 对象,它表示到 URL 所引用的远程对象的连接。
如示例:
public static void shows() throws MalformedURLException{
URL rul=new URL("http://172.168.30.27:8080/D:/index.html?name=zhangsan&age=20");
System.out.println(rul.getFile());//获取路径及文件名和参数
System.out.println(rul.getPath());//获取路径及文件名
System.out.println(rul.getHost());//获取ip地址
System.out.println(rul.getPort());//获取端口号
System.out.println(rul.getProtocol());//获取协议类型
System.out.println(rul.getQuery());//获取参数
}
七 URLConnection 对象的概述:
(一) 抽象类 URLConnection 是所有类的超类,它代表应用程序和 URL 之间的通信链接。此类的实例可用于读取和写入此 URL 引用的资源。
通常 创建一个到 URL 的连接需要几个步骤:
1: 通过在 URL 上调用 openConnection 方法创建连接对象。
2: 处理设置参数和一般请求属性。
3:使用 connect 方法建立到远程对象的实际连接。
4:远程对象变为可用。远程对象的头字段和内容变为可访问。
使用以下方法修改设置参数:
setAllowUserInteraction(boolean allowuserinteraction) 设置此 URLConnection 的 allowUserInteraction 字段的值。
setDoInput(boolean doinput) 将此 URLConnection 的 doInput 字段的值设置为指定的值。
setDoOutput(boolean dooutput) 将此 URLConnection 的 doOutput 字段的值设置为指定的值。
setIfModifiedSince(long ifmodifiedsince) 将此 URLConnection 的 ifModifiedSince 字段的值设置为指定的值。
setUseCaches(boolean usecaches) 将此 URLConnection 的 useCaches 字段的值设置为指定的值。
(2) 上面每个 set 方法都有一个用于获取参数值或一般请求属性值的对应 get 方法。适用的具体参数和一般请求属性取决于协议。
在建立到远程对象的连接后,以下方法用于访问头字段和内容:
getContent() 获取此 URL 连接的内容。
getHeaderField(int n) 返回第 n 个头字段的值:getHeaderField(String name) 返回指定的头字段的值。
getInputStream() 返回从此打开的连接读取的输入流。
getOutputStream() 返回写入到此连接的输出流。
getContentType() 返回 content-type 头字段的值。
getConnectTimeout() 返回连接超时设置。
八:InetSocketAddress:此类实现 IP 套接字地址(IP 地址 + 端口号)。它还可以是一个对(主机名 + 端口号),在此情况下,将尝试解
析主机名。如果解析失败,则该地址将被视为未解析 地址,但是其在某些情形下仍然可以使用,比如通过代理连接。
九:ServerSocket(int port,int backlog):
利用指定的 backlog 创建服务器套接字并将其绑定到指定的本地端口号。端口号 0 在所有空闲端口上创建套接字。
传入连接指示(对连接的请求)的最大队列长度被设置为 backlog 参数。如果队列满时收到连接指示,则拒绝该连接。
port - 指定的端口;或者为 0,表示使用任何空闲端口。
backlog - 队列的最大长度。
如果存在安全管理器,则首先使用 port 参数作为参数调用其 checkListen 方法,以确保允许该操作。这可能会导致
SecurityException 异常。
-------android培训、java培训、期待与您交流! ----------