19网络通信
19.1网络程序设计基础
- 网络程序设计是指编写与其他计算机进行通信的程序
19.1.1局域网与因特网
- Local Area Network,LAN 局域网 一群通过一定形式连接起来的计算机
- Wide Area Network,WAN 由LAN延伸到更大范围
- Internet 由无数LAN和WAN组成
19.1.2网络协议
网络协议规定了计算机之间连接的物理、机械、电子等特征以及计算机之间的相互寻址规则、数据发送冲突的解决、长的数据如何分段传送与接受等。
- IP协议
- Internet Protocol,网络协议。在Internet网上存在数以亿计的计算机,每一台主机在网络上通过为其分配的Internet地址表示自己,这个地址就是IP地址。
- IP地址占四个字节,32位二进制数,称为IPv4
- TCP/IP 分4层,应用层、传输层、互联网层和主机到网络层,每层实现特定的功能,提供特定的服务和访问接口,并具有相对的独立性
- TCP与UDP协议
- TCP (Transmission Comtrol) 传输控制协议,以固线连接为基础,数据抵达的排列顺序与发送顺序相同,适用于可靠性要求比较高的场合。HTTP\FTP\Telnet等场合
- UDP(User Datagram Protocol) 用户数据报协议,无连接通信协议,不保证可靠数据传输,也不保证传输顺序,适用于准确性要求不高的场合,如网络聊天室、在线影片等。由于TCP协议在认证上存在额外耗费,有可能使传输速度变慢。UDP协议更适合对传输速度和时效要求非常高的网站。
19.1.3端口和套接字
- 端口,一般而言是指计算机与网络的唯一的物理连接,网线插口。
- 网络程序设计的端口是一个假想的连接装置,通过不同的端口确定连接到服务器的哪项服务上
- 端口被规定位一个在0-65535之间的整数
- 套接字(Socket)将应用程序与端口连接起来。假想的连接装置。java将套接字抽象为类,创建Socket类对象,即可使用套接字
19.2TCP程序设计基础
- TCP网络程序设计是指利用Socket类编写通信程序。
- 服务器程序ServerSocket
- 客户机程序Socket
19.2.1InetAddress类
-
与IP地址相关的类
-
package com.InternetCommunication; import java.io.*; import java.net.*; public class Address { public static void main(String[] args) { InetAddress ip; try{ ip=InetAddress.getLocalHost();//实例化对象 String localname=ip.getHostName(); String localip=ip.getHostAddress(); System.out.println("本机名:"+localname); System.out.println("本机名:"+localip); }catch (UnknownHostException e){//UnknownHostException // 主机不存在或网络连接错误时发生 e.printStackTrace(); } } }
19.2.2ServerSocket类
-
表示服务器套接字
-
主要功能:等待来自网络上的“请求”,可通过指定的端口来等待连接的套接字,依次可以与一个套接字连接
-
如果多台客户机同时提出连接请求,服务器套接字会将请求连接的客户机存入队列,(队列默认大小为50,大于最大容纳数多余请求被拒绝),然后从中取出一个套接字,与服务器新建的套接字连接起来
-
ServerSocket()
-
ServerSocket(int port)
-
accept()方法会堵塞线程的执行,直到接收到客户的呼叫
-
如果没有客户呼叫,线程也没堵塞,则是程序出了问题,通常是使用了一个还在被其他应用程序占用的端口号,ServerSocket绑定没有成功
19.2.3TCP网络程序
-
单向通信
-
//服务器端 package com.InternetCommunication; 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); System.out.println("服务器套接字已经创建成功"); while(true){ System.out.println("等待客户机的连接"); socket=server.accept(); reader= new BufferedReader(new InputStreamReader(socket.getInputStream()));//三层包装,又称过滤器,可以嵌套,看你想用什么方法,本例用了带缓冲区的字节读入流。 getClientMessage(); } }catch (Exception e){ e.printStackTrace(); } } private void getClientMessage(){ try{ while(true){ 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.InternetCommunication; import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; import javax.swing.*; public class MyClient extends JFrame { private PrintWriter writer; Socket socket; private JTextArea ta=new JTextArea(); private JTextField tf=new JTextField(); Container cc; public MyClient(String title){ super(title); cc=this.getContentPane(); cc.add(ta,"North"); cc.add(tf,"South"); tf.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { writer.println(tf.getText()); ta.append(tf.getText()+'\n'); tf.setText(""); } }); } private void connect(){ ta.append("尝试连接"); try{ socket=new Socket("192.168.124.12", 13422);//实例化Socket对象 writer= new PrintWriter(socket.getOutputStream(),true); ta.append("完成连接"); }catch (Exception e){ e.printStackTrace(); } } public static void main(String[] args) { MyClient Client=new MyClient("向服务器送数据"); Client.setSize(200,200); Client.setVisible((true)); Client.connect();//调用连接方法 } }
netstat -an 可以在命令行中查看端口使用情况
19.3UDP程序设计基础
-
基本模式:
-
将数据打包(称为数据包),然后将数据包发往目的地;
-
接受别人发来的数据包,然后查看数据包
-
UDP程序的步骤
- 使用DatagramSocket()创建一个数据包套接字
- 使用DatagramPacket(byte[] buf,int offset,int length,InetAddress address,int port)创建要发送的数据包
- 使用DatagramSocket类的send()方法发送数据包。
接受数据包
- 使用DatagramSocket(int port)创建数据包套接字,绑定到指定的端口
- 使用DatagramPacket(byte[] buf,int length)创建字节数组来接受数据包
- 使用DatagramPacket类的receive()方法来接受UDP包
-
DatagramPacket类的receive()方法接收数据时没有可接受的数据时方法将阻塞,一直等到网络上有数据到来。
-
如果没有数据到来,receive()方法也没有阻塞,肯定是程序有问题,多半是使用了被其他程序占用的端口号。
-
19.3.1DatagramPacket类
-
构造函数: 表示数据包
-
- DatagramPacket(byte[] buf,int length)//数据包的内存空间和大小 - DatagramPacket(byte[] buf,int length,InetAddress,int port)//数据包的内存空间和大小,数据包的目标地址和端口号,创建了可发送数据的DatagramPacket
19.3.2DatagramSocket类
-
构造函数: 发送和接受数据包的套接字
-
DatagramSocket()//创建数据包套接字,并将其绑定到本地主机上的任何可用端口 DatagramSocket(int port)//创建数据包套接字,并将其绑定到本地主机上的指定端口 DatagramSocket(int port,InetAddress addr)//创建数据包套接字,并将其绑定到指定的本地地址,适用有多块网卡和多个IP的情况
-
接收程序时指定端口号,发送程序时不指定端口号
19.3.3UDP网络程序
-
//广播数据报程序 package com.InternetCommunication; import java.io.IOException; import java.net.*; public class Weather extends Thread{ String weather="节目预报:八点有大型晚会,请收听"; int port=9898; InetAddress iaddress=null; MulticastSocket socket=null; Weather(){ try{ iaddress=InetAddress.getByName("224.255.10.0"); //组播地址用 netsh interface ipv4 show joins命令去命令提示符查找 socket=new MulticastSocket(port); 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); System.out.println(new String(data)); try{ socket.send(packet); sleep(2000); }catch (Exception e){ e.printStackTrace(); } } } public static void main(String[] args) { Weather w=new Weather(); w.start(); } }
-
//接收广播程序 package com.InternetCommunication; import java.io.IOException; import java.net.*; import javax.swing.*; import java.awt.*; import java.lang.*; import java.awt.Container; import java.awt.event.*; import java.awt.event.ActionListener; import java.net.DatagramPacket; import java.net.InetAddress; import java.net.MulticastSocket; 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("广播数据报"); thread = new Thread(this); ince.addActionListener(this); stop.addActionListener(this); 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); center.add(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() { while (true) { byte data[] = new byte[1024]; DatagramPacket packet = null; 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) { break; } } } public void actionPerformed(ActionEvent e) { if (e.getSource() == ince) { ince.setBackground(Color.red); stop.setBackground(Color.yellow); if (!(thread.isAlive())) { thread = new Thread(this); } thread.start(); b = false; } 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); } }