OSI七层模型:
1.层物理层:主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。它的主要作用是传输比特流(就是由1、0转化为电流强弱来进行传输,到达目的地后在转化为1、0,也就是我们常说的数模转换与模数转换)。这一层的数据叫做比特。
2.层数据链路层:主要将从物理层接收的数据进行MAC地址(网卡的地址)的封装与解封装。常把这一层的数据叫做帧。在这一层工作的设备是交换机,数据通过交换机来传输。
3.层网络层:主要将从下层接收到的数据进行IP地址(例192.168.0.1)的封装与解封装。在这一层工作的设备是路由器,常把这一层的数据叫做数据包。
4.层传输层:定义了一些传输数据的协议和端口号(WWW端口80等),如:TCP(传输控制协议,传输效率低,可靠性强,用于传输可靠性要求高,数据量大的数据),UDP(用户数据报协议,与TCP特性恰恰相反,用于传输可靠性要求不高,数据量小的数据,如QQ聊天数据就是通过这种方式传输的)。 主要是将从下层接收的数据进行分段和传输,到达目的地址后再进行重组。常常把这一层数据叫做段。
5.会话层:通过传输层(端口号:传输端口与接收端口)建立数据传输的通路。主要在你的系统之间发起会话或者接受会话请求(设备之间需要互相认识可以是IP也可以是MAC或者是主机名)
6.表示层:主要是进行对接收的数据进行解释、加密与解密、压缩与解压缩等(也就是把计算机能够识别的东西转换成人能够能识别的东西(如图片、声音等)。
7.应用层: 主要是一些终端的应用,比如说FTP(各种文件下载),WEB(IE浏览),QQ之类的(可以把它理解成我们在电脑屏幕上可以看到的东西.就是终端应用)。
一、UDP是一种无连接的传输层协议,提供快速不可靠的服务。
发送端
* 创建UDP发送端* 步骤:* 1.建立UDP的Socket服务* 2.将要发送的数据封装到数据包中* 3.通过UDP的socket服务将数据包发送出去* 4.关闭socket服务1 public class UDPSendDemo2 { 2 3 public static void main(String[] args) throws IOException { 4 System.out.println("发送端启动了..."); 5 6 DatagramSocket ds=new DatagramSocket();//建立socket服务 7 BufferedReader br=new BufferedReader(new InputStreamReader(System.in));//读取键盘录入 8 String line=null; 9 while((line=br.readLine())!=null){10 byte[] buf=line.getBytes();11 DatagramPacket dp=new DatagramPacket(buf, buf.length,InetAddress.getByName("wsw"),10100);//封装数据包12 ds.send(dp);//发送数据13 14 if("over".equals(line))//当录入over时,关闭发送端15 break;16 }17 ds.close();//关闭资源18 }19 }
接收端
* 建立UDP接收端
* 步骤:* 1.建立UDP的Socket服务,因为要接收数据,必须明确接收的端口号* 2.创建数据包,用来存储接收到的数据,方便用数据包对象的方法解析这些数据* 3.使用socket服务的receive方法接收这些数据* 4.通过数据包中的方法解析这些数据* 5.关闭资源1 public class UDPReceDemo2 { 2 3 public static void main(String[] args) throws IOException { 4 System.out.println("接收端启动了..."); 5 6 while(true){//不停接收数据 7 DatagramSocket ds=new DatagramSocket(10100); 8 byte[] buf=new byte[1024]; 9 DatagramPacket dp=new DatagramPacket(buf, buf.length);10 ds.receive(dp);//接收数据,是阻塞式方法11 12 //解析数据 源ip地址,源端口号,发送的内容13 String ip=dp.getAddress().getHostAddress();14 int port=dp.getPort();15 String text=new String(buf,0,dp.getLength());16 System.out.println(ip+":"+port+":"+text);17 if(text.equals("over")){18 System.out.println(ip+"...已经离开聊天室");19 }20 ds.close();//关闭资源21 }22 }23 }
聊天室练习:
实现简单的聊天功能
1 /* 2 聊天 3 */ 4 import java.net.*; 5 import java.io.*; 6 class Send implements Runnable 7 { 8 private DatagramSocket s; 9 public Send(DatagramSocket s)10 {11 this.s=s;12 }13 public void run()14 {15 try16 {17 BufferedReader br=18 new BufferedReader(new InputStreamReader(System.in));19 String line=null;20 while((line=br.readLine())!=null)21 {22 if("over".equals(line))23 break;24 byte[] buf=line.getBytes();25 DatagramPacket dp=26 new DatagramPacket(buf,buf.length,InetAddress.getByName("172.29.115.1"),10005);27 s.send(dp);28 29 }30 }31 catch (Exception e)32 {33 throw new RuntimeException("发送端失败!");34 35 }36 }37 }38 class Receive implements Runnable 39 {40 private DatagramSocket s;41 public Receive(DatagramSocket s)42 {43 this.s=s;44 }45 public void run()46 {47 try48 {49 while(true)50 {51 byte[] buf=new byte[1024];52 DatagramPacket dp=new DatagramPacket(buf,buf.length);53 s.receive(dp);54 String ip=dp.getAddress().getHostAddress();55 String data=new String(dp.getData(),0,dp.getLength());56 System.out.println(ip+"::"+data);57 58 }59 }60 catch (Exception e)61 {62 throw new RuntimeException("接受端失败!");63 64 }65 }66 }67 public class ChatDemo68 {69 public static void main(String[] args)throws Exception70 {71 DatagramSocket send=new DatagramSocket();72 DatagramSocket rece=new DatagramSocket(10005);73 74 new Thread(new Send(send)).start();75 new Thread(new Receive(rece)).start();76 }
二、TCP面向有连接的通信:
/** 客户端:* 通过查阅socket对象,发现TCP是面向连接的,所有对象建立时,就连接指定主机* 服务端存在并连接成功,形成通路后,再该通道进行数据传输*/import java.io.*;import java.net.*;class tcpClient{ public static void main(String[] args) throws IOException{ //1.创建socket服务,指定目的主机和端口 Socket s =new Socket("192.168.1.103",10000); //为了发送数据,应获取socket流的输出流 OutputStream out = s.getOutputStream(); //将内容放到流中 out.write("tcp 我是客户端".getBytes()); s.close(); }}/*需求:定义端点接收数据并打印在控制台服务端:1.创建服务端serversocket对象并指定端口 当不指定时,使用connect方法指定2. 获取连接过来的客户端对象 通过serversocket的accept方法等待,阻塞式,无连接一直等待3.客户端如果发过来数据,服务端使用对应连接的读取流获取发过来数据 打印在服务台4,关闭服务端(可选)*/ class tcpServer{ public static void main(String[] args) throws IOException{ //建立服务端socket服务,并监听端口 ServerSocket ss =new ServerSocket(10000); //通过accept方法获取链接过来的客户端对象(s中有内容,端口,IP属性) Socket s = ss.accept(); //IP: String ip =s.getInetAddress().getHostAddress(); System.out.println(ip+"...连接成功" ); //内容:获取客户端发送过来的数据,那么要使用客户端对象s InputStream in = s.getInputStream(); byte[] buf =new byte[1024]; int len=in.read(buf); String content = new String(buf,0,len); System.out.println("内容:"+content ); s.close();//关闭客户端,服务器可以控制客户 ss.close();//关闭服务端,可选操作 }}
练习一:
演示tcp的传输的客户端和服务端的互访需求:客户端给服务端发送数据,服务端收到后,给客户端反馈信息。客户端:1.建立socket服务。指定要连接主机和端口。2.获取socket流中的输出流。将数据写到该流中,通过网络发送给服务端。3.获取socket流中的输入流,将服务器反馈的数据获取到,并打印。4.关闭客户端。
1 import java.net.*; 2 import java.io.*; 3 class TcpClient2 4 { 5 public static void main(String[] args) 6 { 7 try 8 { 9 Socket s=new Socket("172.29.115.1",10000);10 OutputStream os=s.getOutputStream();11 os.write("你好啊,服务端!".getBytes());12 13 byte[] buf=new byte[1024];14 InputStream is=s.getInputStream();15 int len=is.read(buf);16 System.out.println(new String(buf,0,len));17 s.close();18 }19 catch (Exception e)20 {21 throw new RuntimeException("客户端建立失败!");22 }23 24 }25 }26 class TcpServer227 {28 public static void main(String[] args) 29 {30 try31 {32 ServerSocket ss=new ServerSocket(10000);33 Socket s=ss.accept();34 35 InputStream is=s.getInputStream();36 byte[] buf=new byte[1024];37 int len=is.read(buf);38 System.out.println(s.getInetAddress().getHostAddress()+"::"+new String(buf,0,len));39 40 OutputStream os=s.getOutputStream();41 os.write("你也好,欢迎光临!".getBytes());42 43 s.close();44 ss.close();45 46 47 }48 catch (Exception e)49 {50 throw new RuntimeException("服务端建立失败!");51 }52 }53 }
练习二:
从客户端向服务端上传文件。
import java.net.*; 2 import java.io.*; 3 class TextClient 4 { 5 public static void main(String[] args) throws Exception 6 { 7 Socket s=new Socket("127.0.0.1",10006); 8 BufferedReader bufr= 9 new BufferedReader(new FileReader("ChatDemo.java"));10 PrintWriter out=new PrintWriter(s.getOutputStream(),true);11 String line=null;12 while((line=bufr.readLine())!=null) //bufr.readLine()读取的是文件流对象,读到流末尾能自己结束13 {14 out.println(line);15 }16 s.shutdownOutput(); //禁用此套接字的输出流。结束标记。防止服务端buffIn.readline()一直阻塞等待。另外socket.close()也会发送结束标记17 BufferedReader bufIn=new BufferedReader(new InputStreamReader(s.getInputStream()));18 String str=bufIn.readLine();19 System.out.println("server:"+str);20 bufr.close();21 s.close();22 23 24 }25 }26 class TextServer27 {28 public static void main(String[] args) throws Exception29 {30 ServerSocket ss=new ServerSocket(10006);31 Socket s=ss.accept();32 System.out.println(s.getInetAddress().getHostAddress()+"....connected");33 BufferedReader bufIn=34 new BufferedReader(new InputStreamReader(s.getInputStream()));35 PrintWriter out=new PrintWriter(new FileWriter("server.txt"),true);36 String line=null;37 while((line=bufIn.readLine())!=null) //bufIn.readLine()读取的是socket流,读到流末尾标记才能结束38 {39 out.println(line);40 41 }42 PrintWriter pw=new PrintWriter(s.getOutputStream(),true);43 pw.println("上传成功!");44 45 s.close();46 ss.close();47 out.close();48 49 }50 }
特别注意一些阻塞式方法当没有读取到结束标记的时候引起的一直等待的情况。