赞
踩
通信的协议还是比较复杂的, java.net 包中包含的类和接口,它们提供低层次的通信细节。我们可以直接使用这些类和接口,来专注于网络程序开发,而不用考虑通信的细节。
java.net
包中提供了两种常见的网络协议的支持:
完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可 以保证传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。
TCP协议四次握手断开连接,目的确保双方数据的收发都已经完成!
ipconfig (window电脑)
ping 空格 IP地址 (windows)
ping 220.181.57.216
127.0.0.1
、 localhost
。网络的通信,本质上是两个进程(应用程序)的通信。每台计算机都有很多的进程,那么在网络通信时,如何区分 这些进程呢?
如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的进程(应用程序)了。
利用 协议 + IP地址 + 端口号 三元组合,就可以标识网络中的进程了,那么进程间的通信就可以利用这个标识与其 它进程进行交互。
在Java中UDP的实现类是DatagramSocket
DatagramSocket
:此类表示用于发送和接收数据报数据包的套接字。 以允许广播数据报的传输。 为了接收广播数据包,DatagramSocket应该绑定到通配符地址
/* UDP协议发送数据: 1.创建发送端的socket对象 2.创建数据,把并把数据打包 3.调用Socket对象的发送方法来发送数据 4.释放资源(因为Socket传输数据是采用IO流来操作数据的) DatagramSocket:此类表示用于发送和接收数据报数据包的套接字。 以允许广播数据报的传输。 为了接收广播数据包,DatagramSocket应该绑定到通配符地址 */ public class UDPSendmessage { public static void main(String[] args) { String message = "hello java, UDP 我来了"; udpSendMessage(message.getBytes()); } /* UDP:发送数据 */ public static void udpSendMessage(byte[] bytes){ //创建数据 DatagramSocket datagramSocket = null; try { datagramSocket = new DatagramSocket(); //send(DatagramPacket p) 该类表示数据报包。 InetAddress inetAddress = InetAddress.getByName("192.168.31.31"); DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length,inetAddress,10086); datagramSocket.send(datagramPacket); } catch (SocketException e) { e.printStackTrace(); } catch (UnknownHostException e){ //抛出不认识主机异常 e.printStackTrace(); } catch (IOException e){ e.printStackTrace(); } finally { if (datagramSocket != null) { datagramSocket.close(); } } } }
/* UDP协议发送数据: 1.创建发送端的socket对象 2.创建一个数据包(接收容器) 3.调用Socket对象的接收方法接收数据 4.解析数据,展示在控制台 4.释放资源(因为Socket传输数据是采用IO流来操作数据的) */ public class UDPReceiveMessage { public static void main(String[] args) { //接收数据 receiveMessage(); } public static void receiveMessage() { DatagramSocket datagramSocket = null; try { datagramSocket = new DatagramSocket(10086); //创建一个数据包格式 byte[] bytes = new byte[1024]; DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length); //接收数据 //该方法阻塞,直到接收到数据报。 数据报包对象的length字段包含接收到的消息的长度。 // 如果消息长于数据包的长度,消息将被截断。 datagramSocket.receive(datagramPacket); InetAddress inetAddress = datagramPacket.getAddress(); String ip = inetAddress.getHostAddress();//返回IP地址 //获取真实的数据 byte[] datas = datagramPacket.getData(); int datalength = datagramPacket.getLength(); String dataString = new String(datas,0,datalength); System.out.println(ip + "------" + dataString); } catch (SocketException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { //关闭套接字,释放资源 这里其实不需要关闭,因为接收端是一直接收数据,不需要关闭 if (datagramSocket != null){ //datagramSocket.close(); } } } }
TCP通信能实现两台计算机之间的数据交互,通信的两端,要严格区分为客户端(Client)与服务端(Server)。
Socket
类:该类实现客户端套接字,套接字指的是两台设备之间通讯的端点。Socket client = new Socket("127.0.0.1", 6666);
public InputStream getInputStream()
: 返回此套接字的输入流。 如果此Scoket具有相关联的通道,则生成的InputStream 的所有操作也关联该通道。关闭生成的InputStream也将关闭相关的Socket。
public OutputStream getOutputStream()
: 返回此套接字的输出流。如果此Scoket具有相关联的通道,则生成的OutputStream 的所有操作也关联该通道。 关闭生成的OutputStream也将关闭相关的Socket。
public void close()
:关闭此套接字。 一旦一个socket被关闭,它不可再使用。关闭此socket也将关闭相关的InputStream和OutputStream 。
public void shutdownOutput()
: 禁用此套接字的输出流。任何先前写出的数据将被发送,随后终止输出流。
ServerSocket
类:这个类实现了服务器套接字,该对象等待通过网络的请求。
ServerSocket server = new ServerSocket(6666);
public Socket accept()
:侦听并接受连接,返回一个新的Socket对象,用于和客户端实现通信。该方法 会一直阻塞直到建立连接。客户端实现:
/* 文件上传客户端 数据源:/Users/guoweiyong/Desktop/danei.jepg 目的地: 服务器的硬盘(这里假设在桌面upload文件) 1.首先创建本地IO流读取文件 2.获取网络IO 输出流 把数据上传到服务器 */ public class TCPClient { public static void main(String[] args) { FileInputStream fileInputStream = null; Socket socket = null; try { fileInputStream = new FileInputStream("/Users/guoweiyong/Desktop/danei.jpeg"); socket = new Socket("192.168.31.31",12306); OutputStream os = socket.getOutputStream(); //首先使用本地数日刘来读取文件 byte[] bytes = new byte[1024]; int dataLength = 0; //重点: // read() 方法 读取不到内容会阻塞 直到数据输入 //循环读取数据在这里有点问题 ,卡死在循环,循环下面的代码却不执行了 //原因read()方法读取本地文件,结束标记读取到-1 结束,但是while循环会读取到-1吗? // 不会,所以也不会把结束标记写给服务端 while ((dataLength = fileInputStream.read(bytes)) != -1) { //在使用网络输出流来把读取到的数据写入到服务器 os.write(bytes); } /* 解决方法: shutdownOutput() 禁用此套接字的输出流。 对于TCP套接字,任何先前写入的数据将被发送, 随后是TCP的正常连接终止序列。 如果在套接字上调用shutdownOutput()之后写入套接字输出流,则流将抛出IOException。 当我们写完数据之后,我们需要告诉服务器,数据洗完了 然后禁用输出流 */ socket.shutdownOutput(); //读取服务器传递回来的消息 InputStream inputStream = socket.getInputStream(); while ((dataLength = inputStream.read(bytes)) != -1) { System.out.println(new String(bytes,0,dataLength)); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if (fileInputStream != null) { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
服务端实现:
/* 文件上传客户端 数据源:/Users/guoweiyong/Desktop/danei.jepg 目的地: 服务器的硬盘(这里假设在桌面upload文件) 1.首先创建本地IO流读取文件 2.获取网络IO 输出流 把数据上传到服务器 */ public class TCPClient { public static void main(String[] args) { FileInputStream fileInputStream = null; Socket socket = null; try { fileInputStream = new FileInputStream("/Users/guoweiyong/Desktop/danei.jpeg"); socket = new Socket("192.168.31.31",12306); OutputStream os = socket.getOutputStream(); //首先使用本地数日刘来读取文件 byte[] bytes = new byte[1024]; int dataLength = 0; //重点: // read() 方法 读取不到内容会阻塞 直到数据输入 //循环读取数据在这里有点问题 ,卡死在循环,循环下面的代码却不执行了 //原因read()方法读取本地文件,结束标记读取到-1 结束,但是while循环会读取到-1吗? // 不会,所以也不会把结束标记写给服务端 while ((dataLength = fileInputStream.read(bytes)) != -1) { //在使用网络输出流来把读取到的数据写入到服务器 os.write(bytes); } /* 解决方法: shutdownOutput() 禁用此套接字的输出流。 对于TCP套接字,任何先前写入的数据将被发送, 随后是TCP的正常连接终止序列。 如果在套接字上调用shutdownOutput()之后写入套接字输出流,则流将抛出IOException。 当我们写完数据之后,我们需要告诉服务器,数据洗完了 然后禁用输出流 */ socket.shutdownOutput(); //读取服务器传递回来的消息 InputStream inputStream = socket.getInputStream(); while ((dataLength = inputStream.read(bytes)) != -1) { System.out.println(new String(bytes,0,dataLength)); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if (fileInputStream != null) { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
1.我们模拟服务器端,ServerSocket类监听端口,使用浏览器访问
public static void main(String[] args) throws IOException {
ServerSocket server = new ServerSocket(8000); Socket socket = server.accept();
InputStream in = socket.getInputStream();
byte[] bytes = new byte[1024];
int len = in.read(bytes);
System.out.println(new String(bytes,0,len));
socket.close();
server.close();
}
//转换流,读取浏览器请求第一行
BufferedReader readWb = new BufferedReader(new InputStreamReader(socket.getInputStream())); String requst = readWb.readLine();
//取出请求资源的路径
String[] strArr = requst.split(" ");
//去掉web前面的/
String path = strArr[1].substring(1);
System.out.println(path);
服务端实现:
package com.gy.TCP.BSTcp; import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class BSTCPServer { public static void main(String[] args) throws IOException { ServerSocket serverSocket = null; //创建一个输入流对象,来保存socket中的输入流对象 Socket socket = null; FileInputStream fileInputStream = null; try { serverSocket = new ServerSocket(8080); //首先我们需要回去socket中的输入流来读取数据 //accept(): 阻塞式方法,等待客户端链接 socket = serverSocket.accept(); //获取输入流,来读取数据 //首先要获取heml的地址 127.0.0.1----GET /Network-code/web/index.xml HTTP/1.1 //需要读取第一行,我么需要使用字符流来包装一下 BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String line = bufferedReader.readLine(); //结果:127.0.0.1----GET /Network-code/web/index.xml HTTP/1.1 //根据空格来分割字符串,在截取路径 String htmlPath = (line.split(" ")[1]).substring(1);//Network-code/web/index.xml System.out.println("htmlPath========" + htmlPath); //然后在获取本地字节流来读取xml文件,在获取套接字输出流 把xml页面写给客户端 OutputStream outputStream = socket.getOutputStream(); fileInputStream = new FileInputStream(htmlPath); byte[] bytes = new byte[1024]; int datalength = 0; // 字节输出流,将文件写会客户端 OutputStream out = socket.getOutputStream(); // 写入HTTP协议响应头,固定写法 outputStream.write("HTTP/1.1 200 OK\r\n".getBytes()); outputStream.write("Content‐Type:text/html\r\n".getBytes()); // 必须要写入空行,否则浏览器不解析 outputStream.write("\r\n".getBytes()); while ((datalength = fileInputStream.read(bytes)) != -1) { //在套接字输出流把数据传递给客户端 outputStream.write(bytes,0,datalength); } } catch (IOException e) { e.printStackTrace(); } finally { //因为服务器是接收数据的一方,所以其实不需要关闭 //serverSocket.close(); //但是我们需要关闭 当前和客户端数据交互的IO流 if (fileInputStream != null) { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (socket != null) { try { socket.close(); } catch (IOException e) { e.printStackTrace(); } } if (serverSocket != null) { try { serverSocket.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
备注: 讲课视屏中老师,用的是window环境,可以显示出来网页,但是 我在Mac环境下,浏览器显示是一整个html的代码页面,无法显示页面 。 不知道原因,比对代码也没有发现什么问题.(后续研究,会再次修改)
public class ServerDemo { public static void main(String[] args) throws IOException { ServerSocket server = new ServerSocket(8888); while(true){ Socket socket = server.accept(); new Thread(new Web(socket)).start(); } } static class Web implements Runnable{ private Socket socket; public Web(Socket socket){ this.socket=socket; } public void run() { try{ //转换流,读取浏览器请求第一行 BufferedReader readWb = new BufferedReader(new InputStreamReader(socket.getInputStream())); String requst = readWb.readLine(); //取出请求资源的路径 String[] strArr = requst.split(" "); System.out.println(Arrays.toString(strArr)); String path = strArr[1].substring(1); System.out.println(path); FileInputStream fis = new FileInputStream(path); System.out.println(fis); byte[] bytes= new byte[1024]; int len = 0 ; //向浏览器 回写数据 OutputStream out = socket.getOutputStream(); out.write("HTTP/1.1 200 OK\r\n".getBytes()); out.write("Content‐Type:text/html\r\n".getBytes()); out.write("\r\n".getBytes()); while((len = fis.read(bytes))!=‐1){ out.write(bytes,0,len); } fis.close(); out.close(); readWb.close(); socket.close(); }catch(Exception ex){ } } } }
访问效果:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。