第十九章 网络通信

网络通信

本章将从介绍网络协议开始,向读者介绍TCP网络程序和UDP网络程序

网络程序设计基础

局域网与因特网

网络协议

  1. IPv4,4个字节; IPv6,16个字节。
  2. TCP(Transmission Control Protocol)协议适合可靠性要求比较高的场合,HTTP、FTP和Telnet等都需要使用可靠的通信频道
  3. UDP(User Datagram Protocol)无连接,不可靠通信协议。以独立发送数据包的方式进行。

端口和套接字

  1. 所有数据通过端口对内、外送达特定的计算机。
  2. 网络程序设计中的端口(port)是一个假想的连接装置。端口被规定为一个在0~65535之间的整数。HTTP服务一般使用80端口,而FTP服务使用21端口。
  3. 通常0~1023之间的端口数用于一些知名的网络和应用。
  4. 网络程序中的套接字(Socket) 用于将应用程序与端口连接起来。套接字是一个假想的连接装置,就像插插头的设备“插座”用于连接电器与电线一样。
    在这里插入图片描述

TCP程序设计基础

TCP网络程序设计是指利用Socket类编写通信程序。

InetAddress

Java.net包中的InetAddress类是与IP地址相关的类,利用该类可以获取IP地址、主机地址等信息。
InetAddress类常用方法:

方法返回值说明
getByName (String host)InetAddress获取与Host想对应的InetAddress对象
getHostAddress()String获取InetAddress对象所包含的IP地址
getHostName()String获取此IP地址的主机名
getLocalHost()InetAddress返回本地主机的InetAddress对象

例:

import java.net.*;

public class Address { // 创建类
	public static void main(String[] args) {
		InetAddress ip; // 创建InetAddress对象
		try { // try语句块捕捉可能出现的异常
			ip = InetAddress.getLocalHost(); // 实例化对象
			String localname = ip.getHostName(); // 获取本机名
			String localip = ip.getHostAddress(); // 获取本IP地址
			System.out.println("本机名:" + localname);// 将本机名输出
			System.out.println("本机IP地址:" + localip); // 将本机IP输出
		} catch (UnknownHostException e) {
			e.printStackTrace(); // 输出异常信息
		}
	}
}

ServeSocket类

  1. ServerSocket类用于表示服务器套接字,主要功能是等待来自网络上的“请求”。
  2. 服务器套接字一次可以与一个套接字连接,如果多台客户机同时提出连接请求,服务器套接字会将请求连接的客户机存入列队中,然后从中取出一个套接字,与服务器新建的套接字连接起来。
  3. 队列的默认大小是50

具体构造方法查看API
常用方法:

方法返回值说明
accept()Socket等待客户及的连接。若连接,则创建一套接字
isBound()boolean判断ServerSocket的帮顶状态
getInetAddress()InetAddress返回此服务器套接字的本地地址
isClosed()boolean
close()void关闭服务器套接字
bind(SocketAddress endpoint)void将ServerSocket绑定到特定地址(IP地址和端口号)
getInetAddress()int返回服务器套接字等待的端口号

当服务器向输出流写入信息时,客户端通过相应的输入流就能读取,反之亦然。

accept()方法会阻塞线程的继续执行,知道接收到客户的呼叫

TCP网络程序

package com.lzw;
import java.io.*;
import java.net.*;

public class MyTcp { // 创建类MyTcp
	private BufferedReader reader; // 创建BufferedReader对象
	private ServerSocket server; // 创建ServerSocket对象
	private Socket socket; // 创建Socket对象socket
	
	void getserver() {
		try {
			server = new ServerSocket(8998); // 实例化Socket对象
			System.out.println("服务器套接字已经创建成功"); // 输出信息
			while (true) { // 如果套接字是连接状态
				System.out.println("等待客户机的连接"); // 输出信息
				socket = server.accept(); // 实例化Socket对象
				reader = new BufferedReader(new InputStreamReader(socket
						.getInputStream())); // 实例化BufferedReader对象
				getClientMessage(); // 调用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 {
			if (reader != null) {
				reader.close(); // 关闭流
			}
			if (socket != null) {
				socket.close(); // 关闭套接字
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) { // 主方法
		MyTcp tcp = new MyTcp(); // 创建本类对象
		tcp.getserver(); // 调用方法
	}
}
package com.lzw;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;

import javax.swing.*;
import javax.swing.border.*;

public class MyClien extends JFrame { // 创建类继承JFrame类
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private PrintWriter writer; // 声明PrintWriter类对象
	Socket socket; // 声明Socket对象
	private JTextArea ta = new JTextArea(); // 创建JtextArea对象
	private JTextField tf = new JTextField(); // 创建JtextField对象
	Container cc; // 声明Container对象
	
	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); // 实例化Socket对象
			writer = new PrintWriter(socket.getOutputStream(), true);
			ta.append("完成连接\n"); // 文本域中提示信息
		} catch (Exception e) {
			e.printStackTrace(); // 输出异常信息
		}
	}
	
	public static void main(String[] args) { // 主方法
		MyClien clien = new MyClien("向服务器送数据"); // 创建本例对象
		clien.setSize(200, 200); // 设置窗体大小
		clien.setVisible(true); // 将窗体显示
		clien.connect(); // 调用连接方法
	}
}

当一台机器上安装了多个网络应用程序时,很可能指定的端口号已被占用。使用命令 netstat -an来查看该程序所使用的的端口。

UDP程序设计基础

基于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包。

.

正常情况下,receive()方法将阻塞,一直等到网络上有数据传来

DatagramPacket类

DatagramPacket类用来表示数据包。
在发送数据时,必须指定接收方的Socket地址和端口好,因此使用构造方法:

DatagramPacket(byte[] buf,int length,InetAddress address,int port)

可创建 发送数据 的DatagramPacket对象。

DatagramSocket类

DatagramSocket类用于表示发送和接受数据包的套接字。
接收程序时用以下构造函数,指定端口号:

DatagramSocket(int port);

发送程序时,不需指定端口号:

DatagramSocket();

UDP网络程序

广播数据包是一种较新的技术,类似于电台广播

import java.awt.*;
import java.awt.event.*;
import java.net.*;

import javax.swing.*;

public class Receive extends JFrame implements Runnable, ActionListener {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	int port; // 定义int型变量
	InetAddress group = null; // 声明InetAddress对象
	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; // 创建Thread对象
	boolean b = false; // 创建boolean型变量
	
	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(); // 创建Jpane对象
		north.add(ince); // 将按钮添加到面板north上
		north.add(stop);
		add(north, BorderLayout.NORTH); // 将north放置在窗体的上部
		JPanel center = new JPanel(); // 创建面板对象center
		center.setLayout(new GridLayout(1, 2)); // 设置面板布局
		center.add(inceAr); // 将文本域添加到面板上

		final JScrollPane scrollPane = new JScrollPane();
		center.add(scrollPane);
		scrollPane.setViewportView(inced);
		add(center, BorderLayout.CENTER); // 设置面板布局
		validate(); // 刷新
		port = 9898; // 设置端口号
		try {
			group = InetAddress.getByName("224.255.10.0"); // 指定接收地址
			socket = new MulticastSocket(port); // 绑定多点广播套接字
			socket.joinGroup(group); // 加入广播组
		} catch (Exception e) {
			e.printStackTrace(); // 输出异常信息
		}
		setBounds(100, 50, 360, 380); // 设置布局
		setVisible(true); // 将窗体设置为显示状态
	}
	
	public void run() { // run()方法
		while (true) {
			byte data[] = new byte[1024]; // 创建byte数组
			DatagramPacket packet = null; // 创建DatagramPacket对象
			// 待接收的数据包
			packet = new DatagramPacket(data, data.length, group, port);
			try {
				socket.receive(packet); // 接收数据包
				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) { // 当变量等于true时,退出循环
				break;
			}
		}
	}
	
	public void actionPerformed(ActionEvent e) { // 单击事件
		if (e.getSource() == ince) { // 单击按钮ince触发的事件
			ince.setBackground(Color.red); // 设置按钮颜色
			stop.setBackground(Color.yellow);
			if (!(thread.isAlive())) { // 如线程不处于“新建状态”
				thread = new Thread(this); // 实例化Thread对象
			}
			thread.start(); // 启动线程
			b = false; // 设置变量值
		}
		if (e.getSource() == stop) { // 单击按钮stop触发的事件
			ince.setBackground(Color.yellow); // 设置按钮颜色
			stop.setBackground(Color.red);
			b = true; // 设置变量值s
		}
	}
	
	public static void main(String[] args) { // 主方法
		Receive rec = new Receive(); // 创建本类对象
		rec.setSize(460, 200); // 设置窗体大小
	}
}
import java.net.*;

public class Weather extends Thread { // 创建类。该类为多线程执行程序
	String weather = "节目预报:八点有大型晚会,请收听";
	int port = 9898; // 定义端口
	InetAddress iaddress = null; // 创建InetAddress对象
	MulticastSocket socket = null; // 声明多点广播套接字
	
	Weather() { // 构造方法
		try {
			// 实例化InetAddress,指定地址
			iaddress = InetAddress.getByName("224.255.10.0");
			socket = new MulticastSocket(port); // 实例化多点广播套接字
			socket.setTimeToLive(1); // 指定发送范围是本地网络
			socket.joinGroup(iaddress); // 加入广播组
		} catch (Exception e) {
			e.printStackTrace(); // 输出异常信息
		}
	}
	
	public void run() { // run()方法
		while (true) {
			DatagramPacket packet = null; // 声明DatagramPacket对象
			byte data[] = weather.getBytes(); // 声明字节数组
			// 将数据打包
			packet = new DatagramPacket(data, data.length, iaddress, port);
			System.out.println(new String(data)); // 将广播信息输出
			try {
				socket.send(packet); // 发送数据
				sleep(3000); // 线程休眠
			} catch (Exception e) {
				e.printStackTrace(); // 输出异常信息
			}
		}
	}
	
	public static void main(String[] args) { // 主方法
		Weather w = new Weather(); // 创建本类对象
		w.start(); // 启动线程
	}
}

实践与练习

  1. 获得指定端口的主机名、主机地址和本机地址
  2. 实现创建一个在8001端口上等待的ServerSocket对象,当接收到一个客户机的连接请求后,程序从与客户机建立了连接的Socket对象中获得输入/输出流。通过输出流向客户机发送信息。
  3. 编写聊天室程序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值