Java--网络编程TCP和UDP

  计算机网络,就是把分布在不同区域的计算机用通信线路连接在一起、可以方便地互相传递信息,共享资源。网络编程,就是直接或间接地通过网络协议与其他计算机进行通讯

网络编程中有两个重要问题:

  1. 如何准确地定位网络上一台或多台主机。
  2. 找到主机后如何可靠高效地进行数据传输。

基础术语

  • IP地址

网络中每台主机都必须有一个唯一的IP地址,IP地址是一个逻辑地址。
英特网上的IP地址具有全球唯一性。
32位,四个字节,常用点分十进制的格式表示。例如:113.45.153.190

  • 协议

为进行网络中的数据交换(通信)而建立的规则、标准或约定。(=语义+语法+规则)。
不同层具有各自不同的协议。

  • 端口   

    在互联网上传输的数据都包含有用来识别目的地的IP地址和端口号
    IP地址用来标识网络上的计算机,而端口号用来指明该计算机上的应用程序
    端口用一个整数型标识符来表示,即端口号。
    端口使用一个16位的数字来表示,它的范围是0~655351024以下的为保留端口

  • 数据封装

一台计算机要发送数据到另一台计算机,数据首先必须打包,打包的过程称为封装
封装就是在数据前面加上特定的协议头部
在每一层传递信息的过程中都会进行一次封装,服务端收到信息后然后进行一层一层的解封装最终得到数据。

ISO七层模型

网络体系结构解决异质性问题采用的是分层的方法——把复杂的网络互联问题划分为若干个较小的、单一的问题,在不同层上予以解决,各层之间是严格的单向依赖。

  • OSI(Open System Interconnection)参考模型7层:

  • OSI各层所使用的协议:

应用层:Telnet、FTP、HTTP、DNS、SMTP、POP3
传输层:TCP、UDP
网络层:IP、ICMP、IGMP

详细讲解可查看博客:http://blog.csdn.net/yaopeng_2005/article/details/7064869

TCP/UDP

套接字Socket,是连接运行在网络上的两个程序间的双向通讯的端点。网络通信其实就是Socket间的通信。即,数据在两个Socket间通过IO传输

UDP

  • UDP支持更简单的、快速的、点对点的数据报模式

将数据及源和目的封装成数据包中 不需要建立连接
无连接,即是不可靠协议速度快
协议并不保证数据报是否能正确地到达目的地
每个数据报的大小在限制在64KB内

  • 实例一 、简单的接收和发送数据DEMO
  1. 发送数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.InetAddress;

    /**
    * 通过udp协议发送数据
    * Created by xiaoxiaomo on 2016/4/12.
    *
    */
    public class UdpSendSimpleDemo {

    /**
    * 1:获取socket对象
    * 2:对数据进行封包
    * 3:通过socket对象把数据包发送出去
    * 4:把连接关闭
    * @param args
    */
    public static void main(String[] args) throws IOException {

    //1. 获取socket对象
    DatagramSocket ds = new DatagramSocket();

    //2. 封包数据
    String sendMsg = "UDP ......你好!" ;

    /**
    * @param buf 字节流数据包
    * @param length 流数据包长度 eg,InetAddress.getByName("192.168.3.102")
    * @param address 发送地址
    * @param port 端口号
    */
    DatagramPacket p =
    new DatagramPacket(sendMsg.getBytes(),sendMsg.getBytes().length,InetAddress.getLocalHost(),3000);

    //3. 发送数据包
    ds.send(p);

    //4. 关闭连接
    ds.close();
    }
    }
  2. 接送数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;

    /**
    * UDP接收端代码
    * Created by xiaoxiaomo on 2016/4/12.
    *
    */
    public class UdpReceiveSimpleDemo {
    /**
    * 1:获取socket连接(需要指定监听的端口)
    * 2:通过receive方法接收数据包
    * 3:解包,获取数据包中的内容
    * 4:关闭连接
    * @param args
    * @throws Exception
    */
    public static void main(String[] args) throws IOException {
    //1:获取连接
    DatagramSocket ds = new DatagramSocket(3000);;

    //2:接收数据
    byte[] buf = new byte[1024];
    DatagramPacket p = new DatagramPacket(buf, 0, buf.length);
    ds.receive(p);

    //3:获取数据
    System.out.println( "信息来源:"+p.getAddress().getHostAddress() );
    System.out.println( "收到数据:" + new String( p.getData(),0,p.getLength()) );

    //4:关闭连接
    ds.close();
    }
    }
  3. 运行结果

    1
    2
    3
    4
    如果先运行UdpSendSimpleDemo没有任何信息
    如果先运行UdpReceiveSimpleDemo会输出:
    信息来源:192.168.3.102
    收到数据:UDP ......你好!
  • 实例二 、相对复杂的实例

发送数据后,如果不成功会继续发送4次,都不成功则表示失败。
接收端收到信息后,会返回消息告诉发送者已成功,发送者收到数据会进行相应的校验。

  1. 发送数据:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    import java.io.IOException;
    import java.io.InterruptedIOException;
    import java.net.*;

    /**
    * 通过udp协议发送数据
    * Created by xiaoxiaomo on 2016/4/12.
    *
    */
    public class UdpSendDemo {
    private static final int TIMEOUT = 3000; //设置接收数据的超时时间
    private static final int MAXNUM = 5; //设置重发数据的最多次数
    /**
    * 1:获取socket对象
    * 2:对数据进行封包
    * 3:通过socket对象把数据包发送出去
    * 4:把连接关闭
    * @param args
    */
    public static void main(String[] args) {

    //1. 获取socket对象
    DatagramSocket ds = null;
    try {
    ds = new DatagramSocket(9000); //如果只是用于发送不填写端口
    } catch (SocketException e) {
    System.out.println("创建一个socket对象失败!");
    }

    //2. 封包数据
    String sendMsg = "UDP ......你好!" ;

    DatagramPacket p = null;
    try {
    p = new DatagramPacket(sendMsg.getBytes(),sendMsg.getBytes().length,InetAddress.getLocalHost(),3000);
    } catch (UnknownHostException e) {
    System.out.println("找不到该hosts所对应的地址!");
    }

    //3. 发送数据包
    boolean isReceived = false; //是否接收到数据的标志位
    int tries = 0; //重发数据的次数
    try {
    ds.setSoTimeout(TIMEOUT); //设置超时时间
    //直到接收到数据,或者重发次数达到预定值,则退出循环
    while(!isReceived && tries<MAXNUM) {

    ds.send(p); //发送数据

    try{
    //定义用来接收数据的DatagramPacket实例
    DatagramPacket dp_receive = new DatagramPacket(new byte[1024], 1024);
    //接收从服务端发送回来的数据
    ds.receive(dp_receive);
    //如果接收到的数据不是来自目标地址,则抛出异常
    if(!dp_receive.getAddress().equals( InetAddress.getLocalHost() )){
    throw new IOException("地址出错");
    }
    //如果接收到数据。则将receivedResponse标志位改为true,从而退出循环
    isReceived = true;
    }catch(InterruptedIOException e){
    //如果接收数据时阻塞超时,重发并减少一次重发的次数
    tries += 1;
    System.out.println("Time out,次数:" + tries );
    }
    }
    } catch (IOException e) {
    System.out.println("发送数据失败!");
    }


    if(isReceived){
    //成功
    System.out.println("发送成功!");
    }else{
    //失败
    System.out.println("重发信息"+tries+"次后,失败!");
    }


    //4. 关闭连接
    if( ds != null ){
    ds.close();
    }
    }

    }
  2. 接收数据

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    import java.io.IOException;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;
    import java.net.SocketException;

    /**
    * UDP接收端代码
    * Created by xiaoxiaomo on 2016/4/12.
    *
    */
    public class UdpReceiveDemo {
    /**
    * 1:获取socket连接(需要指定监听的端口)
    * 2:通过receive方法接收数据包
    * 3:解包,获取数据包中的内容
    * 4:关闭连接
    * @param args
    * @throws Exception
    */
    public static void main(String[] args) {
    //1:获取连接
    DatagramSocket ds = null;
    try {
    //在3000端口监听接收到的数据
    ds = new DatagramSocket(3000);
    } catch (SocketException e) {
    //例如,该端口已被占用
    //java.net.BindException: Address already in use: Cannot bind
    System.out.println("创建一个socket对象失败!"+e.getMessage());
    }

    //这些让它一直接收数据
    while( ds != null ){

    //2:接收数据
    byte[] buf = new byte[1024];
    DatagramPacket p = new DatagramPacket(buf, 0, buf.length);
    try {
    ds.receive(p);
    } catch (IOException e) {
    //例如,ds为空等
    System.out.println("接收数据失败!");
    }

    //3:获取数据
    System.out.println( "信息来源:"+p.getAddress().getHostAddress() );
    System.out.println( "收到数据:" + new String( p.getData(),0,p.getLength()) );


    //通知9000端口的客户端数据接收成功
    //数据发动到客户端的
    DatagramPacket dp_reply= new DatagramPacket(p.getData() ,p.getData().length,p.getAddress(),9000);
    try {
    ds.send(dp_reply);
    } catch (IOException e) {
    e.printStackTrace();
    }
    //由于dp_reply在接收了数据之后,其内部消息长度值会变为实际接收的消息的字节数,
    //所以这里要将dp_reply的内部消息长度重新置为1024
    dp_reply.setLength(1024);
    }
    //4:关闭连接
    //ds.close();
    }
    }
  • 运行结果
1
2
3
4
5
6
7
8
9
10
UdpSendDemo先运行,如中途UdpReceiveDemo还未启动则运行结果:
Time out,次数:1
Time out,次数:2
Time out,次数:3
Time out,次数:4
Time out,次数:5
重发信息5次后,失败!
UdpReceiveDemo先运行,UdpSendDemo会收到发送成功!接收者
信息来源:192.168.3.102
收到数据:UDP ......你好!

TCP

  • TCP用于网络的可靠的流式输入/输出,HTTP、FTP、Telnet等应用都需要这种可靠的通信通道。、

两个socket之间必须建立连接形成传输数据的通道
在连接中进行大数据量传输
通过三次握手完成连接 是可靠协议效率会稍低

  • 事例一、简单的TCP实例
  1. TCP发送端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    import java.io.OutputStream;
    import java.net.Socket;

    /**
    * TCP发送端
    * Created by xiaoxiaomo on 2016/4/12.
    */
    public class TcpSendSimpleDemo {
    /**
    * 1:创建socker客户端,需要连接到接收端
    * 2:获取这个socket的输出流
    * 3:通过输出流给其他服务器发送数据
    * 4:关键连接
    * @param args
    * @throws Exception
    */
    public static void main(String[] args) throws Exception {
    //1:获取socker对象
    Socket socket = new Socket("192.168.3.102",6000);

    //2:获取输出流
    OutputStream outputStream = socket.getOutputStream();

    //3:通过输出流写数据
    outputStream.write("TCP ... 你好!".getBytes());

    //4:关闭连接
    socket.close();
    }
    }
  2. TCP接收端

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    import java.io.InputStream;
    import java.net.ServerSocket;
    import java.net.Socket;

    /**
    * TCP接收端
    * Created by xiaoxiaomo on 2016/4/12.
    */
    public class TcpReceiveSimpleDemo {
    /**
    * 1:创建一个socket服务端,需要监听指定端口
    * 2:通过这个服务端对象可以获取到给指定端口发送数据的socket客户端对象
    * 3:通过socket对象获取具体的读取流
    * 4:通过读取流获取数据
    * 5:关闭连接
    * @param args
    * @throws Exception
    */
    public static void main(String[] args) throws Exception {
    //1:获取ServerSocket
    ServerSocket serverSocket = new ServerSocket(6000);

    //2:获取客户端的socket对象
    //是一个阻塞方法,获取socket客户端对象
    Socket socket = serverSocket.accept();

    //3:获取socket的输入流
    InputStream in = socket.getInputStream();
    byte[] bytes = new byte[1024];

    //4:读取数据
    int read = in.read(bytes);
    System.out.println(new String(bytes, 0, read));

    //5:关闭连接
    socket.close();
    serverSocket.close();
    }
    }

TCP/IP模型

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器