网络编程
网络通信协议分层思想
为什么要分层呢?因为整个网络协议非常复杂,要涉及到方方面面的知识,而且还有对底层硬件的操作,利用分层的思想,我们可以将复杂的通信协议分割成一层层的形式,上一层可以调用下一层,而与再下一层不发生关系,各层之间互不影响,便于系统的开发。我们把用户程序作为最高层,把物理通信线路作为最底层,高层到底层一步步封装,我们不需要直接操作底层,而是操作最简单的最高层,这就是分层的意义。
参考模型
物理层、数据链路层、网络层、传输层、会话层、表示层、应用层
应用层、传输层(TCP/UDP层)、网络层(IP层)、数据链路层、物理层
我们今天要讲的主要是传输层。
IP协议
IP层:给我们做的最大贡献就是提供了独一无二的IP地址。
IP TCP UDP 网关 内网 子网掩码
UDP是User Datagram Protocol的简称,是一种无连接的协议。UDP是从一台计算机向另一台计算机发送称为数据报的独立数据包的协议,该协议并不保证数据报是否能正确地到达目的地,它是一个非面向连接的协议。每个数据报都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,因此能否到达目的地,到达时间以及内容的正确性都是不能保证的。
端口:用于区分不同的网络应用程序 ,占两个字节,所以共有 65535 个端口号。不过系统会随时征用 1024 以下端口。
端口又分TCP和UDP端口,各有 65535 个端口。
TCP Socket 通信模型
示例1:最简单的Socket模型:Client写数据务器读数据
// Server
import java.net.*;
import java.io.*;
public class Server {
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(6666); // 已经开始监听6666端口了
while(true) {
Socket s = ss.accept(); // ss建立一个插座Socket,接受和客户端s的连接(阻塞式连接),连接成功,相当于服务器和客户端之间连接了两根管道。读的管道(InputStream)和写的管道(OutputStream)。
System.out.println("A Client has Connected!");
DataInputStream dis = new DataInputStream(s.getInputStream());
System.out.println(dis.readUTF());// 所以先获取管道,要说就获取写的管道,要听就获取读的管道。
dis.close();
s.close();
}
}
}
// Client
import java.net.*;
import java.io.*;
public class Client {
public static void main(String[] args) throws Exception {
Socket s = new Socket("127.0.0.1", 6666);// 申请和ss建立连接
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
dos.writeUTF("Hello TCP."); // 连接好之后开始说话,通过管道流的方式来说话,所以先获取管道,要说就获取写的管道,要听就获取读的管道。
dos.flush();
dos.close();
s.close();
}
}
Client 端的 IP 地址不能随意指定,不知为何。
示例2:服务器向客户端发送数据,客户端接收数据
// Server
import java.net.*;
import java.io.*;
public class Server {
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(6666);
while(true) {
Socket s = ss.accept();
System.out.println("A Client has Connected!");
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
dos.writeUTF("Hi [#IP:" + s.getInetAddress() + " #Port:" + s.getPort() + "], I'm Server.");
dos.close();
s.close();
}
} catch (IOException e) {
System.out.println("Server Error!!!");
e.printStackTrace();
}
}
}
// Client
import java.net.*;
import java.io.*;
public class Client {
public static void main(String[] args) {
try {
Socket s = new Socket("127.0.0.1", 6666);
DataInputStream dis = new DataInputStream(s.getInputStream());
System.out.println(dis.readUTF());
dis.close();
s.close();
} catch(IOException e) {
System.out.println("Client Error!!!");
e.printStackTrace();
}
}
}
//I:\Java\Demo>java Client
//Hi [#IP:/127.0.0.1 #Port:15944], I'm Server.
//I:\Java\Demo>java Client
//Hi [#IP:/127.0.0.1 #Port:15953], I'm Server.
//I:\Java\Demo>java Client
//Hi [#IP:/127.0.0.1 #Port:15954], I'm Server.
//I:\Java\Demo>java Client
//Hi [#IP:/127.0.0.1 #Port:15955], I'm Server.
示例3:服务器和客户端同时读写数据(当然一个先读一个先写喽,如果两个都读或都写的话就卡死啦)
// Server
// 服务器先接收客户端的数据,然后再发送数据给客户端
import java.net.*;
import java.io.*;
public class Server {
public static void main(String[] args) {
try {
ServerSocket ss = new ServerSocket(6666);
Socket s = ss.accept();
System.out.println("A Client has Connected!");
DataInputStream dis = new DataInputStream(s.getInputStream());
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
String str = null;
if((str=dis.readUTF()) != null)
{
System.out.println("I'm reseaving message...");
Thread.sleep(3000);
dos.writeUTF("Hi [#IP:" + s.getInetAddress() + " #Port:" + s.getPort() + "], I hava reseaved: " + str);
}
dis.close();
dos.close();
s.close();
} catch (IOException e) {
System.out.println("Server Error!!!");
e.printStackTrace();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// Client
// 客户端先发送数据给服务器,再接收服务器的回复。
import java.net.*;
import java.io.*;
public class Client {
public static void main(String[] args) {
try {
Socket s = new Socket("localhost", 6666);
DataInputStream dis = new DataInputStream(s.getInputStream());
DataOutputStream dos = new DataOutputStream(s.getOutputStream());
System.out.println("Hi Server, I'm sending message to you...");
Thread.sleep(2000);
dos.writeUTF("Hello HYJ.");
System.out.println(dis.readUTF());
dis.close();
s.close();
} catch(UnknownHostException e) {
e.printStackTrace();
} catch(IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
注意:这里面使用的是 Socket s = new Socket("localhost", 6666);
最后要 catch(UnknownHostException e),UnknownHostException 小于 IOException 所以要放前面。
UDP 通信模型
示例:
// UDPServer 接收并打印来自客户端的数据
import java.net.*;
import java.io.*;
public class UDPServer {
public static void main(String[] args) {
try {
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
DatagramSocket ds = new DatagramSocket(8888);
while(true) {
ds.receive(dp);
//ByteArrayInputStream bais = new ByteArrayInputStream(buf);
//DataInputStream dis = new DataInputStream(bais);
//System.out.println("UDPClient: " + dis.readUTF());
System.out.println("UDPClient: " + new String(buf, 0, dp.getLength()));
}
} catch (IOException e) {
System.out.println("UDPServer Error!!!");
e.printStackTrace();
}
}
}
// UDPClient 发送数据给服务器
import java.net.*;
import java.io.*;
public class UDPClient {
public static void main(String[] args) {
try {
//ByteArrayOutputStream baos = new ByteArrayOutputStream();
//DataOutputStream dos = new DataOutputStream(baos);
//dos.writeUTF("Hello HYJ!");
//dos.flush();
//byte[] buf = baos.toByteArray();
byte[] buf = (new String("Hello HYJ!")).getBytes();
System.out.println("buf.length = " + buf.length);
DatagramPacket dp = new DatagramPacket(buf, buf.length, new InetSocketAddress("127.0.0.1", 8888));
DatagramSocket ds = new DatagramSocket(7777);
ds.send(dp);
ds.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}