---------------------- ASP.Net+Android+IO开发S、.Net培训、期待与您交流! ----------------------
1、 概述
网络通讯的要素:
IP地址:互联网上的每一台计算机都有一个唯一标示自己的标记,这个标记就是IP地址。网络通讯就是通过互联网上的IP地址进行通信。
端口:其实就是一个计算机为应用程序标记的标识号码,便于网络通信寻找对应的网络程序进行通信。它是一个逻辑端口,有效端口:0 ~ 65535,系统使用或保留的端口是:0 ~ 1024.。
传输协议:通信的规则,只有通信两端遵守这个通信规则方能通信,这里我们学习传输协议UDP和TCP。
通信的步骤:
(1)找到IP地址
(2)数据要发送到对象指定应用程序,为标识这些应用程序,所以给这些网络应用程序都用数字标识,为方便称呼这个数字,叫做端口,即逻辑端口。
(3)定义通信规则,称之为协议。国际组织定义了通用协议,即TCP/IP。
2、 网络模型
描述了网络的整个传输的层次,从应用层一层一层的封包,通过最底层的链路层进行物理线路的电信号传输,达到目的地后,再从目标的底层链路层,一层层解包,将数据提交给应用层。如下为OSI的参考模型及TCP/IP的参考模型。
3、 InetAddress
IP地址使用32位长度的二进制数据表示,为便于记忆,IP都是以十进制数据表示形式,如192.168.1.3。
在浏览网站时,为进一步记忆众多网站地址,用含意义的字符串替代十进制表示的IP地址,如www.baidu.com,他们都与IP地址存在映射关系。
在java中由IneAddress类来操作IP的相关功能。
常用方法:
byte[]getAddress() //返回此InetAddress 对象的原始 IP 地址
static InetAddress getByName(String host)//在给定主机名的情况下确定主机的 IP 地址
String getHostAddress() //返回 IP 地址字符串(以文本表现形式)。
static InetAddress getLocalHost() //返回本地主机。
4、 UDP传输协议
一种面向无连接的传输协议,即不需要知道发送端与接收端是否连接,发送端该发数据时就发,数据以数据报的形式传输。数据可能丢失,只求速度,多用于通讯信息不重要但要求实时性的聊天程序和视频会议等。
特点:
(1) 面向无连接,不需建立连接就发送。
(2) 因不确定连接,是不可靠的协议。
(3) 也因不需确定连接,通讯速度快。
(4) 每个数据报的打下限制在64k内。
UDP传输在Java中靠DatagramSocket与DatagramPacket类支持:
DatagramSocket(数据报套接字):
用来发送和接收数据报的套接字。在发送端创建DatagramSocket对象时可不指定端口,它用send方法发送数据报DatagramPacket对象,在接收端创建DatagramSocket时需指定端口,它用receive接收数据报对象。
DatagramPacket(数据报包):
数据报包用来实现无连接包投递服务。每条报文根据该包中包含的信息从一台机器由路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递做出保证。在构建时需传入byte数组,作为数据包,或者指定长度和ip地址。可由getAddress().getHostAddress()获取对方IP地址。
下面看一个简单的聊天程序,考虑到一端可以进行收发数据,采用多线程技术:
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;
/*
多线程实现聊天程序,交替发送端和接收端的端口号能进行同台机子测试
需开两个程序,开一个,交替端口号再开一个。
即10002---》10001,10001-----》10002在端口间测试
*/
public class UDP_Chat
{
public static void main(String[] args)
{
try
{
//如果想在同一台机子测试,请将发送和接收端口设置成不同端口号,即10001--->发送---->10002
//先执行已个程序,而后交换下面代码的端口号,再执行一个,就可实现同一机子测试
int sendPort = 10001;
int receivePort = 10002;
//建立发送端和接收端
DatagramSocket sendSocket = new DatagramSocket();
DatagramSocket receiveSocket =new DatagramSocket(receivePort);
//分别开线程
new Thread(new Send(sendSocket,sendPort)).start();
new Thread(new Receive(receiveSocket)).start();
} catch (SocketException e)
{
throw new RuntimeException("Socket创建失败!");
}
}
}
//发送端
class Send implements Runnable
{
private DatagramSocket ds;
private int sendPort=10001;
public Send(DatagramSocket ds, int sendPort)
{
this.sendPort = sendPort;
this.ds = ds;
}
public void run()
{
try
{
//获取键盘录入
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
String strLine = null;
while((strLine = bf.readLine())!=null)
{
//读到over停止聊天
if("over".equals(strLine))
break;
byte[] bys = strLine.getBytes();
//数据报对象,构造需传入byte数组+ip+端口号
DatagramPacket dp = new DatagramPacket(bys, bys.length, InetAddress.getByName("127.0.0.1"),sendPort);
//发送数据
ds.send(dp);
}
ds.close();
}catch(IOException e)
{
throw new RuntimeException("数据发送失败!");
}
}
}
//接收端
class Receive implements Runnable
{
private DatagramSocket ds = null;
public Receive(DatagramSocket ds)
{
this.ds = ds;
}
public void run()
{
//无限接收外来数据
while(true)
{
//准备一个byte数组接收数据
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys, bys.length);
try
{
//接收
ds.receive(dp);
//打印到控制台
System.out.println(new String(dp.getData(), 0, dp.getLength()));
} catch (IOException e)
{
throw new RuntimeException("数据接收失败!");
}
}
}
}
5、 TCP传输协议
面向连接的网络传输协议,分服务端和客户端,想要发送数据,两者需相互确认连接成功,方可发送数据。这就是需要经过所谓的”三次握手”,这样保证了数据的可靠性,多用于文件的传输,下载等数据要求严格的应用。
特点:
(1) 面向连接,需经“三次握手”进行连接确认。
(2) 是可靠的协议,只在连接成功的情况下发送数据,不易丢失数据。
(3) 由于需建立连接,所以速度稍慢。
(4) 这里通过客户端的IO流操作数据。
Java的TCP协议由ServerSocket与Socket类提供支持,两者是相当于服务端与客户端,分别封装了服务端和客户端的基本操作。
在传输数据量较大的文件,文件的结束标志并不能被传输,它只是告诉了本类的读取流,一旦读取完毕即读到文件结束标志,将结束发送,服务端却无法确认文件是否读完,所以客户端读完后,需调用shutdownOutput();告知服务端结束传输。
ServerSocket:
服务端的套接字,在TCP传输中该端的程序需先启动,它将获取端口号,监听向该端口发送连接请求的客户端套接字对象。通过accept()返回客户端的对象。getInetAddress().getHostAddress()的方式获取客户端的IP。在整个传输中只扮演着控制者或说服务者。
Socket:
客户端的封装类,它是两台机器的通信端,扮演着通信者,构建时传入服务器端的IP地址和通信端口号。由getOutputStream()和getInputStream()返回字节流的对象,转成字节流来进行数据的操作。而后利用字节流的read和write方法对byte数据进行传输。
下面看一个TCP传输协议的例子:
服务端:在这种面向连接的传输协议中,要求服务端必须先开启
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TCPServer
{
public static void main(String[] args) throws IOException
{
ServerSocket ss = new ServerSocket(10001);
//连接客户端对象
Socket s = ss.accept();
//接收客户端数据
InputStream in = s.getInputStream();
byte[] bys = new byte[1024];
//读入数组中
int len = in.read(bys);
System.out.println(new String(bys, 0,len));
//回馈数据给客户端
OutputStream out = s.getOutputStream();
out.write("已收到".getBytes());
//关闭资源
s.close();
ss.close();
}
}
客户端:服务端存在的情况下,开启后向服务端发送连接请求,连接成功后可发送数据。
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class TCPClient
{
public static void main(String[] args) throws UnknownHostException, IOException
{
//创建套接字,输入主机ip和端口号
Socket s = new Socket("127.0.0.1", 10001);
//发送数据流
OutputStream out = s.getOutputStream();
out.write("客户端发送的数据".getBytes());
//接收服务端的回馈信息
InputStream in = s.getInputStream();
byte[] bys = new byte[1024];
int len = in.read(bys);
System.out.println(new String(bys, 0,len));
s.close();
}
}
6、 并发的网络编程
并发,即同时为多个客户端服务,由于客户端的随机性,不可能由服务器单线操作,这里就要考虑能并发执行的多线程了。服务端将执行代码写进线程的run函数中,而后循环监听发送连接请求的客户端,用accept方法连接客户端,并为每一个客户端建立线程,而后交给线程自己去处理,主线程只负责监听连接。
下面看一个并发上传图片的例子,注意如果读取是利用InputStreamReader转换成字符流读入BufferedReader缓冲区中,一定要记得换行和刷新,这里才用的是直接的字节流:
//服务端:需先运行
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
class ServerThread implements Runnable
{
private Socket s = null;
private static int count = 1;
public ServerThread(Socket s)
{
this.s = s;
}
public void run()
{
FileOutputStream fis =null;
try
{
//获取客户端输入流
InputStream in =s.getInputStream();
String ip =s.getInetAddress().getHostAddress();//获取地址
fis = new FileOutputStream(ip+(count++)+".jpg");
byte[] bys= new byte[1024];
int len = 0;
//读取数据
while((len= in.read(bys))!=-1)
{
//写入文件中
fis.write(bys, 0, len);
}
//回馈客户端信息
OutputStream out=s.getOutputStream();
out.write("上传成功!".getBytes());
s.close();
} catch (IOException e)
{
throw new RuntimeException("上传失败");
}
finally
{
try
{
if(fis != null)
fis.close();
} catch (IOException e)
{
throw newRuntimeException("写入流关闭失败!");
}
}
}
}
public class UploadPicDemo
{
public static void main(String[] args)
{
ServerSocket ss=null;
Socket s = null;
try
{
ss = new ServerSocket(10001);
//循环连接多个客户端
while(true)
{
//没连接一个交给线程处理
s = ss.accept();
new Thread(new ServerThread(s)).start();
}
} catch (IOException e)
{
throw new RuntimeException("服务端创建失败!");
}
}
}
//客户端:读入本地图片,上传,可有多个
import java.io.*;
import java.net.Socket;
public class UploadTcpClient
{
public static void main(String[] args)
{
Socket s = null;
FileInputStream fis = null;
try
{
//建立客户端套接字,输入主机ip,端口号
s = new Socket("127.0.0.1", 10001);
//获取要读取文件
fis = new FileInputStream("1.jpg");
OutputStream out =s.getOutputStream();//客户端数据发送对象
byte[] bys = new byte[1024];
int len = 0;
//读取并发送
while((len = fis.read(bys))!=-1)
{
out.write(bys, 0, len);
}
s.shutdownOutput();//告诉服务端发送完毕
//接收反馈信息
InputStream in =s.getInputStream();
byte[] by = new byte[1024];
int n = in.read(by);
System.out.println(new String(by,0, n));
s.close();
} catch (IOException e)
{
throw new RuntimeException("文件读取失败!");
}
finally
{
try
{
if(fis != null)
fis.close();
}catch(IOException e)
{
throw new RuntimeException("文件流关闭失败!");
}
}
}
}
7、 URL-URLConnection
URL代表一个统一资源定位符,是指向互联网”资源”的指针。在这里他更适合对网站地址进行拆分,从网站地址中获取指定的部分,如获取协议,主机号,端口号等。
String getProtocol() // 获取协议
String getHost() // 获取主机名
int getPort() // 获取端口号
String getFile() // 获取URL文件名
String getPath() //获取此URL的路径部分
String getQuery() // 获取此URL的查询部,客户端传输的特定信息
URLConnection:它代表应用程序和 URL 之间的通信链接。此类的实例可用于读取和写入此 URL 引用的资源。通常,创建一个到URL 的连接需要几个步骤:
通过在 URL 上调用 openConnection 方法创建连接对象。
处理设置参数和一般请求属性。
使用 connect 方法建立到远程对象的实际连接。
远程对象变为可用。远程对象的头字段和内容变为可访问。
如:
URL url =new URL(urlPath);//传入一连接服务器的URL地址 //获取连接 URLConnectionconn = url.openConnection(); //获取输入流,读取获得的数据,并显示在文本框中 InputStreamin = conn.getInputStream(); byte[]buf = new byte[1024]; intlen=in.read(); Stringstr = new String(buf,0,len));//获取URL地址的资源信息
8、 域名解析
当获取到一个网站地址时,由于是字串表意思的网址,则需要经过域名解析,解析成IP地址,这要通过DNS服务器(域名解析服务器),再根据IP地址访问该IP地址的服务器。解析过程如下:
比如http://www.sina.com.cn/myweb/demo.html,先启动相对应的http传输协议,而后去公网的DNS服务器需找www.sina.com.cn的IP地址映射,再将IP返回,根据IP地址去访问sina的主机服务器。
DNS服务器一般会默认配置,优先配置和访问最近的DNS服务器。
---------------------- ASP.Net+Android+IO开发S、.Net培训、期待与您交流! ----------------------