关于Java网络编程

这两天学了点网络编程皮毛,暂时没用到,先记录下来。
关于java网络编程无非是在网络中传递数据,比如:通过网络传递信息(应用,在线聊天室),在网络中传输对象,在网络中传输文件(应用,传输二进制文件),这些传输都离不开java中关于“流”的使用,各种“流”的搭配使用。另外就是ServerSocket(用在服务器端)与Socket(用在客户端)的使用。

1. ServerSocket的使用讲解

ServerSocket 负责接收客户连接请求,在ServerSocket的构造方法中, 参数 port 指定服务器要绑定的端口( 服务器要监听的端口), 参数 backlog 指定客户连接请求队列的长度, 参数 bindAddr 指定服务器要绑定的IP 地址.

1.1 ServerSocket 的构造方法有以下几种重载形式:

  1. ServerSocket() throws IOException
  2. ServerSocket(int port) throws
  3. ServerSocket(int port, int backlog) throws IOException
  4. ServerSocket(int port, int backlog, InetAddress bindAddr) throws IOException

使用:

serverSocket = new ServerSocket(5000);//绑定端口为5000的应用程序
Socket socket = serverSocket.accept();

服务器进程通过 ServerSocket 的 accept() 方法从队列中取出连接请求。
ServerSocket 的以下两个 get 方法可以分别获得服务器绑定的 IP 地址, 以及绑定的端口:
public InetAddress getInetAddress();
public int getLocalPort()

2. Socket使用详解

Socket socket = new Socket("192.168.56.1",5000);

“192.168.56.1”为访问远程主机ip地址,5000是端口号。
详细使用情况请看:
点击这里,查看Scoket使用详情

3. 网络传递数据“流”的使用

3.1 在网络中传递信息,在线聊天(使用多线程)。
在网络中传递信息,OutputStream与InputStream的搭配,注意高级流的搭配。
通过socket得到输入流:
InputStream inputStream = socket.getInputStream();
InputStream无法读取字符串,将低级流包装成高级流需要借助InputStreamReader搭桥:

BufferedReader bufferedReader = new BufferedReader(
                    new InputStreamReader(inputStream));

然后,可以通过BufferedReader 中的readLine()方法

bufferedReader.readLine()

一行一行读取流中的信息。
案例一(即时通讯)代码如下:

服务器端:

package com.sws.net;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
    public static void main(String[] args) {
        ServerSocket serverSocket = null;
        try {
            serverSocket = new ServerSocket(5000);
            System.out.println("服务器已经启动,等待连接.......");
            int i = 1;
            while(true){ 
                Socket socket = serverSocket.accept();
                String name = "服务器线程"+i;
                ServerService service = new ServerService(name,socket);
                i++;
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            try {
                if (serverSocket != null) {
                    serverSocket.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }
}

服务器多线程处理客户端请求:

package com.sws.net;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
public class ServerService extends Thread {
    Socket socket = null;
    public ServerService(String name,Socket socket){
        super(name);
        this.socket = socket;
        this.start();
    }
    public void run(){
        String clientIP = socket.getInetAddress().getHostAddress();
        try {
            InputStream inputStream = socket.getInputStream();
            BufferedReader bufferedReader = new BufferedReader(
                    new InputStreamReader(inputStream));
            String message = null;
            while((message=bufferedReader.readLine()) != null){
                if(message.equals("exit")||message.equals("quit")){
                    System.out.println(getName()+"接收到" +clientIP+"的退出请求,服务器线程也要关闭了!");
                    break;
                }
                System.out.println(getName()+"接收到" +clientIP+"说:" + message);
            }
        } catch (Exception e) {
            if(e.getMessage().equals("Connection reset")){
                System.out.println("客户端"+clientIP+"没有遵守协议,非法退出!");
            }else{
                e.printStackTrace();
            }
        } finally{
            try {
                if (socket != null) {
                    socket.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }
}

客户端:

package com.sws.net;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.Socket;
import java.net.UnknownHostException;
public class Client {
    public static void main(String[] args) {
        Socket socket = null;
        BufferedReader reader = null;
        try {
            socket = new Socket("192.168.56.1",5000);
            OutputStream outputStream = socket.getOutputStream();
            PrintStream printStream = new PrintStream(outputStream);

            reader = new BufferedReader(new InputStreamReader(System.in));
            String tempString = null;
            while((tempString=reader.readLine())!=null){
                if(tempString.trim().length()==0){
                    continue;//空数据不发送
                }
                printStream.println(tempString);
                if("exit".equals(tempString)||"quit".equals(tempString)){
                    //客户端发出退出请求
                    break;
                }

            }
            System.out.println("客户端正常退出!");
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            try {
                if (socket != null) {
                    socket.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }
}

案例二(通过网络传递java对象)
知识点:为什么要序列化?
答:
解释一:序列化其实很好理解,假如你现在做一个项目,项目是分工合作的,并且你和其他小组成员不在同一个城市,那么你要如何把你写的那些类给其他小组成员呢?这个时候就要用到序列化了,简单的说:序列化就是将内存中的类或者对象(你写的类都是存储在内存中的)变成可以存储到存储媒介中的“流”,你将类序列化成流之后可以通过互联网传输给别人,你也可以反序列化将别人的序列化流转换成内存中的对象,就这么简单。
解释二:
对象序列化可以实现分布式对象。主要应用例如:RMI要利用对象序列化运行远程主机上的服务,就像在本地机上运行对象时一样。
java对象序列化不仅保留一个对象的数据,而且递归保存对象引用的每个对象的数据。可以将整个对象层次写入字节流中,可以保存在文件中或在网络连接上传递。利用对象序列化可以进行对象的“深复制”,即复制对象本身及引用的对象本身。序列化一个对象可能得到整个对象序列。
java序列化比较简单,通常不需要编写保存和恢复对象状态的定制代码。实现java.io.Serializable接口的类对象可以转换成字节流或从字节流恢复,不需要在类中增加任何代码。只有极少数情况下才需要定制代码保存或恢复对象状态。这里要注意:不是每个类都可序列化,有些类是不能序列化的,例如涉及线程的类与特定JVM有非常复杂的关系。

先准备一个java对象:

package com.sws.net.day2;
import java.io.Serializable;

public class Person implements Serializable {
    /**
     * 序列化的实质
     */
    private static final long serialVersionUID = 9125145084085602014L;
    private String name;
    private int age;
    private String address;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }

    public Person() {
        super();
    }
    public Person(String name, int age, String address) {
        super();
        this.name = name;
        this.age = age;
        this.address = address;
    }
    @Override
    public String toString() {
        return "Person [name=" + name + ", age=" + age + ", address=" + address
                + "]";
    }
}

两个重要的流:ObjectOutputStream和ObjectInputStream,
writeObject(object)将对象写入流中,readObject()从流中读取对象(反序列化)

然后,写服务端,此处省略Server.java,该类与案例一中一样,
package com.sws.net.day2;

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

public class ServerService extends Thread {
    private Socket socket = null;

    public ServerService(String name,Socket socket){
        super(name);
        this.socket = socket;
        this.start();
    }

    public void run(){
        BufferedInputStream bin = null;
        try {
            OutputStream outputStream = socket.getOutputStream();       
//          java网络传输对象实现如下:
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
            Person person = new Person("小松松", 23, "江苏省淮安市");
            objectOutputStream.writeObject(person);
            System.out.println("服务器发送信息成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            try {
                if(socket != null){
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

最后写客户端:在客户端接收来自服务端的对象时,记得本地也得有一个Person类,其包的路径要与服务器端一致,用作反序列化,否则会抛出ClassNotFoundException 异常。

package com.sws.net.day2;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {

    public static void main(String[] args){
        Socket socket = null;
        try {
            socket = new Socket("192.168.56.1",5000);
            InputStream inputStream = socket.getInputStream();
            ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
            //从objectInputStream中读出对象信息
            Person person = (Person) objectInputStream.readObject();
            System.out.println("接受到来自服务器的信息是:"+person.toString());
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally{
            try {
                if(socket != null){
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

案例三(在网络中传输二进制文件):
在网路中传递文件要注意既要传递文件内容(字节数组格式)也要传递文件名称(字符串格式),因此在选择流的时候需要主要使用的流既可以传递字符串也可以传递字节数组,最简单的操作是DataOutputStream和DataInputStream流的搭配和使用,DataOutputStream的writeUTF(str)用作向流中写入字符串,write(content,0,i)方法用来写字节数组,content是一个byte[]类型,从第0索引开始写i个字节。DataInputStream的readUTF()放大用来读取流中的字符串,read(chara)方法用来读取字节数组读到byte[]chara中。
服务器端代码如下:此处省略Server.java,该类与案例一中一样,

package com.sws.net.day2;

import java.io.BufferedInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;

public class ServerService extends Thread {
    private Socket socket = null;

    public ServerService(String name,Socket socket){
        super(name);
        this.socket = socket;
        this.start();
    }

    public void run(){
        //初始化文件
        File file = null;
        //准备读取文件流
        FileInputStream fin = null; 
        BufferedInputStream bin = null;
        //使用DataOutputStream即可以传递字符串(如文件名)也可以传递byte数组(如文件内容)
        DataOutputStream dataOutputStream = null;
        try {
            OutputStream outputStream = socket.getOutputStream();
//          java网络传输二进制文件实现如下:
            //获取文件路径
            file = new File("E:\\Java EE\\axis2-1.6.2-bin.zip");
            //获取文件名
            String fileName = file.getName();
            //初始化读取文件低级流
            fin = new FileInputStream(file);
            //包装成高级流
            bin = new BufferedInputStream(fin);
            //初始化DataOutputStream流
            dataOutputStream = new DataOutputStream(outputStream);
            //先传递文件名DataOutputStream流使用writeUTF()方法来传递字符串格式数据
            dataOutputStream.writeUTF(fileName);
            //然后使用BufferedInputStream读取文件内容到字符数组中
            int i = 0;//用来标记读了多少个字节
            byte[] content = new byte[1024*1014];//创建一个1兆大小的数组,1兆1兆的读取文件内容
            while((i=bin.read(content)) != -1){
                //i=bin.read(content)) != -1说明没有读到文件末尾
                //使用DataOutputStream流向客户端发送
                dataOutputStream.write(content,0,i);
            }
            dataOutputStream.flush();
            System.out.println("服务器发送文件成功!");
        } catch (IOException e) {
            e.printStackTrace();
        } finally{
            try {
                if(socket != null){
                    socket.close();
                }
                if(fin != null){
                    fin.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

客户端代码如下:

package com.sws.net.day2;

import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.UnknownHostException;

public class Client {

    public static void main(String[] args){
        Socket socket = null;
        //定义文件
        File file = null;
        //定义流
        FileOutputStream fout = null;
        BufferedOutputStream bout = null;
        DataInputStream dataInputStream = null;
        try {
            socket = new Socket("192.168.56.1",5000);
            InputStream inputStream = socket.getInputStream();
            //将InputStream包装成高级流,DataInputStream
            dataInputStream = new DataInputStream(inputStream);
            String fileName = dataInputStream.readUTF();//读取文件名
            String filePath = "E:\\"+File.separator+fileName;
            //初始化文件
            file = new File(filePath);
            //初始化写文件流FileOutputStream
            fout = new FileOutputStream(file);
            //将fout包装成高级流BufferedOutputStream,增加写入文件效率
            bout = new BufferedOutputStream(fout);
            //写入文件
            int i = 0;//记录读取字节数
            byte[] chara = new byte[1024*1024];
            while((i=dataInputStream.read(chara)) != -1){
                bout.write(chara, 0, i);
            }
            bout.flush();
            System.out.println("客户端接受来自服务器的文件成功!");
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }/* catch (ClassNotFoundException e) {
            e.printStackTrace();
        } */
        finally{
            try {
                if(socket != null){
                    socket.close();
                }
                if(fout != null){
                    fout.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值