二十五、网络通信基础
1. 网络程序设计基础
局域网与广域网
-
局域网(Local Area Network,LAN)是指在某一区域内由多台计算机互联成的计算机组。一般是方圆几千米以内。局域网可以实现文件管理、应用软件共享、打印机共享、工作组内的日程安排、电子邮件和传真通信服务等功能。局域网是封闭型的,可以由办公室内的两台计算机组成,也可以由一个公司内的上千台计算机组成。
-
广域网(Wide Area Network,WAN),又称广域网、外网、公网。是连接不同地区局域网或城域网计算机通信的远程网。通常跨接很大的物理范围,所覆盖的范围从几十公里到几千公里,它能连接多个地区、城市和国家,或横跨几个洲并能提供远距离通信,形成国际性的远程网络。广域网并不等同于互联网。
网络协议
- 网络协议规定了计算机之间连接的物理、机械(网线与网卡的连接规定)、电气(有效的电平范围)等特征,计算机之间的互相寻址规则,数据发送冲突的解决方式,长数据如何分段传送与接收等内容。就像不同的国家有不同的法律一样,目前网络协议主要的有TCP协议和UDP协议。
(1)TCP协议
-
TCP协议是一种以固接连线为基础的协议,它提供两台计算机间可靠的数据传送。TCP可以保证从一端数据送至连接的另一端,数据能够确实送达,而且抵达的数据的排列顺序和送出时的排列顺序相同。因此TCP协议适合可靠性要求比较高的场合。就像拨打电话,必须先拨号给对方,等两端确定连接后,相互才能听到对方说话,也知道对方回应什么。
- HTTP、FTP和Telnet等都需要使用可靠的通信频道。例如,HTTP从某个URL读取数据时,如果收到的数据顺序与发送时不相同,可能会出现一个混乱的HTML文件或是一些无效的信息。
(2)UDP协议
-
UDP协议是无连接通信协议,不保证数据的可靠传输,但能够像若干个目标发送数据,或接收来自若干个源的数据。UDP以独立发送数据包的方式进行。这种方式就像邮递员送信给收信人,可以寄出很多封信给同一个人,且每一封信都是相对独立的,各封信送达的顺序并不重要,收信人接收信件的顺序也不能保证与寄出信件的顺序相同。
-
UDP协议适合一些对数据准确性要求不高但对传输速度和时效性要求非常高的网站,如网络聊天室、在线影片和直播等。这是由于TCP协议在认证上存在额外耗费,可能使传输速度减慢,而UDP协议即使有一小部分数据包遗失或传送顺序有所不同,也不会严重危害该项通信。
端口和套接字
端口
- 一般而言,一台计算机只有单一的连到网络的物理连接,所有的数据都通过此连接对内、对外送达特定的计算机,这就是端口。网络程序设计中的端口并非真实的物理存在,而是一个假想的连接装置。端口被规定为一个在0~65535之间的整数。
-
通常,0~1023之间的端口数用于一些知名的网络服务和应用,用户的普通网络应用程序应该使用1024及以上的端口数,以避免端口号与另外一个应用或服务系统所用端口冲突。
套接字
-
网络程序中的套接字(Socket)用于将应用程序与端口连接起来。套接字是一个假想的连接装置,就像插座一样可连接电器与电线,我们只需创建Socket类对象,即可使用套接字。
2. TCP程序设计基础
-
TCP网络程序需要服务器和客户端,而且必须先有服务器,服务器先启动,等待客户端的接入,客户端申请连接,待服务器响应后则完成了连接。
-
服务器和客户端是单独分开的,需要建立客户端套接字和服务器套接字。
-
TCP程序设计需要遵循一定的流程,首先创建服务器套接字,然后创建客户端套接字,建立完成后服务器开启监听,服务器和客户端可以同时处理数据,必须要先启动服务器,然后启动客户端。
下面看一个简单的TCP网络程序
- 首先创建一个服务器,然后创建一个客户端,服务器等待客户端的接入。当客户端接入服务器后,客户端和服务器通过输入/输出流互相给对方发消息:
服务器源代码:
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server
{
public static void main(String[] args)
{
try {
ServerSocket server = new ServerSocket(1100);
System.out.println("服务器启动成功,等待用户接入......");
Socket client=server.accept();
System.out.println("有客户端接入,客户端IP:"+client.getInetAddress());
InputStream in=client.getInputStream();
byte[] bt=new byte[1024];
int len=in.read(bt);
String data=new String(bt,0,len);
System.out.println("客户端发来消息:"+data);
OutputStream out=client.getOutputStream();
String message="收到,这里是服务器。";
out.write(message.getBytes());
client.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
客户端源代码:
package lianxi1;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
public static void main(String[] args) {
try {
Socket client = new Socket("127.0.0.1", 1100);
System.out.println("连接成功");
OutputStream out=client.getOutputStream();
String message="服务器你好,我是客户端。";
out.write(message.getBytes());
InputStream in=client.getInputStream();
byte[] bt=new byte[1024];
int len=in.read(bt);
String data=new String(bt,0,len);
System.out.println("服务器发来消息:"+data);
client.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行结果:
3. UDP程序设计基础
- 用户数据报协议(UDP)是网络信息传输的另一种形式。基于UDP的通信和基于TCP的通信不同,基于UDP的信息传递更快,但不提供可靠的保证。使用UDP传递数据时,用户无法知道数据能否正确地到达主机,也不能确定到达目的地的顺序是否和发送的顺序相同。虽然UDP是一种不可靠的协议,但如果需要较快地传输信息,并能容忍小的错误,可以考虑使用UDP。
-
使用UDP协议通信需要数据包类(DatagramPacket)和数据包套接字(DatagramSocket)。
- 根据前面的知识,下面创建一个广播数据报程序。广播数据报是一项较新的技术,其原理类似于电台广播。
先编写发消息端的程序,也就是广播数据报程序:
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Sender extends Thread
{
int port=9898;
InetAddress group;
MulticastSocket socket;
public Sender()
{
try {
group=InetAddress.getByName("224.255.10.0");
try {
socket=new MulticastSocket(port);
socket.joinGroup(group);
} catch (IOException e) {
e.printStackTrace();
}
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
public void run()
{
while(true) {
DatagramPacket packet;
Date date = new Date();
SimpleDateFormat sf = new SimpleDateFormat("HH:mm:ss");
String massege = "[" + sf.format(date) + "]天气预报:当前天气,有雨。";
byte data[] = massege.getBytes();
packet = new DatagramPacket(data, data.length, group, port);//创建数据包
System.out.println(massege);
try {
socket.send(packet);
} catch (IOException e) {
e.printStackTrace();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args)
{
Sender send=new Sender();
send.start();
}
}
运行结果:
- 由运算结果可以看到,此时广播数据报程序已经编写完毕,广播端正在播报天气消息,且一秒一更新。
接下来编写接收端的程序:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;
import java.net.UnknownHostException;
public class Receive extends JFrame implements Runnable, ActionListener
{
JButton ince=new JButton("开始接收");
JButton stop=new JButton("停止接收");
JTextArea inceAr=new JTextArea(10,10);
JTextArea inced=new JTextArea(10,10);
Thread thread;
boolean getMessage=true;
int port=9898;
InetAddress group;
MulticastSocket socket;
public Receive()
{
super("广播数据报");
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
inceAr.setForeground(Color.blue);
JPanel north=new JPanel();
north.add(ince);
north.add(stop);
add(north,BorderLayout.NORTH);
JPanel center=new JPanel();
center.setLayout(new GridLayout(1,2));
center.add(inceAr);
final JScrollPane scrollPane=new JScrollPane();
center.add(scrollPane);
scrollPane.setViewportView(inced);
add(center,BorderLayout.CENTER);
thread=new Thread(this);
ince.addActionListener(this);
stop.addActionListener(this);
validate();//重新验证容器中的组件,即刷新组件
setBounds(100,50,360,380);//设置布局
setVisible(true);//将窗体设置为显示状态
try {
group=InetAddress.getByName("224.255.10.0");
try {
socket=new MulticastSocket(port);
socket.joinGroup(group);
} catch (IOException e) {
e.printStackTrace();
}
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
public static void main(String[] args)
{
Receive rec=new Receive();
rec.setSize(460,200);
}
public void actionPerformed(ActionEvent e)
{
if(e.getSource()==ince)
{
ince.setBackground(Color.red);
stop.setBackground(Color.yellow);
if(!thread.isAlive())
{
thread=new Thread(this);
getMessage=true;
}
thread.start();
}
if(e.getSource()==stop) {
ince.setBackground(Color.yellow);
stop.setBackground(Color.red);
getMessage = false;
}
}
public void run()
{
while (getMessage)
{
DatagramPacket packet;
byte data[]=new byte[1024];
packet=new DatagramPacket(data,data.length,group,port);
try {
socket.receive(packet);//读取数据包
String message=new String(packet.getData(),0,packet.getLength());
inceAr.setText("正在接收的内容:"+message);
inced.append(message+"\n");
}catch (IOException e)
{
e.printStackTrace();
}
}
}
}
运行结果:
- 由运行结果可以看到,接收端接收到了广播数据报信息。