一、TCP程序设计
1、InetAddress类
java.net包中的InetAddress类是与IP地址相关的类,利用该类可以获取IP地址、主机地址等信息。InetAddress类的常用方法如下表所示。

注:InetAddress类的方法会抛出UnknownHostException 异常,所以必须进行异常处理。这个异常在主机不存在或网络连接错误时发生。
2、ServerSocket类
java.net包中的ServerSocket类用于表示服务器套接字,其主要功能是等待来自网络上的“请求”,它可通过指定的端口来等待连接的套接字。服务器套接字一次可以与一个套接字连接。如果多台客户机同时提出连接请求,服务器套接字会将请求连接的客户机存入列队中,然后从中取出一个套接字,与服务器新建的套接字连接起来。若请求连接数(backlog)大于最大容纳数,则多出的连接请求被拒绝。队列的默认大小是50。
ServerSocket类的构造方法都抛出IOException异常,分别有以下几种形式。
- ServerSocket():创建非绑定服务器套接字。
- ServerSocket(int port):创建绑定到特定端口的服务器套接字。
- ServerSocket(int port, int backlog);利用指定的backlog(连接数)创建服务器套接字并将其绑定到指定的本地端口号。
- ServerSocket(int port, int backlog, InetAddress binddress);使用指定的端口、侦听backlog和要绑定到的本地IP地址创建服务器。这种情况适用于计算机上有多块网卡和多个IP 地址的情况,用于可以明确规定ServerSocket在哪块网卡或IP地址上等待客户的连接请求。
ServerSocket类的常用方法如下表所示。

调用ServerSocket类的accept()方法会返回一个和客户端Socket对象相连接的Socket对象,服务器端的Socket对象使用getOutputStream()方法获得的输出流将指向客户端Socket对象使用getInputStream()方法获得的那个输入流;同样,服务器端的Socket对象使用getInputStream()方法获得的输入流将指向客户端Socket对象使用getOutputStream()方 法获得的那个输出流。也就是说,当服务器向输出流写入信息时,客户端通过相应的输入流就能读取,反之亦然。accept()方法会阻塞线程的继续执行,直到接收到客户的请求。
例:TCP网络程序
(1)服务端程序
import java.io.*;
import java.net.*;
public class MyTcp {
private BufferedReader reader;
private ServerSocket server;
private Socket socket;
void getserver() {
try {
server = new ServerSocket(8998); // 1.实例化socket对象
System.out.println("服务器套接字已经创建成功");
while (true) {
System.out.println("等待客户机的连接");
socket = server.accept(); // 2.实例化socket对象
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));// 3.读/写数据
getClientMessage();
}
} catch (Exception e) {
e.printStackTrace(); // 异常输出
}
}
private void getClientMessage() {
try {
while (true) {
if (reader.ready()) {
// 获得客户端信息
System.out.println("客户机:" + reader.readLine());
}
}
} catch (Exception e) {
e.printStackTrace();
}
// 注意关闭
try {
// 4.关闭流
if (reader != null) {
reader.close();
}
// 5.关闭套接字
if (socket != null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
MyTcp tcp = new MyTcp();
tcp.getserver();
}
}
输出:

(2)客户端程序
import java.net.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
public class MyClien extends JFrame{
Socket socket;
private PrintWriter writer;
Container cc; // 物件容器
private JTextArea ta = new JTextArea();
private JTextField tf = new JTextField();
public MyClien(String title) {
super(title); // 调用父类的构造方法
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
cc = this.getContentPane(); // 实例化对象
final JScrollPane scrollPane = new JScrollPane();
scrollPane.setBorder(new BevelBorder(BevelBorder.RAISED));
getContentPane().add(scrollPane, BorderLayout.CENTER);
scrollPane.setViewportView(ta);
cc.add(tf, "South"); // 将文本框放在窗体的下部
tf.addActionListener(new ActionListener() {
// 绑定事件
public void actionPerformed(ActionEvent e) {
writer.println(tf.getText()); // 将文本框中的信息写入流
ta.append(tf.getText() + "\n"); // 将文本框中的信息显示在文本域
ta.setSelectionEnd(ta.getText().length());
tf.setText(""); // 将文本框清空
}
});
}
private void connect() {
ta.append("尝试连接\n");
try {
socket = new Socket("127.0.0.1", 8998); // 1.实例化socket
writer = new PrintWriter(socket.getOutputStream(), true); // 2.读/写数据
ta.append("完成连接\n");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
MyClien client = new MyClien("向服务器送数据");
client.setSize(200, 200);
client.setVisible(true);
client.connect();
}
}
输出:

注:当一台机器上安装了多个网络应用程序时,很可能指定的端口号已被占用。还可能遇到以前运行良好的网絡程序突然运行不了的情况,这种情况很可能也是由于端口被别的程序占用了。此时可以运行netstat-help来获得帮助,使用命令netstat-an 来查看该程序所使用的端口。
二、UDP程序设计
1、基于UDP通信的基本模式如下:
- 将数据打包(称为数据包),然后将数据包发往目的地。
- 接收别人发来的数据包, 然后查看数据包。
下面是总结的UDP程序的步骤。.
发送数据包的步骤如下:
(1)使用DatagramSocket()创建一个数据 包套接字。
(2)使用DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)创建要发送的数据包。
(3)使用DatagramSocket类的send()方法发送数据包。
接收数据包的步骤如下:
(1)使用DatagramSocket(int port)创建数据包套接字,绑定到指定的端口。
(2)使用DatagramPacket(byte[] buf, int length)创建字节数组来接收数据包。
(3)使用DatagramPacket类的receive()方法接收UDP包。
注:DatagramSocket类的receive()方法接收数据时,如果还没有可以接收的数据,在正常情况下receive()方法将阻塞,一直等到网络上有数据传来,receive()方法接收该数据并返回,如果网络上没有数据发送过来,receive()方法 也没有阻塞,肯定是程序有问题,大多数是使用了一个被其他程序占用的端口号。
2、DatagramPacket类
java.net包的DatagramPacket类用来表示数据包。DatagramPacket 类的构造函数有:
- DatagramPacket(byte[] buf, int length)。
- DatagramPacket(byte[] buf, int length, InetAddress address, int port)。
第一种构造函数创建DatagramPacket对象,指定了数据包的内存空间和大小。第二种构造函数不仅指定了数据包的内存空间和大小,还指定了数据包的目标地址和端口。在发送数据时,必须指定接收方的Socket地址和端口号,因此使用第二种构造函数可创建发送数据的DatagramPacket对象。
3、DatagramSocket类
java.net包中的DatagramSocket类用于表示发送和接收数据包的套接字。该类的构造函数有:
- DatagramSocket()。
- DatagramSocket(int port)。
- DatagramSocket(int port, InetAddress addr)。
第一种构造函数创建DatagramSocket对象,构造数据报套接字并将其绑定到本地主机上任何可用的端口。第二种构造函数创建DatagramSocket对象,创建数据报套接字并将其绑定到本地主机上的指定端口。第三种构造函数创建DatagramSocket对象,创建数据报套接字并将其绑定到指定的本地地址。第三种构造函数适用于有多块网卡和多个IP地址的情况。
在接收程序时,必须指定一个端口号,不要让系统随机产生,此时可以使用第二种构造函数。比如有个朋友要你给他写信,可他的地址不确定是不行的。在发送程序时,通常使用第一种构造函数,不指定端口号,这样系统就会为我们分配-一个端口号。就像寄信不需要到指定的邮局去寄-样。
例:UDP网络程序
(1)广播主机程序不断地向外播出信息
import java.net.*;
public class Weather extends Thread{
String weather = "大家好";
int port = 9898;
InetAddress iaddress = null;
MulticastSocket socket = null;
public Weather() {
try {
iaddress = InetAddress.getByName("224.255.10.0"); // 1.实例化InetAddress
socket = new MulticastSocket(port); // 2.实例化MulticastSocket多点广播套接字
socket.setTimeToLive(1); // 指定发送范围是本地网络
socket.joinGroup(iaddress); // 加入广播组
} catch (Exception e) {
e.printStackTrace();
}
}
public void run() {
while (true) {
DatagramPacket packet = null;
byte data[] = weather.getBytes();
packet = new DatagramPacket(data, data.length, iaddress, port); //3.实例化DatagramPacket,将数据打包
System.out.println(new String(data)); // 将广播信息输出
try {
socket.send(packet); // 4、发送数据
sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
Weather w = new Weather();
w.start();
}
}
输出:

(2)接收广播程序
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Receive extends JFrame implements Runnable, ActionListener{
int port;
InetAddress group = null;
MulticastSocket socket = null;
JButton ince = new JButton("开始接收");
JButton stop = new JButton("停止接收");
JTextArea inceAr = new JTextArea(10, 10);
JTextArea inced = new JTextArea(10, 10);
Thread thread;
boolean b =false;
public Receive() {
super("广播数据报");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
thread = new Thread(this);
ince.addActionListener(this); // 绑定按钮ince的单击事件
stop.addActionListener(this); // 绑定按钮stop的单击事件
inceAr.setForeground(Color.blue); // 指定文本域中文字的颜色
JPanel north = new JPanel(); // 创建JPanel对象
north.add(ince);
north.add(stop);
add(north, BorderLayout.NORTH);
JPanel center = new JPanel();
center.setLayout(new GridLayout(1, 2));
center.add(inceAr);
center.add(inced);
add(center, BorderLayout.CENTER);
validate(); // 刷新
port = 9898;
try {
group = InetAddress.getByName("224.255.10.0") // 1.指定接收地址
socket = new MulticastSocket(port); // 2. 绑定多点广播套接字
socket.joinGroup(group); // 加入广播组
} catch (Exception e) {
e.printStackTrace();
}
setBounds(100, 50, 360, 380); // 设置布局
setVisible(true); // 将窗体设置为显示状态
}
@Override
public void run() {
while (true) {
byte data[] = new byte[1024]; // 创建byte数组
DatagramPacket packet = null;
packet = new DatagramPacket(data, data.length, group, port); // 3.待接收包内容
try {
socket.receive(packet); // 4.接收数据包
String message = new String(packet.getData(), 0, packet.getLength()); // 获取数据包中内容
inceAr.setText("正在接收的内容:\n" + message); // 将接收内容显示在文本域中
inced.append(message + "\n"); // 每条信息为一行
} catch (Exception e) {
e.printStackTrace();
}
if (b == true) {
break;
}
}
}
@Override
// 单击事件
public void actionPerformed(ActionEvent e) {
// 单击按钮ince触发的事件
if (e.getSource() == ince) {
ince.setBackground(Color.red);
stop.setBackground(Color.yellow);
if (!(thread.isAlive())) { // 如线程不处于“新建状态”
thread = new Thread(this); // 实例化Thread对象
}
thread.start();
b = false;
}
// 单击按钮stop触发的事件
if (e.getSource() == stop) {
ince.setBackground(Color.yellow);
stop.setBackground(Color.red);
b = true;
}
}
public static void main(String[] args) {
Receive rec = new Receive();
rec.setSize(460, 200);
}
}
输出:

注:要广播或接收广播的主机地址必须加入到一个组内,地址范围为224.0.0.0~224.255.255.255,这类地址并不代表某个特定主机的位置。加入到同一个组的主机可以在某个端口上广播信息。

本文详细介绍了Java中的TCP和UDP网络通信。在TCP部分,讲解了InetAddress类的功能以及ServerSocket类的使用,包括不同构造方法和accept()方法的作用。在UDP部分,阐述了基于UDP通信的基本模式,以及DatagramPacket和DatagramSocket类的运用,展示了发送和接收数据包的步骤。
1780

被折叠的 条评论
为什么被折叠?



