网络编程---UDP&TCP详解

在讲协议之前,我们需要先了解Socket(套接字),就是为网络服务提供的一种机制,通信的两端都有Socket,网络通信其实就是Socket之间的通信,数据在两个Socket之间通过IO传输。可以将Socket理解为“插座”,也就是每一台主机都必须得有

UDP

DatagramSocket();        发送和接收数据包的套接字

DatagramPacket();        数据报包,实现无连接包投递服务

建立UDP发送端

思路:1.建立udp的socket服务     2.明确具体发送的数据    3.通过socket服务将具体的数据发送出去    4.关闭服务

代码实现

package UDP;

import java.net.*;

public class UdpSend {
    public static void main(String[] args) throws Exception {
        System.out.println("udp发送端启动");
        //建立udp服务
        DatagramSocket ds = new DatagramSocket();

        //明确数据
        String str = "UDP来了!";

        //发送数据,将数据封装到数据包中,数据包会明确地址和端口
        byte[] buf = str.getBytes();
        DatagramPacket dp = new DatagramPacket(buf, buf.length,
                InetAddress.getByName("localhost"), 12345);

        //发送
        ds.send(dp);

        //关闭服务
        ds.close();
    }
}

此时,发送端就完成发送任务啦~

建立UDP接收端

思路:1.创建socket服务,明确一个端口 (该端口应与发送端的端口保持一致)     2.接收数据(以数据包的形式)     3.将需要的数据取出来      4.关闭资源

代码实现

package UDP;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UdpReceive {
    public static void main(String[] args) throws IOException {
        System.out.println("udp接收端启动");
        //创建接收端服务
        DatagramSocket ds = new DatagramSocket(12345);   //端口对应发送端的端口

        //接收数据
        //接收数据将会是接收所有的数据。而我们需要将这些数据存储在数据包中,
        //然后通过数据包对象的方法对收到的数据进行解析
            //创建数据包
            byte[] buf = new byte[1024];
            DatagramPacket dp = new DatagramPacket(buf, buf.length);
            //接收数据
        ds.receive(dp);

        //对收到的数据进行解析,使用数据包的方法
        String id = dp.getAddress().getHostAddress();
        int port = dp.getPort();
        //获取文字数据
        String str= new String(dp.getData(), 0, dp.getLength());
        System.out.println(id + "---" + port + "---" + str);

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

需要注意:在运行程序时,会出现执行到receive()方法时,程序不再反应了,有可能出现了这两种问题

(1).发送端和接收端执行的先后顺序搞错。因为UDP是不可靠的协议,它不会考虑是否有接受者的问题,先启动发送端,它没有找到相应的接收者,于是数据包就丢失了,正确顺序是:先执行接收端连接发送端端口,再执行发送端

(2).关闭防火墙,可能是数据包被拦截了,receive这边没收到,关掉防火墙就可以了

用UDP实现聊天室

思路架构:

聊天室其实就是在一个用户上既可以发送数据,又可以接收数据,那么,就需要两个类分别代表发送和接收,并且这两个要能够同时进行,此时就需要多线程。

发送:用UDP发送数据,那么就需要通过DatagramSocket类帮助发送,也就是任务对象一建立,就需要socket对象,那么我们可以将DatagramSocket作为成员变量并进行封装。

然后线程中的run()如何实现呢?既然是发送端,那么核心任务就是发送数据了:1.发送的数据是通过键盘进行录入的   2.将数据封装到数据包中    3.将数据包发送出去

接收:和发送一样,接收也同样需要通过DatagramSocket类,创建socket对象

接收数据如何实现?其实原理就和上面的UDP接收端是一样的:1.接收具体的数据内容    2.创建数据包对象     3.将受到的数据存储到数据包中    4.获取数据

但是,这样的话,接收和发送的数据就都只是一次性的,要让它变为不停的来回发送和接收,那么只需要给条件加一个循环就可以了,以下是代码实现:

package UDP;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

public class UdpChat {
    public static void main(String[] args) throws SocketException {
        DatagramSocket send = new DatagramSocket(8888);
        DatagramSocket receive = new DatagramSocket(12345);

        Thread t1 = new Thread(new Send(send));
        Thread t2 = new Thread(new Receive(receive));
        t1.start();
        t2.start();
    }
}

//发送端
class Send implements Runnable {
    private DatagramSocket ds;

    public Send(DatagramSocket ds) {
        super();
        this.ds = ds;
    }

    @Override
    public void run() {
        //键盘录入数据
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        //读取数据
        String line = null;
        try {
            while((line = br.readLine()) != null) {
                if("over".equals(line)) {
                    break;
                }

                //将数据变成字节数组,封装到数据包中
                byte[] buf = line.getBytes();
                DatagramPacket dp = new DatagramPacket(buf, buf.length,
                        InetAddress.getByName("localhost"), 12345);

                //发送数据
                ds.send(dp);
            }

            //不在录入数据,关闭服务
            ds.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

//接收端
class Receive implements Runnable {
    private DatagramSocket ds;

    public Receive(DatagramSocket ds) {
        super();
        this.ds = ds;
    }
    @Override
    public void run() {
        while (true) {
            //创建字节数组,因为使用数据包必须要有字节数组
            byte[] buf = new byte[1024];
            //创建数据包对象
            DatagramPacket dp = new DatagramPacket(buf, buf.length);
            //接收数据
            try {
                ds.receive(dp);
            } catch (IOException e) {
                e.printStackTrace();
            }

            //获取数据
            InetAddress id = dp.getAddress();
            String data = new String(buf, 0, buf.length);

            if("over".equals(data)) {
                System.out.println(id + "离开聊天室...");
            }

            System.out.println(id + ": " + data);
        }

    }
}

执行结果:

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N6eTIzMzM=,size_16,color_FFFFFF,t_70

TCP

和UDP需要发送端和接收端也一样,TCP也要建立客户端和服务端,但是TCP是面向连接的,只有连接成为通路,才会在客户端和服务端之间进行数据的传输,而这个连接过程就被成为三次握手,简易图解如下

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N6eTIzMzM=,size_16,color_FFFFFF,t_70

更深入了解三次握手和四次挥手可以看看转载的这篇文章:https://www.cnblogs.com/laowz/p/6947539.html

建立TCP客户端:

Socket();            客户端的套接字

ServerSocket();           服务端的套接字

思路:

1.建立tcp客户服务

         1.1因为是面向连接,必须有连接才可以进行通信

         1.2在创建客户端时,就必须明确目的地址和端口

2.一旦建立连接,就有了传输数据的通道,就可以在通道中进行数据传输,而这个传输就是通过流实现的,该流就是socket io流

3.只要获取socket io中的写动作就可以将数据写到socket流中发给服务端

4.关闭资源

代码实现:

package TCP;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

public class TcpClient {
    public static void main(String[] args) throws IOException {
        System.out.println("客户端启动...");

        //创建客户端,明确地址和端口
        Socket socket = new Socket("localhost", 10234);

        //获取socket中的输出流,将数据发送给服务端
        OutputStream out = socket.getOutputStream();

        //通过流写数据
        out.write("注意,tcp来啦!".getBytes());

        //关闭资源
        socket.close();   //关闭的时候连同流一起关闭

    }
}

注意:此时运行程序不会出现结果,并且会报错,如图

watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3N6eTIzMzM=,size_16,color_FFFFFF,t_70

原因很简单,因为TCP是面向连接的,没有接收端就不能建立传输通道,自然会报错

建立TCP服务端:

思路:

1.创建socket服务端服务,服务端为了让客户端可以连接上,必须提供端口,佳宁一个端口

2.获取客户端对象,通过客户端的socket流对应的客户端进行通信

3.获取客户端的socket流的读取流

4.读取并显示在服务器端

5.关闭资源

代码实现:

package TCP;

import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

public class TcpClient {
    public static void main(String[] args) throws IOException {
        System.out.println("客户端启动...");

        //创建客户端,明确地址和端口
        Socket socket = new Socket("localhost", 10234);

        //获取socket中的输出流,将数据发送给服务端
        OutputStream out = socket.getOutputStream();

        //通过流写数据
        out.write("注意,tcp来啦!".getBytes());

        //关闭资源
        socket.close();   //关闭的时候连同流一起关闭

    }
}

运行结果:先启动服务端建立连接,在启动客户端发送数据

20190323115240203.png                   20190323115255692.png

TCP客户端服务端互访

上面的例子只是客户端向服务端发送数据,服务端显示,我们觉得不太过瘾,希望实现客户端非服务端发送,服务端也可以给客户端进行回应,下面是代码实现

客户端

package TCP;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class TcpClient2 {
    public static void main(String[] args) throws IOException {
        System.out.println("客户端2启动...");
        //建立客户端socket
        Socket s = new Socket("localhost", 12345);

        //获取输出流,将数据发送给服务端
        OutputStream out = s.getOutputStream();

        //写数据
        out.write("注意,tcp2来啦!".getBytes());

        //接收服务端的回馈
        InputStream in = s.getInputStream();
        byte[] buf = new byte[1024];

        int len = in.read(buf);
        String str = new String(buf, 0, len);
        System.out.println(str);

        s.close();
    }
}

服务端

package TCP;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServer2 {
    public static void main(String[] args) throws IOException {
        System.out.println("服务端2启动...");
        //创建服务端socket
        ServerSocket ss = new ServerSocket(12345);

        //接收客户端的对象
        Socket s = ss.accept();

        //通过对象获得socket流
        InputStream in = s.getInputStream();

        //读取并显示
        String id = s.getInetAddress().getHostAddress();
        byte[] buf = new byte[1024];
        int len = in.read(buf);
        String str = new String(buf, 0, len);
        System.out.println(id + ": " + str);

        //反馈给客户端
        OutputStream out = s.getOutputStream();
        out.write("好的,知道你来啦".getBytes());

        s.close();
        ss.close();
    }
}

运行结果:

20190323123841129.png               20190323123922213.png

TCP上传文本客户端

需求:上传文本到客户端,读取本地文本数据,服务端接收完毕后,回馈“上传成功”字样

在这一块,需要用到大量的IO流知识,关于IO流可以参考文章:https://blog.csdn.net/szy2333/article/details/81531531

客户端

package TCP;

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

public class TcpUploadTextClient {
    public static void main(String[] args) throws IOException {
        System.out.println("上传文本客户端启动...");
        /*
         *上传文本的客户端。读取本地文本数据,发送给服务端,服务端接收完毕后,回馈“上传成功”字样
         */
        //创建客户端socket
        Socket s = new Socket("localhost", 1345);

        //确定数据源,本地文件上传
        BufferedReader bufr = new BufferedReader(new FileReader("client.txt"));

        //确定目的。socket输出流
        //BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
        //和被注释的上面一行代码的效果是一样的
        PrintWriter out = new PrintWriter(s.getOutputStream(), true);

        String line = null;
        while((line = bufr.readLine()) != null) {
            out.println(line);
        }

        //给服务端发送标记,使用socket的禁用输出流的方法
        s.shutdownOutput();

        //接收服务端的反馈
        BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
        String str = bufIn.readLine();
        System.out.println(str);
    }
}

服务端

package TCP;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpUploadTextServer {
    public static void main(String[] args) throws IOException {
        System.out.println("上传文本服务端启动...");
        //创建服务端
        ServerSocket ss = new ServerSocket(1345);

        //获取客户端
        Socket s = ss.accept();

        //获取读取流
        BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));

        //确定目的文件
        PrintWriter pw = new PrintWriter(new FileWriter("server.txt", true));

        //频繁读写
        String line = null;
        while ((line = bufIn.readLine()) != null) {
            pw.println(line);
        }

        //给客户端返回信息
        PrintWriter pwOut = new PrintWriter(s.getOutputStream(), true);
        pwOut.println("上传成功");

        //关闭各种资源
        pw.close();
        s.close();
        ss.close();
    }
}

最后结果就将client.txt文件中的内容上传至服务器,然后通过服务器保存在server.txt中了,两个文件的内容是一样的,所以本地上传文件的本质就是文件复制!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值