1 网络通信协议
通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则
1.1 IP地址和端口号
1.计算机指定一个标识号,通过这个标识号来指定接受数据的计算机或者发送数据的计算机。
2.IPv4,它是由4个字节大小的二进制数来表示,如:00001010000000000000000000000001。由于二进制形式表示的IP地址非常不便记忆和处理,因此通常会将IP地址写成十进制的形式,每个字节用一个十进制数字(0-255)表示,数字间用符号“.”分开,如 “192.168.1.100”。
3.IPv6使用16个字节表示IP地址,它所拥有的地址容量约是IPv4的8×1028倍,达到2128个(算上全零的),这样就解决了网络地址资源数量不够的问题。
4.访问目标计算机中的某个应用程序,还需要指定端口号。在计算机中,不同的应用程序是通过端口号区分的。端口号是用两个字节(16位的二进制数)表示的,它的取值范围是0~65535,其中,0~1023之间的端口号用于一些知名的网络服务和应用,用户的普通应用程序需要使用1024以上的端口号,从而避免端口号被另外一个应用或服务所占用。
1.2 InetAddress
public class Demo01 { public static void main(String[] args) throws UnknownHostException { //获取本地主机对象 InetAddress inet=InetAddress.getLocalHost(); System.out.println(inet); //根据主机名获取Inet对象 InetAddress inet2=InetAddress.getByName("PC-201704301608"); System.out.println(inet2); //获取主机名 System.out.println(inet2.getHostName()); //获取ip地址 String ip=inet.getHostAddress(); System.out.println(ip); } }
2 UDP与TCP协议
2.1 UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。
UDP传输数据被限制在64K以内。
2.2 TCP协议是面向连接的通信协议,即在传输数据前先在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。
“三次握手”:第一次握手,客户端向服务器端发出连接请求,等待服务器确认,第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求,第三次握手,客户端再次向服务器端发送确认信息,确认连接。
3 UDP通信
JDK中提供了一个DatagramPacket类,该类的实例对象就相当于一个集装箱,用于封装UDP通信中发送或者接收的数据。
在创建发送端和接收端的DatagramPacket对象时,使用的构造方法有所不同,接收端的构造方法只需要接收一个字节数组来存放接收到的数据,而发送端的构造方法不但要接收存放了发送数据的字节数组,还需要指定发送端IP地址和端口号。
JDK中提供的一个DatagramSocket类。DatagramSocket类的作用就类似于码头,使用这个类的实例对象就可以发送和接收DatagramPacket数据包。
发送端:
public class UDPSend { //UDP协议发送端 public static void main(String[] args) throws IOException { Scanner sc=new Scanner(System.in); String str=sc.next(); //1.创建数据包对象,封装要发送的信息,接收端的ip以及端口号 byte[]bytes=str.getBytes(); InetAddress inet=InetAddress.getByName("127.0.0.1"); DatagramPacket dp=new DatagramPacket(bytes, bytes.length,inet,8888); //2.创建DatagramSocket对象 DatagramSocket ds=new DatagramSocket(); //3.发送数据包 ds.send(dp); //4.释放资源 ds.close(); } }
接受端:
public class UDPRecieve { //UDP接收端 public static void main(String[] args) throws IOException { //1.创建DatagramSocket对象明确端口号 DatagramSocket ds=new DatagramSocket(8888); //2.创建字节数组接受数据 byte[]bytes=new byte[1024]; //3.创建数据包对象 DatagramPacket dp=new DatagramPacket(bytes, bytes.length); //4.接受数据包 ds.receive(dp); //5.拆包 //获取发送端的ip地址 String ip=dp.getAddress().getHostAddress(); //获取发送端的端口号 int port=dp.getPort(); //获取实际数据的长度 int length=dp.getLength(); System.out.println("ip地址为"+ip+",端口号为"+port+"发送的内容为:"+new String(bytes,0,length)); System.out.println(new String(bytes)); //6.关闭资源 } }
4 TCP通信
在JDK中提供了两个类用于实现TCP程序,一个是ServerSocket类,用于表示服务器端,一个是Socket类,用于表示客户端。
通过一张图来描述服务器端和客户端的数据传输,如下图所示。
服务器端:
public class TCPServer { public static void main(String[] args) throws IOException { //创建ServerSocket对象,接受客户端 ServerSocket server=new ServerSocket(8888); //调用accept方法,获取连接我的客户端的Socket对象 Socket socket=server.accept(); //获取输入字节流 InputStream in=socket.getInputStream(); byte[]bytes=new byte[1024]; int len=in.read(bytes); //获取ip地址 String ip=socket.getLocalAddress().getHostAddress(); //获取端口号 int port=socket.getPort(); System.out.println(ip+port+new String(bytes,0,len)); //回复客户端 OutputStream out=socket.getOutputStream(); out.write("收到!".getBytes()); //释放资源 server.close(); } }
客户端:
public class TCPClient { public static void main(String[] args) throws IOException { // 创建Socket对象,连接服务器 Socket socket = new Socket("127.0.0.1", 8888); // 获取字节输出流 OutputStream out = socket.getOutputStream(); // 发送数据 out.write("你好服务器".getBytes()); // 接受服务器端的回复 InputStream in = socket.getInputStream(); byte[] bytes = new byte[1024]; int len = in.read(bytes); // 获取ip地址 String ip = socket.getLocalAddress().getHostAddress(); // 获取端口号 int port = socket.getPort(); System.out.println(ip + port + new String(bytes, 0, len)); // 释放资源 socket.close(); } }
练习:文件上传到服务器
线程接口:
public class MyRunnable implements Runnable { private Socket socket; public MyRunnable(Socket socket) { this.socket = socket; } public void run() { FileOutputStream fos = null; try { // 获取字节输入流 InputStream in = socket.getInputStream(); // 明确目的地 File file = new File("D:\upload"); // 如果文件夹不存在,则创建一个新的文件夹 if (!file.exists()) { file.mkdirs(); } // 文件路径 // 文件名:域名+毫秒值+6位随机数 String filename = "oracle" + System.currentTimeMillis() + new Random().nextInt(999999); String path = file.getAbsolutePath() + File.separator + filename + ".png"; fos = new FileOutputStream(path); // 开始复制 byte[] bytes = new byte[1024]; int len = 0; while ((len = in.read(bytes)) != -1) { fos.write(bytes, 0, len); } // 回复客户端 OutputStream out = socket.getOutputStream(); out.write("上传成功!".getBytes()); } catch (IOException ex) { ex.printStackTrace(); } finally { // 释放资源 try { fos.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } }
服务器端:
public class TCPServer { public static void main(String[] args) throws IOException { // 创建ServerSocket对象,接受客户端 ServerSocket server = new ServerSocket(8888); while (true) { // 调用accept方法,创建连接并获取客户端Socket对象 Socket socket = server.accept(); MyRunnable mr = new MyRunnable(socket); new Thread(mr).start(); } } }
客户端:
public class TCPClient { public static void main(String[] args) throws UnknownHostException, IOException { //创建Socket对象,连接服务器 Socket socket=new Socket("192.168.1.171",8888); //获取字节输出流 OutputStream out=socket.getOutputStream(); //创建字节输入流,明确数据源 FileInputStream fis=new FileInputStream("D:\test\a1.jpg"); byte[]bytes=new byte[1024]; //开始复制 int len=0; while((len=fis.read(bytes))!=-1){ out.write(bytes,0,len); } //告诉服务器端不要再等了,末尾加个标记 socket.shutdownOutput(); //接受服务器的回复 InputStream in=socket.getInputStream(); len=in.read(bytes); System.out.println(new String(bytes,0,len)); //释放资源 fis.close(); socket.close(); } }