网络模型:
OSI参考模型 TCP/IP参考模型
1.找到对方IP
2.数据要发送到对方指定的应用程序上,为了标识这些应用程序,所以给这些网络应用程序都用数字进行标识。
为了方便称呼这个数字,叫做端口,逻辑端口。
3.定义通信规则。这个规则称为协议。国际通用协议 TCP/IP
OSI参考模型 TCP/IP参考模型
应用层
表示层 应用层
会话层
传输层 传输层
网络层 网际层
数据链路层 主机至网络层
物理层
IP地址 :网络中设备的标识。不易记忆,可用主机名。
端口:用于标识进程的逻辑地址,不同进程的标识
有效端口 0~65535,其中0~1024系统使用或保留端口。
以下代码简单演示获取IP地址方法
import java.net.*; import java.io.*; class IPDemo { public static void main(String[] args) throws Exception { InetAddress i = InetAddress.getLocalHost(); //获取本地IP地址 System.out.println(i.toString()); InetAddress ia = InetAddress.getByName("www.baidu.com");//获取百度的IP地址 System.out.println("name:"+ia.getHostName()); //打印百度的主机名 System.out.println("address:"+ia.getHostAddress()); //打印百度的主机地址 } }
UDP:将数据及源和目的封装成数据包中,不需要建立连接
每个数据包大小限制在64k内
因无连接,是不可靠协议
不许建立连接,速度快
TCP:建立连接,形成传输数据的通道
在连接中进行大数据批量传输
通过三次握手完成连接,是可靠协议
必须建立连接,效率会稍低
Socket:Socket就是为网络服务提供的一种机制。
通信的两端都是有Socket。
网络通信其实就是Socket间的通信。
数据在连个Socket间通过IO传输。
需求:通过UDP传输方式,将一段文字数据发送出去
思路:
1.建立UDPSocket服务
2.提供数据,并将数据封装到数据包中。
3.通过socket服务的发送功能,将数据包发出去。
4.关闭资源。
import java.io.*; import java.net.*; class UdpSend //建立UDP的客户端 { public static void main(String[] args) throws Exception { //1.创建udp服务,通过DatagramSocket对象 DatagramSocket ds = new DatagramSocket (); //2.确定数据,并封装成数据包 byte[] buf = "udp ge men lai le ".getBytes(); //创建字节数组存储字符串的字节数据 DatagramPacket dp = //将字节数组封装,并设置目的地为192.168.0.100的10000端口 new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.0.100"),10000); //3.铜鼓Socket服务,将已有的数据包发送出去,通过send方法发送出去 ds.send(dp); //4.关闭资源 ds.close(); } }
需求:定义一个应用程序,用于接收udp数据
1.定义udpsocket服务。
2.定义一个数据包,因为要存储接收到的字节数据。
因为数据包对象中有更多功能可以提取字节数据的不同数据信息。
3.通过socket服务的recevice方法将受到的数据存入到定义好的数据中
4.通过数据包对象的特有功能,将这些不同的数据取出,打印在控制台上
5.关闭资源
import java.io.*; import java.net.*; class UdpReceive //建立UDP的服务端 { public static void main(String[] args) throws Exception { //1.创建udp socket,建立端点,设置端口为10000 DatagramSocket ds = new DatagramSocket (10000); //2.确定数据,用于存储数据 while(true) { byte[] buf = new byte [1024]; //建立字节数组 DatagramPacket dp = //将数据封装入字节数组中 new DatagramPacket(buf,buf.length); //3.通过recevice方法将疏导的数据存入数据包中 ds.receive(dp); //这是阻塞式方法,无数据接收时会停止线程 //4.通过数据包 String ip = dp.getAddress().getHostAddress(); //获取数据包中的IP地址 String data = new String(dp.getData(),0,dp.getLength());//将数据变为字符串 int port = dp.getPort();//获取数据包中的端口 System.out.println(ip+"::"+data+":"+port); } //5.关闭资源 //ds.close(); } }
TCP传输
客户端对应Socket
服务端对应SerrverSocket
客户端:通过查阅Socket对象,该对象建立时,就可以去连接指定的主机。
因为Tcp是面向连接的,所以在建立socket服务时,就需要有服务端存在,
并连接成功形成通路后,在该通路中进行数据传输。
步骤:1.建立Socket服务,并指定要连接的主机和端口
2.获取Socket中的输出流,写入数据后,自动发出
3.提取Socket中的输入流,获取数据后,操作数据(如:打印)。
4.关闭客户端
服务端:1.建立服务端的socket服务,即ServerSocket,并监听一个端口。
2.通过accept()获取连接过来的客户端对象。该方法为阻塞式的,没有连接会自动等待。
3.客户端发过来对象,服务端使用对应客户端对象,并获取该客户端对象的输入流获取数据,
获取该客户端对象的输出流,写入数据,自动发送给客户端。
4.关闭服务端。
演示TCP的传输的客户端和服务端的互访。
需求:客户端给服务器端发送数据,服务端收到,给客户端反馈信息。
客户端:
1.建立socket服务,指定要连接的主机和接口。
2获取socket流中的输出流,将数据写入到该流中,通过网络发送给服务端。
3.获取socket流中的输入流,将服务端反馈的数据获取到,并打印
4.关闭客户端
服务端:
1.建立ServerSocket服务,自定要监听的端口
2.从Socket中获取输入流和输出流
3.从输入流中读取客户端传过来的数据并操作
4.将数据写入输出流并传给客户端
5.关闭服务端
import java.io.*; import java.net.*; class TcpClient { public static void main(String [] args) throws Exception { Socket s = new Socket("192.168.0.100",10001); //建立Socket服务,指定连接的主机和端口 OutputStream out = s.getOutputStream(); //获取Socket中的输出流 out.write("服务端,你好".getBytes()); //写入需要输出的数据 InputStream in = s.getInputStream(); //获取Socket中的输入流 byte[] buf = new byte[1024]; //定义字节数组 int len = in.read(buf); //将输入流中的数据写入字节数组 System.out.println(new String(buf,0,len)); //将字节数组转为字符串并在控制台上打印 s.close(); //关闭资源 } } class TcpServer { public static void main(String [] args) throws Exception { ServerSocket ss = new ServerSocket(10001); //建立服务端ServerSocket对象,指定监听端口 Socket s = ss.accept(); //从ServerSockeet中获取Socket对象 String ip = s.getInetAddress().getHostAddress();//获取Socket中的IP地址 System.out.println(ip+"....connecting"); //显示正在连接的主机 InputStream in = s.getInputStream(); //从Socket中获取输入流 byte[] buf = new byte[1024]; //定义字节数组 int len = in .read(buf); //将输入流中数据存入字节数组,并将数组有效长度传给len System.out.println(new String (buf,0,len)); //将字节数组转为字符串并在控制台上打印 OutputStream out = s.getOutputStream(); //获取Socket中的输出流 Thread.sleep(10000); //为了掩饰效果,令当亲线程睡一段时间 out.write("已收到,你也好".getBytes()); //在输入流中写入数据的字节数据 s.close(); //关闭资源 ss.close(); } }
需求:建立一个文本转换服务器
客户端给服务端发送文本,服务端会将文本转成大写并返给客户端
客户端可以一直输入,直至客户端输入over,关闭客户端和服务端。
分析:
客户端:既然是操作设备上的数据,那么久可以使用io技术,并按照io的操作规律来思考
源:键盘录入
目的:网络设备,网络输出流
操作文本数据,选择字符流。
步骤:
1.建立服务
2.获取键盘录入
3.将数据发给服务端
4,获取服务端返回的大写数据
5.关闭数据
本例中出现问题
现象:客户端和服务端都在莫名等待
因为客户端和服务端中都有阻塞式方法,这些方法无结束标记,就会一直等待而导致两端都在等待。
出现此种现象,优先查询阻塞式方法。
import java.io.*; import java.net.*; class TransClient { public static void main(String[] args) throws Exception { Socket s = new Socket("192.168.0.100",10005); //定义读取键盘的流对象 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); //定义目的并与Socket对象中的输出流关联 BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); //定义获取服务端的返回的数据(字节流转换成字符流) BufferedReader bufr1 = new BufferedReader(new InputStreamReader(s.getInputStream())); String line = null; while ((line=bufr.readLine())!=null)//按行读取输入流中的数据 { if (line.equals("over")) //定义结束标识 break; bufw.write(line); //在输出流中写入数据 bufw.newLine(); //换行 bufw.flush(); //刷新缓冲区 String lines = bufr1.readLine();//在lines中写入服务端返回的数据 System.out.println("server:"+lines); } bufr.close(); //关闭资源 bufw.close(); bufr1.close(); s.close(); } } class TransServer { public static void main(String[] args) throws Exception { ServerSocket ss = new ServerSocket(10005); //创建SeverSocket对象并指定监听端口 Socket s = ss.accept(); //从ServerSocket中获取Socket对象 String ip = s.getInetAddress().getHostAddress();//获取IP地址 System.out.println(ip+"....connecting"); //显示正在连接的主机 BufferedReader bufr = //定义源 new BufferedReader(new InputStreamReader(s.getInputStream())); BufferedWriter bufw = //定义目的 new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); String line = null; while ((line=bufr.readLine())!=null) //按行读取输入流中的数据 { if (line.equals("over")) //定义结束标识 break; System.out.println("client:"+line); //打印当前客户端传输过来的数据 bufw.write(line.toUpperCase()); //在输出流中写入读取数据的大写形式 bufw.newLine(); //换行 bufw.flush(); //刷新缓存区 } ss.close(); //关闭资源 s.close(); bufr.close(); } }
需求:上传图片
为保证图片质量用TCP
客户端:
1.建立服务端点
2.读取客户端已有图片数据
3.通过Socket输出流将数据发个服务端
4.读取服务端反馈后关闭。
服务端
这个服务端有个局限性,一次只能连接一个客户端
为了让多个客户端并发访问服务端。
那么服务端最好就是将每个客户端封装到一个单独的线程中,这样就可以同时处理多个客户端。
具体实现:
明确每个客户端要在服务端执行的代码,将其存入run方法。
import java.io.*; import java.net.*; class PicClicent { public static void main(String[] args) throws Exception { Socket s= new Socket("192.168.0.100",10006); //定义Socket对象并指定连接主机和端口 FileInputStream fi = new FileInputStream ("d:\1.jpg"); //定义输入流并关联需操作文件 OutputStream out = s.getOutputStream(); //从Socket中获取输出流 byte[] byt = new byte[1024]; //定义字节数组 int len = 0; while ((len=fi.read(byt))!=-1) //通过循环,将输入流数据存入字节数组 { out.write(byt,0,len); //并依次写入输出流,传给服务端 } s.shutdownOutput(); //关闭Socket中的输出流 InputStream in = s.getInputStream(); //获取Socket中的输入流 byte[] bytin = new byte[1024]; //定义字节数组 int num = in.read(bytin); //读取字节数组的数据 System.out.println(new String(bytin,0,num)); //转换成字符串后再控制台上打印 fi.close(); //关闭资源 s.close(); } } class PicServer { public static void main(String[] args) throws Exception { ServerSocket ss = new ServerSocket (10006); //定义ServerSocket对象并监听指定端口 while (true) //通过循环多次调用 { Socket s = ss.accept(); //获取ServerSocket中的Socket对象 new Thread (new PicThread(s)).start(); //开启复制服务线程 } //ss.close(); } } class PicThread implements Runnable //定义图片复制操作线程类 { private Socket s; PicThread (Socket s) //将Socket对象传给线程类 { this.s = s; } public void run() //覆写run方法,定义需多线程操作的代码 { try { String ip = s.getInetAddress().getHostAddress(); //获取Socket对象的IP地址 System.out.println(ip+"....connecting"); //显示正在连接的主机 InputStream in = s.getInputStream(); //获取Socket对象的输入流 File file = new File(ip+".jpg"); //定义新文件为按照IP地址命名 FileOutputStream fo =new FileOutputStream(file); //定义输出流与文件关联 byte[] byt = new byte[1024]; //定义字节数组 int len = 0; while ((len=in.read(byt))!=-1) //通过循环将输入流中的数据依次写入文件 { fo.write(byt,0,len); } OutputStream out =s.getOutputStream(); //获取Socket中的输出流 out.write("上传成功".getBytes()); //在输出流中写入文件复制成功的提示信息 fo.close(); //关闭资源 s.close(); } catch (Exception e) { throw new RuntimeException(e); } } }