【Socket入门】从零开始实现TCP和UDP简单的网络通信

本文是看完慕课网这组视频的学习笔记:https://www.imooc.com/learn/161

简介:

    不同主机主机的应用程序要进行网络通信,则需要指定主机的IP地址以及程序所对应的端口号,端口号是用于区分同一主机上的不同程序。概括来说:Socket=IP地址+端口号,Socket是网络上程序双向通信链路的终结点,是TCP和UDP通信的基础。

常用的端口号:

http:80,ftp:21,telnet:23

Java中提供网络功能的四大类:

1.InetAddress:用于标识网络上的硬件资源

2.URL:统一资源定位符,用于直接读取或写入网络上的数据

3.Socket:使用TCP协议实现网络通信的Sockets相关类。

4.Datagram:使用UDP协议,将数据存在数据报中进行网络通信。

一、InetAddress:

    没有构造方法,无法直接new一个对象进行使用,包含一些静态的方法,可以直接使用静态方法获取到实例。

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;

public class InetAddressTest {
    public static void main(String[] args) throws UnknownHostException {
        //通过静态方法获取本机的InetAddress实例
        InetAddress address = InetAddress.getLoopbackAddress();
        System.out.println("计算机名:"+address.getHostName());
        System.out.println("IP地址:"+address.getHostAddress());
        //获取字节数组形式的IP地址
        byte[] addressBytes = address.getAddress();
        System.out.println("字节数组形式的IP:"+ Arrays.toString(addressBytes));
        System.out.println(address);
        //根据主机名获取InetAddress实例
        InetAddress address2 = InetAddress.getByName("localhost");
        System.out.println("计算机名:"+address2.getHostName());
        System.out.println("IP地址:"+address2.getHostAddress());
        //根据主机IP获取InetAddress实例
        InetAddress address3 = InetAddress.getByName("127.0.0.1");
        System.out.println("计算机名:"+address3.getHostName());
        System.out.println("IP地址:"+address3.getHostAddress());
    }
}

二、URL:

    url用于标识网络上资源的地址,由协议名称和资源名称两部分组成,两部分之间由冒号隔开,如:http://www.baidu.com。http表示协议名称,www.baidu.com表示资源名称。

URL的常用方法:

import java.net.MalformedURLException;
import java.net.URL;

public class URLTest {
    public static void main(String[] args) {
        try {
            URL baidu = new URL("http://www.baidu,com");
            //通过已存在的实例创建新实例,第一个参数是实例名称,第二个参数是传递参数的名称
            URL url = new URL(baidu,"/index.html?test=111");
            System.out.println("协议:"+url.getProtocol());
            System.out.println("主机信息:"+url.getHost());
            //如未指定端口号,使用默认端口号,此时getPort方法的返回值为-1
            System.out.println("端口号:"+url.getPort());
            System.out.println("文件路径:"+url.getFile());
            System.out.println("相对路径:"+url.getRef());
            System.out.println("查询字符串:"+url.getQuery());
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }
}

使用URL获取网络上的资源:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;

public class URLLoadWebPageInfo {
    //创建一个URL实例
    public static void main(String[] args) {
        try {
            URL url = new URL("http://www.baidu.com");
            //通过openStream方法获取URL所表示资源的字节输入流
            InputStream is = url.openStream();
            //将字节输入流转换为字符输入流,并指定编码为utf-8
            InputStreamReader isr = new InputStreamReader(is,"utf-8");
            //为字符输入流添加缓冲
            BufferedReader br = new BufferedReader(isr);
            String data = br.readLine();
            //循环读取数据
            while (data!=null){
                System.out.println(data);
                data = br.readLine();
            }
            br.close();
            isr.close();
            is.close();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

三、使用Socket实现TCP网络通信:

    TCP是面向连接的、有序的、可靠的以字节流发送数据的通信方式。Java网络编程中客户端主要用到Socket类,服务器端主要用到serverSocket类。

Socket通信实现步骤:

1.创建Socket和serverSocket

扫描二维码关注公众号,回复: 11019711 查看本文章

2.打开连接到Socket的输入/输出流

3.按照协议对Socket进行读/写操作

4.关闭输入输出流,关闭Socket。

Socket的TCP通信服务器端代码实现步骤:

1.创建ServerSocket对象,绑定监听端口。

2.通过accept()方法监听客户端请求

3.连接建立后,通过输入流获取到客户端的请求信息

4.通过输出流向客户端发送响应信息

5.关闭相关资源

Socket的TCP通信客户端代码实现步骤:

1.创建Socket对象,指定需要连接的服务器地址和端口号。

2.连接建立后,通过输出流向服务器端发送请求信息

3.通过输入流获取服务器端的响应信息

4.关闭相关资源

服务器端代码:

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

/**
 * 基于TCP协议的网络通信,实现用户登录
 * 服务器端
 */
public class ServerSocketTest {
    public static void main(String[] args) {
        try {
            //1、创建一个服务器端Socket,即ServerSocket,指定端口进行绑定和监听
            ServerSocket serverSocket = new ServerSocket(8888);
            //2.调用accept()方法开始监听,等待客户端的链接
            System.out.println("服务器即将启动等待客户端的连接");
            Socket socket = serverSocket.accept();
            //3.获取输入流,并读取客户端信息
            InputStream is = socket.getInputStream();
            //将字节流变为字符流
            InputStreamReader isr = new InputStreamReader(is);
            //为输入流添加缓冲
            BufferedReader br = new BufferedReader(isr);
            String data = null;
            //循环读取客户端信息
            while((data = br.readLine())!=null){
                System.out.println("我是服务器端,客户端提交信息:"+data);
            }
            socket.shutdownInput();
            //4.获取输出流,相应客户端的请求
            OutputStream os = socket.getOutputStream();
            //包装为打印流
            PrintWriter pw = new PrintWriter(os);
            pw.write("服务器端欢迎客户端");
            pw.flush();//调用flush方法将缓冲输出
            //5.关闭相关资源
            pw.close();
            os.close();
            br.close();
            isr.close();
            is.close();
            socket.close();
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客户端代码:

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

public class ClientSocketTest {
    public static void main(String[] args) {
        try {
            //1.创建客户端Socket,指定服务器地址和端口
            Socket socket = new Socket("localhost",8888);
            //2.获取输出流,向服务器端发送信息
            OutputStream os = socket.getOutputStream();
            //将输出流包装为打印流
            PrintWriter pw = new PrintWriter(os);
            pw.write("用户名:admin;密码:123");
            pw.flush();
            socket.shutdownOutput();
            //3.获取输入流,用来读取服务器端的相应信息
            InputStream is = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String data = null;
            while((data = br.readLine())!=null){
                System.out.println("我是客户端,服务器端提交信息:"+data);
            }
            //4.关闭相关资源
            is.close();
            br.close();
            pw.close();
            os.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

测试时,需要先执行服务器端代码再执行客户端的代码。

服务器与多个客户端进行TCP通信:

应用多线程技术实现服务器与多个客户端之间的通信。

实现步骤:

1.创建ServerSocket对象,循环调用accept()方法等待客户端的链接。

2.客户端创建一个Socket,并请求和服务器端连接。

3.服务器端接收客户端请求,创建Socket与该客户端简历专线连接。

4.建立连接的两个socket在一个单独的线程上对话。

5.服务器等待新的链接

在上面的代码基础上修改。

服务器端代码:

服务器线程

package internet;

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

/**
 * 服务器端线程处理类
 */
public class ServerThread extends Thread{
    //和本线程相关的Socket
    Socket socket = null;

    public ServerThread(Socket socket) {
        this.socket = socket;
    }

    //响应客户端的请求

    @Override
    public void run() {
        super.run();
        InputStream is = null;
        InputStreamReader isr = null;
        BufferedReader br = null;
        OutputStream os = null;
        PrintWriter pw = null;
        try {
            //获取输入流,并读取客户端信息
            is = socket.getInputStream();
            //将字节流变为字符流
            isr = new InputStreamReader(is);
            //为输入流添加缓冲
           br = new BufferedReader(isr);
            String data = null;
            //循环读取客户端信息
            while((data = br.readLine())!=null){
                System.out.println("我是服务器端,客户端提交信息:"+data);
            }
            socket.shutdownInput();
            //获取输出流,相应客户端的请求
            os = socket.getOutputStream();
            //包装为打印流
            pw = new PrintWriter(os);
            pw.write("服务器端欢迎客户端");
            pw.flush();//调用flush方法将缓冲输出
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //关闭相关资源
            try {
                if(pw!=null)
                    pw.close();
                if(os!=null)
                    os.close();
                if(br!=null)
                    br.close();
                if(isr!=null)
                    isr.close();
                if(is!=null)
                    is.close();
                if(socket!=null)
                    socket.close();
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    }
}

服务器端代码:

package internet;

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

/**
 * 基于TCP协议的网络通信,实现用户登录
 * 服务器端
 */
public class ServerSocketTest {
    public static void main(String[] args) {
        try {
            //1、创建一个服务器端Socket,即ServerSocket,指定端口进行绑定和监听
            ServerSocket serverSocket = new ServerSocket(8888);
            System.out.println("服务器即将启动等待客户端的连接");
            Socket socket = null;
            //记录客户端的数量
            int count = 0;
            //循环监听等待客户端的连接
            while(true){
                //2.调用accept()方法开始监听,等待客户端的链接
                socket = serverSocket.accept();
                //创建一个新线程
                ServerThread serverThread = new ServerThread(socket);
                //启动线程
                serverThread.start();
                count++;
                System.out.println("客户端的数量为:"+count);
                InetAddress inetAddress = socket.getInetAddress();
                System.out.println("当前客户端的IP:"+inetAddress.getHostAddress());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客户端代码不变:

package internet;

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

public class ClientSocketTest {
    public static void main(String[] args) {
        try {
            //1.创建客户端Socket,指定服务器地址和端口
            Socket socket = new Socket("localhost",8888);
            //2.获取输出流,向服务器端发送信息
            OutputStream os = socket.getOutputStream();
            //将输出流包装为打印流
            PrintWriter pw = new PrintWriter(os);
            pw.write("用户名:admin;密码:123");
            pw.flush();
            socket.shutdownOutput();
            //3.获取输入流,用来读取服务器端的相应信息
            InputStream is = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String data = null;
            while((data = br.readLine())!=null){
                System.out.println("我是客户端,服务器端提交信息:"+data);
            }
            //4.关闭相关资源
            is.close();
            br.close();
            pw.close();
            os.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

四、使用Datagram数据报相关类实现UDP网络通信:

    UDP协议是无连接、不可靠、无序的,速度相对较快。以数据报作为数据传输的载体。数据传输时,首先将需要传输的数据定义为数据报,在数据报中指明需要达到的Socket(主机地址加端口号),然后再将数据发送出去。编程主要使用到DatagramPacket数据报包类和DatagramSocket进行端到端通信的类。

服务器端实现步骤:

1.创建DatagramSocket,指定端口号

2.创建DatagramPacket

3.接收客户端发送的信息

4.读取信息

客户器端实现步骤:

1.定义发送信息

2.创建DatagramPacket,包含要发送的信息

3.创建DatagramSocket

4.发送数据

服务器端代码:

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

public class UdpServer {
    public static void main(String[] args) throws IOException {
        //1.创建服务器端,DatagramSocket,指定端口
        DatagramSocket datagramSocket = new DatagramSocket(8800);
        //2.创建数据报,用于接受客户端发送的数据
        byte[] data = new byte[1024];//创建字节数组,指定接收的数据包大小
        DatagramPacket datagramPacket = new DatagramPacket(data,data.length);
        //3.接收客户端发送的数组
        System.out.println("服务器端已经启动等待客户端发送数据");
        datagramSocket.receive(datagramPacket);//此方法在接收到数据报之前一直阻塞
        //4.读取数据
        String info = new String(data,0,datagramPacket.getLength());
        System.out.println("我是服务器端,客户端说"+info);
        /**
         * 服务器端向客户端发送响应数据
         */
        //1.定义客户端的地址、端口号和响应的数据
        InetAddress address = datagramPacket.getAddress();
        int port = datagramPacket.getPort();
        byte[] data2 = "服务器端欢迎客户端访问".getBytes();
        //2.创建数据报包含响应信息
        DatagramPacket datagramPacket2 = new DatagramPacket(data2,data2.length,address,port);
        //3.响应客户端
        datagramSocket.send(datagramPacket2);
        //4.关闭资源信息
        datagramSocket.close();
    }
}

客户端代码:

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

public class UdpClient {
    public static void main(String[] args) throws IOException {
        //1.定义服务器地址,端口号,需要发送的数据
        InetAddress address = InetAddress.getByName("localhost");
        int port = 8800;
        byte[] data = "用户名:admin;密码:123".getBytes();
        //2.创建数据报,包含发送的信息,四个参数,发送的数据;数据长度;目标地址信息;端口号
        DatagramPacket datagramPacket = new DatagramPacket(data,data.length,address,port);
        //3.创建DatagramSocket对象
        DatagramSocket datagramSocket = new DatagramSocket();
        //4.向服务器发送数据
        datagramSocket.send(datagramPacket);
        /**
         * 客户端接收服务器的响应信息
         */
        //1.创建数据报,用于接收服务器端的响应信息
        byte[] data2 = new byte[1024];
        DatagramPacket datagramPacket2 = new DatagramPacket(data2,data2.length);
        //2.接收服务器端的响应数据
        datagramSocket.receive(datagramPacket2);
        //3.读取服务器端响应的数据信息
        String reply = new String(data2,0,datagramPacket2.getLength());
        System.out.println("我是客户端,服务器端的响应信息为:"+reply);
        //4.关闭资源
        datagramSocket.close();
    }
}

服务器与多个客户端进行UDP通信:

服务器代码:

服务器线程代码:

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

public class UdpServerThread extends Thread {
    private DatagramSocket datagramSocket;
    private DatagramPacket datagramPacket;
    private String info;
    private InetAddress address;
    private int port;

    public UdpServerThread(DatagramSocket datagramSocket, DatagramPacket datagramPacket) {
        this.datagramSocket = datagramSocket;
        this.datagramPacket = datagramPacket;
        this.info = new String(datagramPacket.getData(), 0, datagramPacket.getLength());
        this.address=datagramPacket.getAddress();
        this.port=datagramPacket.getPort();
    }

    @Override
    public void run() {
        super.run();
        //接收客户端发送的数组
        try {
            //读取数据
            System.out.println("我是服务器端,客户端说"+info);
            /**
             * 服务器端向客户端发送响应数据
             */
            byte[] data2 = "服务器端欢迎客户端访问".getBytes();
            //2.创建数据报包含响应信息
            DatagramPacket datagramPacket2 = new DatagramPacket(data2,data2.length,address,port);
            //3.响应客户端
            datagramSocket.send(datagramPacket2);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //4.关闭资源信息
//            if(datagramSocket!=null)
//                datagramSocket.close();
        }
    }
}

服务器代码:

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

public class UdpServer {
    public static void main(String[] args) throws IOException {
        //1.创建服务器端,DatagramSocket,指定端口
        DatagramSocket datagramSocket = new DatagramSocket(8800);
        byte[] data = new byte[1024];//创建字节数组,指定接收的数据包大小
        DatagramPacket datagramPacket = new DatagramPacket(data,data.length);
        System.out.println("服务器端已经启动等待客户端发送数据");
        int count = 0;
        while(true){
            datagramSocket.receive(datagramPacket);//此方法在接收到数据报之前一直阻塞
            UdpServerThread udpServerThread =new UdpServerThread(datagramSocket,datagramPacket);
            udpServerThread.start();
            count++;
            System.out.println("客户端的数量为:"+count);
            InetAddress inetAddress = datagramPacket.getAddress();
            System.out.println("当前客户端的IP:"+inetAddress.getHostAddress());
        }
    }
}

客户端代码不变:

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

public class UdpClient {
    public static void main(String[] args) throws IOException {
        //1.定义服务器地址,端口号,需要发送的数据
        InetAddress address = InetAddress.getByName("localhost");
        int port = 8800;
        byte[] data = "用户名:admin;密码:123".getBytes();
        //2.创建数据报,包含发送的信息,四个参数,发送的数据;数据长度;目标地址信息;端口号
        DatagramPacket datagramPacket = new DatagramPacket(data,data.length,address,port);
        //3.创建DatagramSocket对象
        DatagramSocket datagramSocket = new DatagramSocket();
        //4.向服务器发送数据
        datagramSocket.send(datagramPacket);
        /**
         * 客户端接收服务器的响应信息
         */
        //1.创建数据报,用于接收服务器端的响应信息
        byte[] data2 = new byte[1024];
        DatagramPacket datagramPacket2 = new DatagramPacket(data2,data2.length);
        //2.接收服务器端的响应数据
        datagramSocket.receive(datagramPacket2);
        //3.读取服务器端响应的数据信息
        String reply = new String(data2,0,datagramPacket2.getLength());
        System.out.println("我是客户端,服务器端的响应信息为:"+reply);
        //4.关闭资源
        datagramSocket.close();
    }
}
发布了54 篇原创文章 · 获赞 11 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/baidu_35800355/article/details/105135333