嘿,亲!知识可是无价之宝呢,但咱这精心整理的资料也耗费了不少心血呀。小小地破费一下,绝对物超所值哦!如有下载和支付问题,请联系我们QQ(微信同号):813200300
本次赞助数额为: 2 元微信扫码支付:2 元
请留下您的邮箱,我们将在2小时内将文件发到您的邮箱
一.TCP/IP网络参考模型的运作过程:
TCP/IP网络参考模型分为四个层:
1.应用层(数据源)
应用层对应于OSI参考模型的高层,为用户提供所需要的各种服务,例如:FTP、Telnet、DNS、SMTP等.具体端口的应用程序,所要发送数据的来源。在应用层有不同的数据传输协议:
1)FTP(File Transfer Protocol) 文件传输协议
2)HTTP (Hyper Text Transfer Protocol)超文本传输协议
3)SMTP (Simple Mail Transfer Protocol)简单邮件传输协议
4)POTP (Post Office Transfer Protocol)邮局协议
应用层还包括 DNS(Domain Name System)域名系统:
在域名访问的时候都是将域名转为所对应的IP地址进行访问的,在每个系统本地都会有一个域名体统文件,里面记录着网站域名和所对应的IP,如果在本地没有所访问的的域名,体统就会去网络上的DNS服务器去找,这个DNS服务器记录着网上各个网站域名和对应的IP,
2.传输层
传输层是将应用层的数据从该应用的端口传输到另外一个地址端口的应用程序。传输层是提供端到端的传输连接,传输层的作用是为上层协议提供端到端的可靠和透明的数据传输服务,有两种传输协议:
1)UDP(User Datagram Protocol)用户数据包(报)协议
2)TCP(Transfer Control Protocol) 传输控制协议
3.网际层
在这个庞大的网络体系中,两台主机要通讯就必须知道对方的地址,在网络上这个地址叫IP(Internet Protocol)互连协议地址。而在这个网络体系中,两台主机通讯,可以通过n条路来进行数据传输,而路径的选择就是路由器的工作了。所以在网际层主要就是给连接到网络的主机编配IP地址和根据路径选择算法(路由)选择路径,这里的IP地址就是所说的网外地址,
1)IP (Internet Protocol) 网络协议
2)ARP (Address Resolution Protocol) 地址解析协议
3)ICMP (Internet Control Message Protocol) 因特网控制消息协议
4)HDLC (High Data Link Control)高级数据链路控制
4.网络接口层 (数据链路层、物理层)
数据链路层:
主机和路由器为节点,连接相邻节点的通道为链路,数据链路层负责吧数据从一个节点通过链路传到另一个节点。传输的数据协议单元为帧。数据帧中包含物理地址(又称MAC地址)、控制码、数据及校验码等信息,该层的主要作用是通过校验、确认和反馈重发等手段,将不可靠的物理链路转换成对网络层来说无差错的数据链路。
链路层功能主要在网卡上实现,
PPP(Point To Point Protocol)点到点协议
物理层:
物理层的媒体包括架空明线、平衡电缆、光纤、无线信道等。通信用的互连设备指DTE和DCE间的互连设备。DTE(Data Terminal Equipment)既数据终端设备,又称物理设备,如计算机、终端等都包括在内。而DCE(Data Communication Equipment)则是数据通信设备或电路连接设备,如调制解调器等。数据传输通常是经过DTE──DCE,再经过DCE──DTE的路径。互连设备指将DTE、DCE连接起来的装置,如各种插头、插座。LAN中的各种粗、细同轴电缆、T型接头、插头,接收器,发送器,中继器等都属物理层的媒体和连接器。
物理层建立在传输介质基础上,利用物理传输介质为数据链路层提供物理连接。它的主要功能是在物理介质上传输二进制数据比特流
二、Socket(插座,套接字,插口)编程
Java提供了UDP和TCP的传输接口
UDP:
这种传输协议是面向无连接的,就是说无论另一端在不在,都可以发送,发送的时候如果找不到指定的IP主机和指定接收数据的端口,数据就会丢失,就算以后在线也接收不到了。因为数据已经丢失。这样的传输方式是不可靠的,唯一的优点就是快点。
DatagramSocket() 数据报包套接字
此类是用来发送或接受数据包报的套接字,这种传输方式没有客户端和服务端之分,DatagramSocket()这个套接字既可以发数据也可以收数据,只要在发送或接收的时候传递相对应的数据包就可以。下面一个简单的聊天例子:
public class TestDatagramSocket{ public static void main(String[] args) throws IOException { //为了能同时向多个用户发送数据和同时接收多个用户发来的数据,必须使用多线程 Thread socketSend=new Thread(new SocketSend()); Thread socketReceive=new Thread(new SocketReceive()); socketSend.start(); socketReceive.start(); } } class SocketSend implements Runnable{ @Override public void run() { BufferedReader in=null; DatagramSocket socket=null; try { //创建绑定本地端口和IP地址的数据报包套接字 socket = new DatagramSocket(10002, Inet4Address.getLocalHost()); //键盘录入,也可以用Scanner扫描仪,用buffer貌似有点大材小用 // in=new BufferedReader(new InputStreamReader(System.in)); // String str=in.readLine(); Scanner scanner=new Scanner(System.in); System.out.println("请输入对方的ip地址"); /*ip的主机好可以是2到255,0是所在子网的ip,1是路由器的ip,255是广播ip,所以只有2到255可用,如果用255则可以向所有在线的主机发信息*/ String ip=scanner.nextLine(); System.out.println("请输入对方通讯的端口号"); int port=scanner.nextInt(); System.out.println("好了可以开始聊天了"); //为了能不停的向这个用户不停的发数据,创建一个死循环 while(true){ String str=scanner.nextLine(); System.out.println("ip:" InetAddress.getLocalHost() "端口:" 10002 ":自己"); System.out.println(str); //将字符编码成字节数组 byte[] bt=str.getBytes(); //创建发送时要用的数据报包,传入数据的byte数组bt,发送b.length个字节的数据,并指定发送的目标IP地址和端口 DatagramPacket p = new DatagramPacket(bt, bt.length,InetAddress.getByName(ip), port); socket.send(p);//调用socket的send方法发送,并传入要发送的数据报包 if(str.equals("886")){ break; } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }finally{ try { //关闭并释放资源,一般在finally关闭比较安全,无论有没有异常发生都会关闭 socket.close(); in.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } class SocketReceive implements Runnable{ @Override public void run() { try{ //创建绑定本地端口和IP地址的数据报包套接字 DatagramSocket socket=new DatagramSocket(10003,InetAddress.getLocalHost());; byte[] buf=null; DatagramPacket p=null; //为了不停的收数据,创建一个死循环 while(true){ buf=new byte[1024]; //创建接收数据的报包,接收数据的报包只需要传入一个接收数据的byte数组,和指定接收数据的字节长度 p=new DatagramPacket(buf, 1024); //接收数据 socket.receive(p); System.out.println("ip:" p.getAddress() "端口:" p.getPort() ":"); System.out.println(new String(buf)); //因为要不停的接收,所以不关闭socket // socket.close(); } }catch(IOException e){ e.printStackTrace(); } } }
TCP:
TCP是面向连接的,必须服务端在线然后客户端连接服务端经过三次握手协议成功后才能开始通讯,是必须双方发生连接才能通讯的,这种是即发即收的传输方式。不会有数据丢失的现象,安全可靠,就是比UDP稍慢。下面一个数据上传的例子:
Socket( )客户端:
是客服端套接字,创建的时候可以指定连接服务端的IP地址和端口号,也可以创建一个不带参数的套接字,然后用comment方法连接并指定连接服务端的IP和端口号,而且可以指定超时时间,即如果在指定时间内还没有连接到服务端的话就报超时异常。如果不指定超时,底层好像是默认为0的。
public class FileUploadSocket {
public static void main(String[] args){
upload();
}
//建立链接
private static void upload(){
//键盘录入IP
Scanner scanner=new Scanner(System.in);
String IP=null;
while(IP==null||IP.equals("")){
System.out.println("请输入服务器IP(退出请输入“-1”)");
IP=scanner.nextLine();
if(IP.equals("-1")){
stopThread();
}
}
System.out.println("请输入服务端端口号:");
//键盘录入端口
int port=getport();
try {
//创建一个套接字并链接到指定IP和端口的服务端套接字
// Socket socket=new Socket(IP, port);
//创建一个没有链接的套接字
Socket socket=new Socket();
//将这个没有链接的套接字链接到指定的IP和端口的服务端套接字,也可以设置超时时间,
socket.connect(new InetSocketAddress(IP, port), 10000);
//调用上传方法
fileUpload(socket);
} catch (Exception e) {
//如果发生的异常时套接字链接超时则提醒超时
if(e instanceof SocketTimeoutException){
System.err.println("链接超时,可能是找不到该IP地址或端口哦!请重新输入:");
}else{
System.err.println("链接失败!无效的IP地址或端口!请重新输入:");
}
// e.printStackTrace();
//如果发生异常则递归
upload();
}
}
//键盘录入端口方法
private static Integer getport() {
Scanner scanner=new Scanner(System.in);
int port=0;
try{
port=scanner.nextInt();
if(port==-1){
stopThread();
}
if(port<0||port>65535){
System.out.println("端口号为0·65535之间的数字");
return getport();
}
}catch(Exception e){
System.out.println("端口号为0·65535之间的数字");
e.printStackTrace();
return getport();
}
return port;
}
private static void stopThread(){
System.out.println("退出");
//停止当前线程,一个过时的方法,但是还是被我发现并拿来用了
Thread.currentThread().stop();
}
//上传方法
public static void fileUpload(Socket socket){
BufferedOutputStream bufOut=null;
try {
System.out.println("请输入文件路径:(路径分隔应用“/”或“\\”)");
//键盘录入文件路径
File file = getFileByInput();
//获取当前系统时间的毫秒数
long star=System.currentTimeMillis();
System.out.println("正在上传。。。");
//建立要上传文件的流
BufferedInputStream bufIn=new BufferedInputStream(new FileInputStream(file));
//获取套接字的输出流,在已经链接到服务端的基础上,将数据写到服务端
bufOut=new BufferedOutputStream(socket.getOutputStream());
byte[] bt=new byte[1024];
int len;
//写出文件名,在路径名后面加换行,在服务端所读的第一行就是文件名
bufOut.write((file.getName() "\n").getBytes());
// bufOut.flush();
while((len=bufIn.read(bt))!=-1){
//写出文件的数据,读到几个字节就写几个字节,否则如果读到最后没有把数组bt填满,
//在服务端最后读到的数据将会是这个数组剩余的0,因为数组初始化是1024个字节,
//没有数据的字节默认值为0
bufOut.write(bt,0,len);
}
long end=System.currentTimeMillis();
System.out.println("上传成功");
float time=(float)(end-star);
System.out.println("用时:" time "毫秒," (time)/1000 "秒," (time/60000) "分钟,约" (Math.round(time/60000)) "分钟");
// if(time<60000){
// System.out.println("用时:" (time)/1000 "秒");
// }else {
// System.out.println("用时:" (time/60000) "分钟");
// System.out.println("用时约:" (Math.round(time/60000)) "分钟");
// }
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
if(bufOut!=null){
bufOut.close();
}
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//键盘录入获取File对象的方法
public static File getFileByInput() throws IOException {
Scanner scanner=new Scanner(System.in);
String filePath=scanner.next();
// BufferedReader bufRd=new BufferedReader(new InputStreamReader(System.in));
// String filePath=bufRd.readLine();
if(filePath==null||filePath.equals("")){
//递归,调用方法自身
getFileByInput();
}
File file=new File(filePath);
if(!file.exists()||!file.canRead()){
System.out.println("路径不存在,请输入正确的文件路径:");
file = getFileByInput();
}
if(!file.isFile()){
System.out.println("此路径所指的不是一个文件,请输入正确的文件路径:");
file = getFileByInput();
}
// bufRd.close();
return file;
}
}
ServerSocket() 服务端:
用来接收客服端的连接的服务端套接字,必须调用套接字的accept方法接受客户端连接,如果没有客户端连接过来就或处在阻塞状态,直到有客户端连接过来。
public class FileUploadServerSocket {
public static void main(String[] args) {
openServerSocket();
}
public static void openServerSocket() {
try {
System.out.println("请输入端口号:");
//键盘录入端口号
int port=getPort();
//创建一个指定本地端口号的套接字
ServerSocket server=new ServerSocket(port);
System.out.println("服务器已开启!");
//为了能同时接收多个用户上传的文件,或同一个用户同时上传多个文件
//为每个用户每次过来都创建一个线程,
while(true){
System.out.println("等待连接。。。");
//接收用户连接,accept()方法放回的是连接过来的用户的socket对象
Socket socket=server.accept();//如果没有连接就处于阻塞状态
System.out.println(socket.getInetAddress().getHostAddress() "连接过来了,端口是:" socket.getPort());
Thread thread=new Thread(new FileReceive(socket));
thread.start();
}
} catch (IOException e) {
//如果是绑定异常(BindException)则提醒端口已被占用
if(e instanceof BindException){
System.err.println("该端口已被占用");
openServerSocket();
}else{
System.err.println("服务器启动异常。。。");
}
}
}
//键盘录入端口号方法
public static int getPort(){
Scanner scanner=new Scanner(System.in);
int port=0;
try {
/*next跟nextLine的区别是:
next是以空格为标示的,遇到空格就读,遇到换行也读
nextLine是以换行为标示的,遇到换行就读*/
String a=scanner.nextLine();
port=Integer.parseInt(a.trim());
if(port<0||port>65535){
System.err.println("请输入正确的端口号:(端口号范围为0·65535的数字)");
}
} catch (Exception e) {
System.err.println("请输入正确端口号:(端口号范围为0·65535的数字)");
return getPort();
}
//因为System.in这个流是静态的,如果关闭了下一次就用不了键盘录入这个流了
// scanner.close();
return port;
}
}
//文件接收线程类
class FileReceive implements Runnable{
private Socket socket;
//用构造方法实例socket
public FileReceive(Socket socket){
this.socket=socket;
}
@Override
public void run() {
fileReceive();
}
public void fileReceive() {
BufferedReader bufRd=null;
BufferedInputStream bufIn=null;
BufferedOutputStream bufOut=null;
try {
//获取链接用户的socket
InputStream socketIn=socket.getInputStream();
byte[] bt=new byte[1024];
int len;
File file=null;
//文件存放路径
String uploadPath="E:/toolooSoftware/eclipse/project/test/src/uploadFile/";
//只有字符流才能读一行,所以用字符转换流
//如果客户端没有写数据过来,流就会处于阻塞状态。
bufRd=new BufferedReader(new InputStreamReader(socketIn));
//读出文件名,第一行为文件名
String fileName=bufRd.readLine();
System.out.println(fileName "...上传中。。。");
//创建文件对象
file=new File(uploadPath fileName);
//如果文件不存在就创建
if(!file.exists()){
file.createNewFile();
}
// while((len=bufIn.read(bt))!=-1){
// System.out.println(Arrays.toString(bt));
// System.out.println("文件名:" new String(bt,0,len));
// file=new File(uploadPath new String(bt,0,len));
// if(!file.exists()){
// file.createNewFile();
// }
// }
bufIn=new BufferedInputStream(socketIn);
bufOut=new BufferedOutputStream(new FileOutputStream(file));
//读取流中剩下的内容,除第一行的文件名外剩下的就是这个文件的内容
//同一个流对象中的内容是可以分开读的,读了的内容就像取出来一样这些内容就不在流中了
while((len=bufIn.read(bt))!=-1){
//读到几个字节就写几个字节,以免会把数组中剩余的0以写进去
bufOut.write(bt,0,len);
}
bufOut.flush();
System.out.println("上传成功!");
} catch (IOException e) {
// e.printStackTrace();
if(e instanceof SocketException){
System.out.println("上传异常,可能是客户端断开连接了!!!");
}
}finally{
//如果在try里面的某一步发生异常,可能有些流还没有实例化,关闭就会发生空指针异常
try{
if(bufRd!=null){
bufRd.close();
}
if(bufIn!=null){
bufIn.close();
}
if(bufOut!=null){
bufOut.close();
}
socket.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
UDP和TCP的区别
UDP:面向无连接 不可靠 快
TCP:面向连接 可靠 慢