网络编程一:概述、网络模型、TCP\UDP、Socket套接字、UDP发送和接收、UDP聊、阻塞式方法

时间:2021-03-12 10:22:17

1  网络编程概述

网络编程就是通过使用套接字(Socket来达到进程间通信目的的编程,在两个设备之间进行数据交换。

在网络中的连接,是基于请求/响应的方式,就是发起连接请求的程序(Client客户端),发送连接请求到接收请求的程序(Server服务端),客户端可以在需要时才启动,而服务端需要一直运行,一直侦听请求。

 

网络模型:

      OSI七层模型从下往上:物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。

      TCP/IP四层模型从下往上:网络接入层(主机-网络)、网际互联层、传输层、应用层。

网络编程一:概述、网络模型、TCP\UDP、Socket套接字、UDP发送和接收、UDP聊、阻塞式方法

   常用概念:

   IP地址:IP地址是网络中设备的标识,是一台主机在网络中的地址。127.0.0.1是本地回环地址。

端口号:数据要发送到对方主机上指定的应用程序上,为了标识这些应用程序,所以给这些网络应用程序都用数字进行标识。为了方便称呼这个数字,叫做端口(逻辑端口),是不同进程的标识。

有效端口:0~65535,其中0~1024系统使用或保留端口。

传输协议:我们定义了网络中的通信规则,这个通信规则称为协议。国际组织定义了通用协议TCP/IP。 

 

TCP/IP模型中,

应用层协议有HTTPFTP协议,

传输层有TCPUDP协议,

网际层有IP协议,

网络接入层有ARP协议。


2  IP地址InetAddress

java.net包中的InetAddress类:此类表示互联网协议 (IP) 地址。

 

InetAddress类中常用方法:

getLocalHost():静态方法,获取本地主机的IP地址对象,返回InetAddress类型。

getByName():静态方法,根据一个主机名或IP地址,返回一个InetAddress对象。

getAllByName():静态方法,根据一个主机名,返回其IP地址组成的数组,InetAddress[]

getHostAddress():获取InetAddress对象的主机IP地址,返回字符串类型。

getHostName():获取InetAddress对象的主机名,返回字符串类型。

代码演示:

import java.net.*;

class IPDemo {
public static void main(String[] args) throws Exception {
//获取一个InetAddress对象
InetAddress i = InetAddress.getLocalHost(); //获取本地主机的IP地址对象。

System.out.println(i.toString());
System.out.println("address: "+i.getHostAddress());//返回主机IP地址
System.out.println("name: "+i.getHostName());//返回主机名

//根据IP或主机名,获取一个InetAddress对象
InetAddress ia = InetAddress.getByName("192.168.0.103");
System.out.println("罗琦HostAddress: "+ia.getHostAddress());
System.out.println("罗琦HostName: "+ia.getHostName());

//根据IP或主机名,获取一个InetAddress对象。
InetAddress[] ib = InetAddress.getAllByName("www.baidu.com");
for(InetAddress ibb : ib){
System.out.println("百度HostAddress: "+ibb.getHostAddress());
System.out.println("百度HostName: "+ia.getHostName());
}
}
}

3  TCPUDPSocket(套接字)

TCPUDP是两种不同的传输层协议,TCP即传输控制协议,UDP即用户数据报协议。

 

TCP特点:

1,建立连接,形成传输数据的通道。

2,在连接中进行大数据量传输。

3,通过三次握手完成连接,是可靠协议。

4,必须建立连接,效率会稍低。

 

UDP特点:

1,将数据及源和目的封装成数据包中,不需要建立连接。

2,每个数据报的大小在限制在64k内。

3,因无连接,是不可靠协议。

4,不需要建立连接,速度快。

 

Socket即套接字,是进程间通信的机制,网络上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket英文原意为插座,在Socket通信机制中,一台主机就像一个插座,每个服务对应的端口就是一个插孔,连接到不同的端口,就对应不同的服务。

Socket特点:

1Socket就是为网络服务提供的一种机制。

2,通信的两端都有Socket

3,网络通信其实就是Socket间的通信。

4,数据在两个Socket间通过IO传输。

 

4  UDP发送端实例——DatagramSocket类、DatagramPacket

定义一个UDP协议的发送端。

需求:通过UDP传输方式,将一段文字数据发送出去。

思路:

1,建立UDPsocket服务。

2,提供数据,并将数据封装到数据包中。

3,通过socket服务的发送功能,将数据包发出去。

4,关闭资源。

 

DatagramSocket类,是UDP协议中用于发送和接收数据包的套接字。Java.net包中。

DatagramSocket( int)构造方法,创建一个Socket并绑定到本地主机的指定端口。

发送时,通过构造函数指定要发送到的端口。

接收时,通过构造函数指定要接收哪个端口上的数据包。

send( dp)方法:从这个Socket发送数据包。

receive( dp)方法:从这个Socket接收数据包,阻塞式方法。

 

DatagramPacket类,表示数据包。Java.net包中的。

getAddress()DatagramPacket类的getAddress方法返回数据包中IP地址,InetAddress对象。

getData():获取数据包中的数据,返回字节数组。

getLength():返回数据包中数据的长度,字节数组长度。

getPort():返回端口号,数据包发送到这个端口,或从这个端口发出。

 

代码示例:

import java.net.*;

class UdpSend { //创建服务、IP地址均会出现异常,send()有IO异常
public static void main(String[] args) throws Exception{

//1,创建udp的socket服务。通过DatagramSocket对象。
DatagramSocket ds = new DatagramSocket(); //套接字绑定到本地任意可用端口。

//2,确定数据,并封装成数据包。
//DatagramPacket(byte[] buf, int length, InetAddress address, int port)
byte[] buf = "udp ge men lai la".getBytes();
DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("127.0.0.1"),10000);

//3,通过socket服务,将已有的数据包发送出去,通过send方法。
ds.send(dp);

//4,关闭资源。
ds.close();
}
}

5  UDP接收端实例、阻塞式方法

定义一个UDP协议的接收端。

需求:定义一个应用程序,用于接收UDP协议传输的数据并处理的。

思路:

1,创建UDPSocket对象。通常会监听一个端口,其实就是给这个接收应用程序,定义一个数字标识(端口)。方便于明确哪些数据过来该应用程序可以处理。

2,创建一个数据包,因为要存储接收到的字节数据。因为数据包对象中有更多功能,可以提取字节数据中的不同数据信息。

3,通过Socket对象的receive方法,将收到的数据存入已定义好的数据包中。

4,通过数据包对象的特有功能,将这些不同的数据取出,打印在控制台上。

5,关闭资源。

 

DatagramSocketreceive( dp)是阻塞式方法。

阻塞式方法:程序一直停在这里等,直到接收到数据才继续执行。IO流中的输入流的read方法也是阻塞式方法。

 

代码示例:

import java.net.*;

class UdpReceive{
public static void main(String[] args) throws Exception{
//1,创建udp的socket服务,指定端口。
DatagramSocket ds = new DatagramSocket(10000);//监听10000端口,如果不指定,端口随机分配

//2,定义数据包,用于存储接收到的数据。
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf,buf.length);//数据包存储buf字节数组

//3,通过服务的receive方法,将收到的数据存入数据包中。
ds.receive(dp); //阻塞式方法:程序一直停在这里等,接收到数据才继续执行。

//4,通过数据包的方法,获取其中的数据。(IP地址、端口、数据)
String ip = dp.getAddress().getHostAddress();//getAddress()返回主机的InetAddress对象(即IP地址)
//getHostAddress()返回InetAddress对象的主机ip
String data = new String(dp.getData(),0,dp.getLength());//得到数据包中封装的数据。

int port = dp.getPort(); //得到端口。
System.out.println(ip+"::"+data+"::"+port); //打印出来

//5,关闭资源。
ds.close();
}
}

6  完整的UDP传输实例(键盘录入方式)

使用键盘录入的方式,实现UDP数据传输。

发送端通过键盘,每录入一些数据,接收端就接收一些数据。再发送,再接收。

两个包含主函数的类在一个java文件中,先统一编译,再分别执行。

即:javac UdpDemo2.java

java UdpSend2

java UdpReceive2

 

代码示例:

import java.net.*; //用到net包往往会用到io包,网络传输用到IO流 
import java.io.*;

class UdpSend2 {
public static void main(String[] args) throws Exception{
DatagramSocket ds = new DatagramSocket(); //创建Socket服务

BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in));//读取键盘录入

String line = null;
while((line=bufr.readLine())!=null){ //readLine阻塞式方法
if(line.equals("886"))
break;
byte[] buf = line.getBytes();

//创建一个发送的数据包,其中定义了数据、数据长度、目的ip、目的端口。
DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("127.0.0.1"),10001);//本机IP

ds.send(dp); //Socket服务把数据包dp发送出去,udp传输。
}
ds.close(); //关闭Socket资源。
}
}

class UdpReceive2 {
public static void main(String[] args) throws Exception{
DatagramSocket ds = new DatagramSocket(10001);//绑定10001端口

while(true){
byte[] buf = new byte[1024*64]; //数据包最大64KB

//创建一个存储接收数据的数据包,定义了数据、数据长度。
DatagramPacket dp = new DatagramPacket(buf,buf.length);

ds.receive(dp); //阻塞式方法,Socket接收数据并存入dp数据包中。

String ip = dp.getAddress().getHostAddress(); //获取IP地址
String data = new String(dp.getData(),0,dp.getLength());

System.out.println(ip+"::"+data);
}
//没有关闭资源,演示阻塞式方法。
}
}

7  UDP聊天程序

需求:编写一个聊天程序,这一个程序中有接收数据的部分,也有发送数据的部分。这两部分需要同时执行,需要用多线程技术。

思路:

1,需要用到多线程技术。一个线程控制收,一个线程控制发。

2,因为收和发动作是不一致的,所以要定义两个run方法。

3,而且这两个方法要封装到不同的类中。

 

为了方便演示阻塞式方法,此例中没有关闭资源DatagramSocket

代码示例:

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

class Send implements Runnable {//实现Runnable接口,多线程的方式之一。
private DatagramSocket ds;
public Send(DatagramSocket ds){
this.ds = ds;
}
public void run(){
try{
BufferedReader bufr =
new BufferedReader(new InputStreamReader(System.in)); //读取键盘录入

String line = null;
while((line=bufr.readLine())!=null){ //readLine()阻塞式方法
if(line.equals("886"))
break;
byte[] buf = line.getBytes();

//创建一个发送的数据包,其中定义了数据、数据长度、目的ip、目的端口。
DatagramPacket dp =
new DatagramPacket(buf,buf.length,InetAddress.getByName("127.0.0.1"),10002);

ds.send(dp); //Socket服务把数据包dp发送出去,udp传输方式。
}
}
catch(Exception e){
throw new RuntimeException("发送端失败");
}
}
}

class Receive implements Runnable{
private DatagramSocket ds;
public Receive(DatagramSocket ds){
this.ds = ds;
}
public void run(){
try{
while(true){
byte[] buf = new byte[1024*64];
DatagramPacket dp = new DatagramPacket(buf,buf.length);//创建一个存储接收数据的数据包

ds.receive(dp); //阻塞式方法,Socket接收数据并存入dp数据包中。

String ip = dp.getAddress().getHostAddress();
String data = new String(dp.getData(),0,dp.getLength());

System.out.println(ip+"::"+data);
}
}
catch(Exception e){
throw new RuntimeException("接收端失败");
}
}
}

class ChatDemo{
public static void main(String[] args) throws Exception{//抛出监听端口时的Socket异常
DatagramSocket sendSocket = new DatagramSocket();
DatagramSocket receSocket = new DatagramSocket(10002);

new Thread(new Send(sendSocket)).start(); //启动线程
new Thread(new Receive(receSocket)).start();
}
}