java实现socket网络编程

1、网络编程原理:

网络编程两大步骤
一、如何定位网络上的一台或者多台主机:
网络层主要负责网路主机的定位,数据传输传输的路由,由ip地址可以唯一的确定internet上的一台主机。

二、如何在定位之后进行数据的传输;
在传输层则提供面向应用的可靠(TCP)或者非可靠(UDP)的数据传输机制

对于客户端/服务器(C/S)结构。 即通信双方一方作为服务器等待客户提出请求并予以响应。
客户则在需要服务时向服务器提出申请。服务器一般作为守护进程始终运行,监听网络端口,
一旦有客户请求,就会启动一个服务进程来响应该客户,同时自己继续监听服务端口,使后来的
客户也能及时得到服务。

对于浏览器/服务器(B/S)结构。 客户则在需要服务时向服务器进行请求。服务器响应后及时返回,
不需要实时监听端口。

2、TCP协议与UDP协议

Tcp协议是一种面向链接的保证数据可靠传输的协议,得到的是一个顺序的无差错对的数据流,发送方和接收方创建socket连接的方式是,
服务端开启服务等待,等待客户端发送链接,创建链接后双方可进行发送或者接收操作。

UDP协议是一种无连接协议,每个数据包都是一个独立的信息,包括完整的源地址或目的地址,它在网络上以任何可能的路径传往目的地,
因此能否到达目的地,到达目的地的时间以及内容的正确性都是不能被保证的。

3、什么是socket网络编程?

在网络编程中,网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
Socket套接字是通信的对象,它支持TCP/IP协议的网络通信。它是网络通信过程中端点的抽象表示,
包含进行网络通信必须的五种信息:连接使用的协议,本地主机的IP地址,本地进程的协议端口,远地主机的IP地址,
远地进程的协议端口。

Socket本质是编程接口(API),对TCP/IP协议进行封装提供借口给程序员使用,这就是Socket编程接口;

4、java实现socket网络编程

服务端使用ServerSocket绑定IP和端口,使用Accept监听端口是否有客户端发送连接请求,一旦有客户端发送连接请求,
服务端就回送连接信息,正式建立连接。Server端和Client端都可以通过Send,Write等方法与对方通信。

对于一个功能齐全的Socket,都要包含以下基本结构,其工作过程包含以下四个基本的步骤:
1、创建Socket;
2、打开连接到Socket的输入/出流;
3、按照一定的协议对Socket进行读/写操作;
4、关闭Socket。

5、代码实现

基于TCP的socket实现

服务端

public class TcpSocketServer {

  public static void main(String[] args) {
        try {
            // 创建服务端socket 绑定端口
            ServerSocket serverSocket = new ServerSocket();
            //绑定ip
            serverSocket = new ServerSocket(8088, 10, InetAddress.getByName("192.168.0.110"));
            // 创建客户端socket 用户下面接收客户端socket对象
            Socket socket = new Socket();
            System.out.println("等待客户端连接...");
            //循环监听等待客户端的连接
            while(true){
                // 监听客户端  没有接受到数据才会停在此处 接受到往下执行
                socket = serverSocket.accept();
               //发送内容实现线程的创建
                ServerThread thread = new ServerThread(socket);  
                thread.start();
                //获取客户端的ip
                InetAddress address=socket.getInetAddress();
                System.out.println("当前链接的客户端的IP:"+address.getHostAddress());
            }
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
}
    
}

服务端执行线程

public class ServerThread extends Thread{

    private Socket socket = null;

    public ServerThread(Socket socket) {

        this.socket = socket;
    }
    @Override
    public void run() {
        InputStream is=null;
        InputStreamReader isr=null;
        BufferedReader br=null;
        OutputStream os=null;
//                * PW支持两个直接对文件写操作的构造方法:
//                * PrintWriter(File f)传文件名
//                * PrintWriter(String s)传路径
//                * PrintWriter给人一种可以直接对文件进行操作的假象
//                * PW是一个高级流
//                * 实际上PW包装了字节流、字符流和字符缓冲流。
//                * PW负责自动行刷新
//                * bw负责提高效率
//                * osw负责读字符
//                * fos负责写字节
//                * 最后PW能够按行很快地把一个字符串变成字节写在文件中
        PrintWriter pw=null;
        try {
            is = socket.getInputStream();
            isr = new InputStreamReader(is);
            br = new BufferedReader(isr);
            String info = null;
            while((info=br.readLine())!=null){
                System.out.println("客户端:"+info);
            }
            //非关闭连接 仅关闭一方的发送状况
            socket.shutdownInput();
            os = socket.getOutputStream();
            pw = new PrintWriter(os);
            pw.write("服务器欢迎你1");
            pw.flush();
        } catch (Exception e) {
            // TODO: handle exception
        } 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();
            }
        }
    }
}

客户端

public class TcpSocketClient {
    public static void client() throws InterruptedException {
        try {
            // 和服务器创建连接
            Socket socket = new Socket("192.168.0.111", 8088);
            // 要发送给服务器的信息
            OutputStream os = socket.getOutputStream();
            PrintWriter pw = new PrintWriter(os);
            pw.write("订单状态以改变");
            //flush方法是用于将输出流缓冲的数据全部写到目的地。
            //所以一定要在关闭close之前进行flush处理,即使PrintWriter有自动的flush清空功能
            pw.flush();
            socket.shutdownOutput();
            // 从服务器接收的信息
            InputStream is = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String info = null;
            while ((info = br.readLine()) != null) {
                System.out.println("我是客户端,服务器返回信息:" + info);
            }
            br.close();
            is.close();
            os.close();
            pw.close();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在执行顺序上,如果先执行了客户端会直接请求,但是因为服务端还没有执行监听端口等待连接,客户端会出现连接异常或者超时
所以最好先执行服务端在执行客户端。

6、UDP实现

服务端:

public class UdpSocketServer {
   DatagramSocket socket = null;

public static void main(String[] args) {
        try {
            // 要接收的报文
            byte[] bytes = new byte[1024];
            DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
            // 创建socket并指定端口 ip
    InetSocketAddress isa = new InetSocketAddress("192.168.0.110", 8088);
    socket = new DatagramSocket(isa);
            // 接收socket客户端发送的数据
            while(true) {
                //如果没有收到会一致阻塞
                socket.receive(packet);
                String receiveMsg = new String(packet.getData(), 0, packet.getLength());
                System.out.println("消息长度" + packet.getLength());
                System.out.println("客户端:" + receiveMsg);
                msg=receiveMsg;
            }
            // 关闭socket
//            socket.close();
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }

}

客户端

public class UdpSocketClient {
    public static void Client(String ip,String msg) {
        try {
            // 要发送的消息
            String sendMsg = msg;
            System.out.println(sendMsg);
            // 获取服务器的地址  得到本地主机地址
            InetAddress addr = InetAddress.getByName(ip);
            // 创建packet包对象,封装要发送的包数据和服务器地址和端口号
            DatagramPacket packet = new DatagramPacket(sendMsg.getBytes(),
                    sendMsg.getBytes().length, addr, 8088);
            // 创建Socket对象
            DatagramSocket socket = new DatagramSocket();
            // 发送消息到服务器
            socket.send(packet);
            System.out.println("继续发送");
            // 关闭socket
            socket.close();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}

socket传输文件

因为TCP的可靠性这里选用Tcp进行传输,传输原理,连接建立后,客户端发送请求传输的文件名或者绝对路径+文件名到服务端,
服务端就会根据文件名或者绝对路径创建文件对象,判断文件是否存在,存在则以字节或者字符形式传输至客户端,客户端在指定目录保存

客户端:

public class FileTcpSocketClient {
//image文件名
    public static void Imageclient(String image) throws InterruptedException {

        try {
            // 和服务器创建连接
            Socket socket = new Socket("192.168.0.106", 8089);
            // ===================发送给服务器的信息=================
            OutputStream os = socket.getOutputStream();
            PrintWriter pw = new PrintWriter(os);
            pw.write(image);
            //flush方法是用于将输出流缓冲的数据全部写到目的地。
            //所以一定要在关闭close之前进行flush处理,即使PrintWriter有自动的flush清空功能
            pw.flush();
            socket.shutdownOutput();

            // ========================从服务器接收文件====================
            InputStream is = socket.getInputStream();
            DataInputStream dis = new DataInputStream(is);
            FileOutputStream fos=null;
            //文件名
            String fileName = dis.readUTF();
            System.out.println("文件名"+fileName);
            //文件长度
            long fileLength = dis.readLong();
            System.out.println("文件大小"+fileLength);
         //下面三行是保存的路径  可以自己设置
            String staticPath = ClassUtils.getDefaultClassLoader().getResource("static/img").getPath();
            String path1=staticPath.substring(1, staticPath.length());
            System.out.println(path1);
            //指定保存目录  判断是否存在在 不存在则创建
            File directory = new File(path1);
            if(!directory.exists()) {
                directory.mkdir();
            }
            //与系统有关的默认名称分隔符。此字段被初始化为包含系统属性 file.separator 值的第一个字符。在 UNIX 系统上,此字段的值为 '/';在 Microsoft Windows 系统上,它为 '\'。
            //directory.getAbsolutePath() 获取指定目录绝对路径
            File file = new File(directory.getAbsolutePath() + File.separatorChar + fileName);
            if(!file.exists()) {
                fos = new FileOutputStream(file);
                // 开始接收文件
                byte[] bytes = new byte[1024];
                int length = 0;
                while ((length = dis.read(bytes, 0, bytes.length)) != -1) {
                    fos.write(bytes, 0, length);
                    fos.flush();
                }

                System.out.println("======== 文件接收成功 [File Name:" + fileName + "]");
            }
            is.close();
            os.close();
            pw.close();
            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

服务端:

public class FileTcpSocketServer extends Thread{

    @Override
    public void run() {
        try {
            // 创建服务端socket
            ServerSocket serverSocket = new ServerSocket(); 
           //绑定ip 端口
            serverSocket.bind(new InetSocketAddress("192.168.0.106",8089));
//            serverSocket = new ServerSocket(8089, 3, InetAddress.getByName("127.0.0.1"));
            // 创建客户端socket
            Socket socket = new Socket();
            System.out.println("等待客户端连接...");
            //循环监听等待客户端的连接
            while(true){
                // 监听客户端  没有接受到数据才会停在此处
                socket = serverSocket.accept();
               FileThread thread = new FileThread(socket);
                thread.start();
                //获取客户端的ip
                InetAddress address=socket.getInetAddress();
                System.out.println("当前链接的客户端的IP:"+address.getHostAddress());
            }
        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }
}

文件传输线程

public class FileThread extends Thread{

    private Socket socket = null;

    public FileThread(Socket socket) {

        this.socket = socket;
    }
    @Override
    public void run() {
        InputStream is=null;
        InputStreamReader isr=null;
        BufferedReader br=null;
        OutputStream os=null;
         FileInputStream fis=null;
         DataOutputStream dos=null;
        String fileName=null;
//                * PW支持两个直接对文件写操作的构造方法:
//                * PrintWriter(File f)传文件名
//                * PrintWriter(String s)传路径
//                * PrintWriter给人一种可以直接对文件进行操作的假象
//                * PW是一个高级流
//                * 实际上PW包装了字节流、字符流和字符缓冲流。
//                * PW负责自动行刷新
//                * bw负责提高效率
//                * osw负责读字符
//                * fos负责写字节
//                * 最后PW能够按行很快地把一个字符串变成字节写在文件中
        PrintWriter pw=null;
        try {
            is = socket.getInputStream();
            isr = new InputStreamReader(is);
            br = new BufferedReader(isr);

            String info = null;

            while((info=br.readLine())!=null){
                System.out.println("客户端:"+info);
                fileName=info;
            }
            //非关闭连接 仅关闭一方的发送状况
            socket.shutdownInput();
            os = socket.getOutputStream();
            pw = new PrintWriter(os);
            System.out.println(fileName);
//下面三行设置查找文件路径
            String staticPath = ClassUtils.getDefaultClassLoader().getResource("static/img").getPath();
            String path1=staticPath.substring(1, staticPath.length());
            System.out.println(path1+fileName);

            File file=new File(path1+fileName);
            String responseMsg="请求的文件不存在";
            System.out.println("7");
            if(!file.exists())
            {
                System.out.println("不存在");
                pw.write(responseMsg);
                pw.flush();
            }else {
                System.out.println("存在");
                fis = new FileInputStream(file);
                // 文件名和长度
                dos = new DataOutputStream(os);
                dos.writeUTF(file.getName());
                dos.flush();
                dos.writeLong(file.length());
                System.out.println(file.length());
                dos.flush();
                // 开始传输文件
                System.out.println("======== 开始传输文件 ========");
                byte[] bytes = new byte[1024];
                int length = 0;
                long progress = 0;
                while ((length = fis.read(bytes, 0, bytes.length)) != -1) {
                    dos.write(bytes, 0, length);
                    dos.flush();
                    progress += length;
                    System.out.print("| " + (100 * progress / file.length()) + "% |");
                }
                System.out.println();
                System.out.println("======== 文件传输成功 ========");
            }

    } catch (Exception e) {
            // TODO: handle exception
        } 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();
            }
        }
    }

}

你可能感兴趣的:(java实现socket网络编程)