黑马程序员——Java之网络通信

------Java培训、Android培训、iOS培训、Net培训、期待与您交流! -------

 

一、网络协议

       网络协议规定了计算机之间连接的物理、机械(网线与网卡的链接规定)、电气(有效的电平范围)等特征以及计算机之间的相互寻址规则、数据发送冲突的解决、长的数据如何分段传送与接收等。而常用的网络协议有:TCP/IP协议、TCP与UDP协议。

(1)IP协议

         IP是Internet Protocol的简称,由此知道它是一种“网络协议”。Internet网络采用的协议是TCP/IP协议。在Internet网上存在数以亿计的主机,每一台主机在网络上通过为其分配的Internet地址表示自己,这个地址就是IP地址。到目前为止IP地址用4个字节,也就是32位的二进制数来表示,称为IPv4。为了便于使用,通常取用每个字节的十进制数,并且每个字节之间用圆点隔开来表示IP地址,如192.168.1.1。

        TCP/IP模式是一种层次结构,共分4层,分别为应用层、传输层、互联网层和主机到网络层。各层实现特定的功能,提供特定的服务和访问接口,并具有相对的独立性。

(2)TCP与UDP协议

       在TCP/IP协议栈中,有两个高级协议是网络应用程序是我们应该知道的,即“传输控制协议”(TCP)与“用户数据报协议”(UDP)。

       TCP协议是一种以固接连线为基础的协议,它提供两台计算机剑可靠的数据传送。TCP可以保证数据从一端送至连接的另一端时能够确实送达,而抵达的数据的排列顺序和送出时的顺序相同,因此TCP协议适合可靠性要求比较高的场合。如HTTP、FTP、Telnet等应用层协议都建立在TCP基础上。

       UDP是无连接通信协议,不保证可靠数据的传输,但能够向若干个目标发送数据,接收发自若干个源的数据。UDP是以独立发送数据包的方式进行。UDP协议适合于一些对数据准确性要求不高的场合,如网络聊天室、在线影片等。

      TCP协议与UDP协议缺点:

      TCP协议在认证上存在额外耗费,有可能使传输速度减慢;

      UDP协议可能会更适合这些对传输速度和时效要求非常高的网站,即使有一小部分数据包的遗失或传送顺序有所不同,并不会严重危害该项通信。

 

二、端口与套接字

(1)端口

       一般而言,一台计算机只有单一的连到网络的“物理连接”,所有的数据都通过此连接对内、对外送达特定的计算机,这就是端口。端口被规定为一个在0~65535之间的整数。如HTTP服务一般使用80端口,FTP服务使用21端口。假设一台计算机提供了HTTP、FTP等多种服务,那么客户机通过不同端口来确定连接到服务器的哪项服务上,如图:

(2)套接字

        网络程序中套接字(Socket)用于将应用程序与端口连接起来。Jvav将套接字抽象化为类,程序设定只需要创建Socket类对象,即可使用套接字。套接字是一个假想的连接装置,就是像插头的设备“插座”,用于连接电气与电线,如下图:

 

三、TCP程序设计

       TCP网络程序设计是指利用Scoket类编写通信程序。利用TCP协议进行通信的两个应用程序是有主次之分的,一个称为服务器程序,另一个称为客户机程序。两者的功能和编写方法不大一样。

       服务器端与客户端的交互过程:

       ①服务器程序创建一个ServerSocket(服务器端套接字),调用accept()方法等待客户机来连接;

       ②客户端程序创建一个Socket,请求与服务器建立连接;

       ③服务器接收客户机的连接请求,同时创建一个新的Socket与客户建立连接。服务器继续等待新的请求。

(1)InetAddress类

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

      InetAddress类的常用方法如下:

      ①getByName(String host ):获取与host相对应的InetAddress对象;

      ②getHostAddress():获取InterAddress对象所含的IP地址;

      ③getHostName():获取此IP地址的主机名;

      ④getLocalHost();返回本地主机的InetAddress对象。

示例:使用InetAddress类的getHostName()和getHostAddress()方法获得本地主机名、本机IP地址。

import java.net.InetAddress;
import java.net.UnknownHostException;

public class MyAddress {

	public static void main(String[] args) {
		try {
			//实例化InetAddress对象
			InetAddress ip = InetAddress.getLocalHost();
			//获取本地主机名
			String localname = ip.getHostName();
			//获取本地主机的IP地址
			String localip = ip.getHostAddress();
			//输出本地主机名
			System.out.println("本地主机名:"+localname);
			//输出本地主机的IP地址
			System.out.println("本地IP地址:"+localip);
		} catch (UnknownHostException e) {
			
			e.printStackTrace();
		}
	}
}

运行结果:

本地主机名:WIN~~~~
本地IP地址:192.168.~~

 

(2)ServerSocket类

        java.net包中的ServerSocket类用于表示服务器套接字,其主要功能是等待来自网络上的”请求“,它可以通过指定的端口来等待连接套接字。服务器套接字一次可以与一个套接字连接。如果多台客户机同时提出连接请求,服务器套接字会将请求连接的客户机存入队列中,然后从中取出一个套接字,与服务器新建的套接字连接起来。若请求连接数大于最大容纳数,则多出的连接请求被拒绝。队列的默认大小为50.

       ServerSocket类的构造方法如下:

      ①ServerSocket():创建非绑定服务器套接字;

      ②ServerSocket(int port):创建绑定到特定端口的服务器套接字;

      ③ ServerSocket(int port , int backlog):利用指定的backlog创建服务器套接字并将其绑定到指定的本地端口号。

      ④ServerSocket(int port ,int backlog,InetAddress bindAddress):使用指定的端口、侦听backlog和要绑定到的本地IP地址创建服务器。

     ServerSocket类的常用方法:

     ①accept():等待客户机的连接。若连接,则创建一套接字

     ②isBound():判断ServerSocket的绑定状态

     ③getInetAddress(): 返回此服务器套接字的本地地址

     ④isClosed():返回服务器套接字的关闭状态

     ⑤clos():关闭服务器套接字

     ⑥bind(SocketAddress endpoint):将ServerSocket绑定到特定地址(IP地址和端口号)

示例:

import java.io.*;
import java.net.*;

public class Address { // 创建类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) { // 主方法
		Address tcp = new Address(); // 创建本类对象
		tcp.getserver(); // 调用方法
	}
}

运行结果:


 

四、UDP程序设计

       用户数据包协议(UDP)是网络信息传输的另一种形式。基于UDP的通信和基于TCP的通信不同,基于UDP的信息传递更快,但不提供可靠的保证。使用UDP传递数据时,用户无法知道数据能否正确地到达主机,也不能确定到达目的地的顺序是否和发送的顺序相同。

       基于UDP通信的基本模式如下:

       ①将数据打包(称为数据包),然后将数据包发往目的地;

       ②接收别人发来的数据包,然后查看数据包

       发送数据包:

       ①DatagramSocket()方法:创建一个数据包套接字

       ② DatagramPacket(byte[] buf ,int offset , int length , InetAddress sddress ,int port )方法:创建要发送的数据包

       ③DatagramSocket类的send()方法发送数据包

       接收数据包:

       ①DatagramSocket(int port )方法:创建数据包套接字,绑定到指定的端口;

       ②DatagramPacket(byte[]buf , int length)方法:创建字节数组来接收数据包;

       ③DatagramPacket类的receive()方法接收UDP包。

示例:广播主机程序不断地向外发出广播。

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(); // 启动线程
	}
}<span style="font-size:18px;">       </span>

 运行结果:

节目预报:八点有大型晚会,请收听
节目预报:八点有大型晚会,请收听
节目预报:八点有大型晚会,请收听
....


 

示例:接收广播程序。当单击“开始接收”按钮时,开始接收主机播出的信息;单击“停止接收”按钮时,系统会停止接收广播主机播出的信息。

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

import javax.swing.*;

public class Receive extends JFrame implements Runnable, ActionListener {
	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); // 设置窗体大小
	}
}


运行结果:

     


 


 

 

 

 

      

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值