Java的第一遍学习笔记 网络编程

网络通信

网络 

ip地址 

域名与端口号

 端口就类似于家里的门,可以开很多门,只有从门才能进入家里。

通信协议

TCP与UDP

类似于发短信,并不知道对方收到没有,也可能发错了。 

InetAddress类 

Socket

TCP网络通信编程 

注: 当客户端连接到服务端后,实际上客户端也是通过一个端口和服务端进行通讯的,这个端口是TCP/IP 来分配的,是不确定且随机的。

编程举例

客户端:

public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket(InetAddress.getLocalHost(),9999);//连接本机的9999端口,连接成功返回socket对象
        System.out.println("客户端socket返回: " + socket.getClass());
        OutputStream outputStream = socket.getOutputStream(); //通过socket.getOutputStream()获得输出流
        outputStream.write("hello,server".getBytes()); //写入数据(需要调用getBytes方法)
        outputStream.close(); //关闭流和socket
        socket.close();
    }
}

服务端: 

class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9999);        // 本机的9999端口开始监听(要求本机没有其它服务在监听9999)
        System.out.println("服务端,9999端口监听,等待连接...");    // 这个ServerSocket可以通过accept()方法 返回多个Socket,允许多个客户端连接服务器的并发
        //当没有客户端连接9999端口时,程序会阻塞,等待连接
        Socket socket = serverSocket.accept();    //如果有客户端连接,则会返回Socket对象,程序继续
        InputStream inputStream = socket.getInputStream();        //连接上后,通过 socket.getInputStream得到输入流对象
        byte[] buf = new byte[1024];
        int readLen = 0;
        while((readLen = inputStream.read(buf))!=-1){
            System.out.println(new String(buf,0,readLen));
        } //参见IO流,这里每次打印一个字符串
        inputStream.close(); //记得最后关闭流对象和socket,否则会占用资源
        socket.close();
    }
}

注意运行的时候先运行服务器,再运行客户端。

进阶版:

这次需要客户端做出回应,那么就需要考虑一个问题:什么时候服务端才算接收完了。就比如说两个聊天,A必须等B说完才能回应。我们需要加上对应的shutdownOutput() / shutdownInput()

 总体的思路就是:让客户端先用OutputStream,服务端接收。然后用shutdown断开客户端的输出流。接着服务端用OutputStream,客户端接收。 

客户端:

public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket(InetAddress.getLocalHost(),9999);//连接本机的9999端口,连接成功返回socket对象
        System.out.println("客户端socket返回: " + socket.getClass());
        OutputStream outputStream = socket.getOutputStream(); //通过socket.getOutputStream()获得输出流
        outputStream.write("hello,server".getBytes());
        socket.shutdownOutput(); //停止输出流(告诉服务端我停止输入了)
        InputStream inputStream = socket.getInputStream();
        int readLen = 0;
        byte[] bytes = new byte[1024];
        while((readLen = inputStream.read(bytes))!=-1){
            System.out.println(new String(bytes,0,readLen));
        }
        inputStream.close();
        outputStream.close(); //关闭流和socket
        socket.close();
    }

服务端: 

class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务端,9999端口监听,等待连接...");
        Socket socket = serverSocket.accept();
        InputStream inputStream = socket.getInputStream();  //输入流    
        int readLen = 0;
        byte[] bytes = new byte[1024];
        while((readLen = inputStream.read(bytes))!=-1){
            System.out.println(new String(bytes,0,readLen));
        } //读入数据三部曲
        OutputStream outputStream = socket.getOutputStream(); //开始反输出
        outputStream.write("hello,client".getBytes());
        socket.shutdownOutput(); //停止输出流
        outputStream.close(); //断开
        inputStream.close();
        socket.close();
    }
}

把字节流改成字符流版本:

 修改输出:

        OutputStream outputStream = socket.getOutputStream();
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream); //转换流
        BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter); //用转换流为中介,把字节流转换成字符流
        bufferedWriter.write("hello,server");
        bufferedWriter.newLine(); //插入一个换行符,表示写入的内容结束,注意:要求对方使用 readLine方法接收
        bufferedWriter.flush(); //刷新
        

修改输入: 

        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); //用转换流作为中介创建 BufferedReader
        String s = bufferedReader.readLine(); //用readLine读入
        System.out.println(s);

收发文件案例

    大致思路:把磁盘上图片输入到文件字节数组,然后通过Socket发送到服务端的Socket,重新解码为文件数据,然后放到某个目录上。服务端发送收到图片的消息,客户端显示收到的消息。

    解释一下StreamUtils:这是一个自己定义的库,里面有各种各样的方法。我们要用到的是下面这几个:

首先是客户端:

1. 连接本机的8888端口。

Socket socket = new Socket(InetAddress.getLocalHost(),8888);//连接本机的8888端口,连接成功返回socket对象

2. 把要传输的文件先转化为byte数组,存储数据。

String filePath = "d:\\tupian1.png"; //要传输的文件路径
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath));
byte[] bytes = StreamUtils.streamToArray(bis); //把文件存到byte数组中

3. 客户端传输数据到服务器。

BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
bos.write(bytes); //输出到服务器
socket.shutdownOutput(); //暂停输出,让服务器回应

4. 接收服务器的回应。

InputStream inputStream = socket.getInputStream(); //接收服务器回应
String s = StreamUtils.streamToString(inputStream); //转化成字符串
System.out.println(s);

5. 关闭各种流与socket。

        inputStream.close();
        bis.close();
        bos.close();
        socket.close();

然后是服务器:

1. 创建端口等待连接。

ServerSocket serverSocket = new ServerSocket(9999);
System.out.println("服务端,8888端口监听,等待连接...");
Socket socket = serverSocket.accept(); //返回端口对应的socket,等待连接

2. 接收客户端发来的数据,保存在byte数组里。

BufferedInputStream bis = new BufferedInputStream(socket.getInputStream());
//因为图片是二进制文件,因此用InputStream
byte[] bytes = StreamUtils.streamToByteArray(bis); //接收客户端数据,bytes数组存储的就是图片的内容

3. 把byte数组里的数据还原到指定文件中。

String desFilePath = "src:\\tupian2.png"; //存放文件的位置
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(desFilePath)); //输出到指定文件中,用输出流
bos.write(bytes);//输出,此时文件已复制完成

4. 告诉客户端自己已经收到数据。

//接下来就是告诉客户端自己收到信息了(用字符流输出汉字信息)
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
//用OutputStreamWriter修改为字符流输出
writer.write("收到图片");
writer.flush(); //注意刷新
socket.shutdownOutput(); //暂停输出

5. 关闭各种流与socket。

        bos.close();
        bis.close();
        socket.close();
        serverSocket.close();

netstat指令 

UDP网络编程

基本介绍 

基本流程

应用案例

接收端A: 

public class Receiver {
    public static void main(String[] args) throws IOException {
        DatagramSocket socket = new DatagramSocket(9999); // 1.创建一个 DatagramSocket对象,准备在9999接收数据   
        byte[] buf = new byte[1024 * 64]; 
        DatagramPacket packet = new DatagramPacket(buf,buf.length); // 2.构建一个 DatagramPacket 对象,准备存放数据 (一个数据包最大64k)
        socket.receive(packet);//3.调用接收方法,将传输的数据填充到 packet中
        //4. 可以把packet进行拆包,取出数据并显示。
        int length = packet.getLength(); //实际接收到的数据字节长度
        byte[] data = packet.getData(); // 接收到的数据(以字节数组形式保存)
        String s = new String(data,0,length);
        System.out.println(s);

        //5.关闭资源
        socket.close();
    }
}

接收端B: 

public class Sender {
    public static void main(String[] args) throws IOException {
        //1.创建 DatagramSocket对象,准备在9998端口接收数据
        DatagramSocket socket = new DatagramSocket(9998);
        //注意:在UDP中,即使是发送端也是可以接收数据的。
        //2.将需要发送的数据,封装到 DatagramPacket对象
        byte[] data = "hello,明天吃火锅".getBytes();
        //3. 有内容的DatagramPacket构造器:内容字节数组  数组长度  主机IP  端口
        DatagramPacket packet =
                new DatagramPacket(data,data.length, InetAddress.getByName("192.168.43.8"),9999);
        socket.send(packet);//4.发送内容
        socket.close(); //关闭资源
    }
}

    想要反过来发信息,只需要角色对换重新写一遍send和receive即可。注意 receive和 send方法都是socket的,即使是在9998端口监听的socket,也可以向9999端口发送数据

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值