一开始学 Java 网络编程的时候,因为不懂 TCP/IP 协议,被搞得一头雾水。前段时间读了《计算机网络》,对 TCP/IP 协议有了新的认识,现在再来写一下。
阅读前可参考博文:
- 运输层协议概述:https://blog.youkuaiyun.com/codejas/article/details/80105292
- UDP 介绍:https://blog.youkuaiyun.com/codejas/article/details/80192469
本来也想总结一下 TCP 的知识点,但是 TCP 真的是太复杂了,三言两语是解释不清楚的。如果以后有机会深入了解 TCP,再尝试着总结一下。
关于 TCP 与 UDP 相关的内容就不再介绍了,会在注释里作一些相关解释。
一、UDP(User Datagram Protocol)编程
数据接收端(当发送端发送的消息是“over”时,关闭通信)
public class UdpRecieve {
public static void main(String[] args) throws IOException {
/**
* DatagramSocket:UDP 协议 API
* 6457:一个16位的协议端口号,因此最大值为65535,用于实现不同主机进程之间的逻辑通信
* 注意:这个端口号是主机之间用于进程识别的端口号,不是数据发送的端口号
*/
DatagramSocket socket = new DatagramSocket(6457);
while (true) {
/**
* 注意接收的字节数组大小,一般一个中文汉字占2个字节
*/
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
/**
* 接收发送端发送的数据
* 并将收到的数据(发送端的IP地址,端口,数据等)封装成数据报
*/
socket.receive(packet);
/**
* 获取数据报中的数据信息,主机IP,端口号等
* 关于数据报中有哪些信息,可以参照UDP报文段格式(包括首部与数据信息)
*/
InetAddress host = packet.getAddress();
int port = packet.getPort();
String message = new String(packet.getData(), 0, packet.getLength());
System.out.println(host + "发来了消息,使用的端口号是:"
+ port + ",数据内容:" +message);
if("over".equals(message)) {
socket.close();
break;
}
}
}
}
数据发送端(从控制台接收用户输入的数据,如果输入“over”停止发送):
public class UdpSend {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
/**
* 指定数据接收端的IP地址,用于找到目的主机
* 指定数据接收端的端口号,用于找到目的主机中的具体进程
*/
String host = "172.18.126.14";
int port = 6457;
String line = null;
while((line = reader.readLine()) != null) {
byte[] bytes = line.getBytes();
DatagramSocket socket = new DatagramSocket();
/**
* 在数据报中封装接收端的IP地址,端口号与发送的数据
*/
DatagramPacket packet = new DatagramPacket(bytes, bytes.length
, InetAddress.getByName(host), port);
socket.send(packet);
if("over".equals(line)) {
socket.close();
break;
}
}
reader.close();
}
}
程序运行的时候,先运行数据接收端,然后再运行数据发送端。如果数据发送端先运行会因为找不到目的主机的进程,而丢弃数据。
二、TCP(Transmission Controll Protocol)编程
服务端(接收客户端信息并回应):
public class TcpServer {
public static void main(String[] args) throws IOException {
/**
* ServerSocket:TCP 服务端 API
* 指定服务端使用的端口号
*/
ServerSocket serverSocket = new ServerSocket(7854);
/**
* 创建套接字对象,套接字是TCP连接的端点
* TCP套接字=IP地址:端口号
* 因为UDP是面向无连接的,因此在UDP中不存在套接字的概念
*/
Socket socket = serverSocket.accept();
/**
* 根据套接字获取数据发送方的IP地址与使用的端口号
*/
InetAddress host = socket.getInetAddress();
int port = socket.getPort();
/**
* 根据套接字对象获取数据输入流对象
*/
InputStream in = socket.getInputStream();
byte[] bytes = new byte[1024];
/**
* 从数据输入流中读取发送的数据
*/
int len = in.read(bytes);
String message = new String(bytes, 0, len);
System.out.println(host + "发来了消息,使用的端口号是:"
+ port + ",数据内容:" +message);
/**
* 因为TCP提供全双工的数据通信,在确认消息收到后
* 返回一个确认消息给发送端
*/
OutputStream out = socket.getOutputStream();
out.write("数据已收到".getBytes());
serverSocket.close();
socket.close();
in.close();
out.close();
}
}
客户端(发送消息,并接收服务端的确认消息):
public class TcpClient {
public static void main(String[] args) throws IOException {
/**
* 创建客户端套接字,指定服务端主机IP地址与端口号
*/
Socket socket = new Socket( "172.18.126.14", 7854);
/**
* 根据套接字对象获取输出流对象,并将数据发送
*/
OutputStream out = socket.getOutputStream();
out.write("嘿嘿嘿~我来了".getBytes());
/**
* 用于接收服务端发出的消息确认
*/
byte[] bytes = new byte[1024];
InputStream in = socket.getInputStream();
int len = in.read(bytes);
String responseMessage = new String(bytes, 0, len);
System.out.println("客户端收到了确认:" + responseMessage);
socket.close();
out.close();
in.close();
}
}