二十五、網(wǎng)絡(luò)通信基礎(chǔ)
1. 網(wǎng)絡(luò)程序設(shè)計(jì)基礎(chǔ)
局域網(wǎng)與廣域網(wǎng)
-
局域網(wǎng)(Local Area Network,LAN)是指在某一區(qū)域內(nèi)由多臺(tái)計(jì)算機(jī)互聯(lián)成的計(jì)算機(jī)組返帕。一般是方圓幾千米以內(nèi)丑慎。局域網(wǎng)可以實(shí)現(xiàn)文件管理、應(yīng)用軟件共享誓斥、打印機(jī)共享、工作組內(nèi)的日程安排许帐、電子郵件和傳真通信服務(wù)等功能劳坑。局域網(wǎng)是封閉型的,可以由辦公室內(nèi)的兩臺(tái)計(jì)算機(jī)組成成畦,也可以由一個(gè)公司內(nèi)的上千臺(tái)計(jì)算機(jī)組成泡垃。
-
廣域網(wǎng)(Wide Area Network,WAN)羡鸥,又稱廣域網(wǎng)蔑穴、外網(wǎng)、公網(wǎng)惧浴。是連接不同地區(qū)局域網(wǎng)或城域網(wǎng)計(jì)算機(jī)通信的遠(yuǎn)程網(wǎng)存和。通常跨接很大的物理范圍衷旅,所覆蓋的范圍從幾十公里到幾千公里捐腿,它能連接多個(gè)地區(qū)、城市和國(guó)家柿顶,或橫跨幾個(gè)洲并能提供遠(yuǎn)距離通信茄袖,形成國(guó)際性的遠(yuǎn)程網(wǎng)絡(luò)。廣域網(wǎng)并不等同于互聯(lián)網(wǎng)嘁锯。
網(wǎng)絡(luò)協(xié)議
- 網(wǎng)絡(luò)協(xié)議規(guī)定了計(jì)算機(jī)之間連接的物理宪祥、機(jī)械(網(wǎng)線與網(wǎng)卡的連接規(guī)定)聂薪、電氣(有效的電平范圍)等特征,計(jì)算機(jī)之間的互相尋址規(guī)則蝗羊,數(shù)據(jù)發(fā)送沖突的解決方式藏澳,長(zhǎng)數(shù)據(jù)如何分段傳送與接收等內(nèi)容。就像不同的國(guó)家有不同的法律一樣耀找,目前網(wǎng)絡(luò)協(xié)議主要的有TCP協(xié)議和UDP協(xié)議翔悠。
(1)TCP協(xié)議
-
TCP協(xié)議是一種以固接連線為基礎(chǔ)的協(xié)議,它提供兩臺(tái)計(jì)算機(jī)間可靠的數(shù)據(jù)傳送野芒。TCP可以保證從一端數(shù)據(jù)送至連接的另一端蓄愁,數(shù)據(jù)能夠確實(shí)送達(dá),而且抵達(dá)的數(shù)據(jù)的排列順序和送出時(shí)的排列順序相同狞悲。因此TCP協(xié)議適合可靠性要求比較高的場(chǎng)合撮抓。就像撥打電話,必須先撥號(hào)給對(duì)方效诅,等兩端確定連接后胀滚,相互才能聽到對(duì)方說話趟济,也知道對(duì)方回應(yīng)什么乱投。
- HTTP、FTP和Telnet等都需要使用可靠的通信頻道顷编。例如戚炫,HTTP從某個(gè)URL讀取數(shù)據(jù)時(shí),如果收到的數(shù)據(jù)順序與發(fā)送時(shí)不相同媳纬,可能會(huì)出現(xiàn)一個(gè)混亂的HTML文件或是一些無效的信息双肤。
(2)UDP協(xié)議
-
UDP協(xié)議是無連接通信協(xié)議,不保證數(shù)據(jù)的可靠傳輸钮惠,但能夠像若干個(gè)目標(biāo)發(fā)送數(shù)據(jù)茅糜,或接收來自若干個(gè)源的數(shù)據(jù)。UDP以獨(dú)立發(fā)送數(shù)據(jù)包的方式進(jìn)行素挽。這種方式就像郵遞員送信給收信人蔑赘,可以寄出很多封信給同一個(gè)人,且每一封信都是相對(duì)獨(dú)立的预明,各封信送達(dá)的順序并不重要缩赛,收信人接收信件的順序也不能保證與寄出信件的順序相同。
-
UDP協(xié)議適合一些對(duì)數(shù)據(jù)準(zhǔn)確性要求不高但對(duì)傳輸速度和時(shí)效性要求非常高的網(wǎng)站撰糠,如網(wǎng)絡(luò)聊天室酥馍、在線影片和直播等。這是由于TCP協(xié)議在認(rèn)證上存在額外耗費(fèi)阅酪,可能使傳輸速度減慢旨袒,而UDP協(xié)議即使有一小部分?jǐn)?shù)據(jù)包遺失或傳送順序有所不同汁针,也不會(huì)嚴(yán)重危害該項(xiàng)通信。
端口和套接字
端口
- 一般而言峦失,一臺(tái)計(jì)算機(jī)只有單一的連到網(wǎng)絡(luò)的物理連接扇丛,所有的數(shù)據(jù)都通過此連接對(duì)內(nèi)、對(duì)外送達(dá)特定的計(jì)算機(jī)尉辑,這就是端口帆精。網(wǎng)絡(luò)程序設(shè)計(jì)中的端口并非真實(shí)的物理存在,而是一個(gè)假想的連接裝置隧魄。端口被規(guī)定為一個(gè)在0~65535之間的整數(shù)卓练。
-
通常,0~1023之間的端口數(shù)用于一些知名的網(wǎng)絡(luò)服務(wù)和應(yīng)用购啄,用戶的普通網(wǎng)絡(luò)應(yīng)用程序應(yīng)該使用1024及以上的端口數(shù)襟企,以避免端口號(hào)與另外一個(gè)應(yīng)用或服務(wù)系統(tǒng)所用端口沖突。
套接字
-
網(wǎng)絡(luò)程序中的套接字(Socket)用于將應(yīng)用程序與端口連接起來狮含。套接字是一個(gè)假想的連接裝置顽悼,就像插座一樣可連接電器與電線,我們只需創(chuàng)建Socket類對(duì)象几迄,即可使用套接字蔚龙。
2. TCP程序設(shè)計(jì)基礎(chǔ)
-
TCP網(wǎng)絡(luò)程序需要服務(wù)器和客戶端,而且必須先有服務(wù)器映胁,服務(wù)器先啟動(dòng)木羹,等待客戶端的接入,客戶端申請(qǐng)連接解孙,待服務(wù)器響應(yīng)后則完成了連接坑填。
-
服務(wù)器和客戶端是單獨(dú)分開的,需要建立客戶端套接字和服務(wù)器套接字弛姜。
-
TCP程序設(shè)計(jì)需要遵循一定的流程脐瑰,首先創(chuàng)建服務(wù)器套接字,然后創(chuàng)建客戶端套接字廷臼,建立完成后服務(wù)器開啟監(jiān)聽苍在,服務(wù)器和客戶端可以同時(shí)處理數(shù)據(jù),必須要先啟動(dòng)服務(wù)器中剩,然后啟動(dòng)客戶端忌穿。
下面看一個(gè)簡(jiǎn)單的TCP網(wǎng)絡(luò)程序
- 首先創(chuàng)建一個(gè)服務(wù)器,然后創(chuàng)建一個(gè)客戶端结啼,服務(wù)器等待客戶端的接入掠剑。當(dāng)客戶端接入服務(wù)器后,客戶端和服務(wù)器通過輸入/輸出流互相給對(duì)方發(fā)消息:
服務(wù)器源代碼:
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("服務(wù)器啟動(dòng)成功郊愧,等待用戶接入......");
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("客戶端發(fā)來消息:"+data);
OutputStream out=client.getOutputStream();
String message="收到井佑,這里是服務(wù)器。";
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="服務(wù)器你好眠寿,我是客戶端躬翁。";
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("服務(wù)器發(fā)來消息:"+data);
client.close();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
運(yùn)行結(jié)果:
3. UDP程序設(shè)計(jì)基礎(chǔ)
- 用戶數(shù)據(jù)報(bào)協(xié)議(UDP)是網(wǎng)絡(luò)信息傳輸?shù)牧硪环N形式盒发。基于UDP的通信和基于TCP的通信不同狡逢,基于UDP的信息傳遞更快宁舰,但不提供可靠的保證。使用UDP傳遞數(shù)據(jù)時(shí)奢浑,用戶無法知道數(shù)據(jù)能否正確地到達(dá)主機(jī)蛮艰,也不能確定到達(dá)目的地的順序是否和發(fā)送的順序相同。雖然UDP是一種不可靠的協(xié)議雀彼,但如果需要較快地傳輸信息壤蚜,并能容忍小的錯(cuò)誤,可以考慮使用UDP徊哑。
-
使用UDP協(xié)議通信需要數(shù)據(jù)包類(DatagramPacket)和數(shù)據(jù)包套接字(DatagramSocket)袜刷。
- 根據(jù)前面的知識(shí),下面創(chuàng)建一個(gè)廣播數(shù)據(jù)報(bào)程序实柠。廣播數(shù)據(jù)報(bào)是一項(xiàng)較新的技術(shù)水泉,其原理類似于電臺(tái)廣播善涨。
先編寫發(fā)消息端的程序窒盐,也就是廣播數(shù)據(jù)報(bào)程序:
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) + "]天氣預(yù)報(bào):當(dāng)前天氣,有雨钢拧。";
byte data[] = massege.getBytes();
packet = new DatagramPacket(data, data.length, group, port);//創(chuàng)建數(shù)據(jù)包
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();
}
}
運(yùn)行結(jié)果:
- 由運(yùn)算結(jié)果可以看到蟹漓,此時(shí)廣播數(shù)據(jù)報(bào)程序已經(jīng)編寫完畢,廣播端正在播報(bào)天氣消息源内,且一秒一更新葡粒。
接下來編寫接收端的程序:
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("廣播數(shù)據(jù)報(bào)");
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();//重新驗(yàn)證容器中的組件,即刷新組件
setBounds(100,50,360,380);//設(shè)置布局
setVisible(true);//將窗體設(shè)置為顯示狀態(tài)
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);//讀取數(shù)據(jù)包
String message=new String(packet.getData(),0,packet.getLength());
inceAr.setText("正在接收的內(nèi)容:"+message);
inced.append(message+"\n");
}catch (IOException e)
{
e.printStackTrace();
}
}
}
}
運(yùn)行結(jié)果:
- 由運(yùn)行結(jié)果可以看到膜钓,接收端接收到了廣播數(shù)據(jù)報(bào)信息嗽交。