第二十六天网络编程

网络编程的三要素:

1、IP地址

2、端口号

3、协议

举例:

假设王看上了一个女生,想要去表白。

1、要去找到这个女生 -- IP地址

2、对Ta表白,跟Ta说话,对着耳朵说话 -- 端口号

3、要说什么呢?“I Love You”

但是呢,这个女生没有学过英文,听不懂

他就没有必要说英文,说她能听懂的语言,说汉语就可以,说“我爱你” -- 协议

IP地址:

网络中计算机唯一标识

但是呢,我们计算机只能识别二进制的数据,所以我们想IP地址在计算机中存储的时候应该也是由二进制的形式进行存储的。

IP:192.168.7.42

换算成二进制:11000000.10101000.00000111.00101010

实际上存储的是:11000000101010000000011100101010,这样的存储形式,我们在学习过程中,

难免会遇到配置网络地址的操作,这样的IP地址从记忆和书写层面上来说都是很麻烦的。

为了方便IP地址的标识,就把IP地址上的每一个字节上的数据转换成10进制,然后字节与字节之间用.分割标识。

这样的表示法:点分十进制表示法。

11111111

IP地址的组成:网络号段 + 主机号段

IP地址的分类:

A:第一个号段定义为网络号段 + 后面三个号段都是为主机号段

可以有多少台主机相连:256*256*255

B:前两个号段为网络号段 + 后两个号段为主机号段 256*255

C:前三个号段都为网路号段 + 最后一个号段为主机号段 255 局域网

D:224.0.0.1---239.255.255.254

E:240.0.0.1---247.255.255.254

特殊的IP地址:

1、loaclhost = 本机 = 127.0.0.1

127.0.0.1 回环地址,可以用于测试本机的网络是否有问题,ping 127.0.0.1

2、广播地址:

x.x.x.255

3、网络地址:

x.x.x.0

三个DOS命令:

ipconfig Windows: 查看ip地址

Linux:

centOS6:ifconfig

centOS7:ip addr

ping:测试网络是否联通

ping + IP地址/host名

为了方便我们对IP地址的获取和操作,java提供了一个类InetAddress 供我们使用

public static InetAddress getByName(String host)

throws UnknownHostException确定主机名称的IP地址。

import java.net.InetAddress;


public class InetAddressDemo {
    public static void main(String[] args) throws Exception {
        InetAddress name = InetAddress.getByName("192.168.7.5");
        System.out.println(name);
        //public String getHostName()获取此IP地址的主机名
        String hostName = name.getHostName();
        System.out.println(hostName);
        //public String getHostAddress()返回文本显示中的IP地址字符串。
        String hostAddress = name.getHostAddress();
        System.out.println(hostAddress);
    }
}

UDP协议发送数据:

  • 1、创建发送端的Socket对象
  • 2、创建数据,并把数据打包
  • 3、调用Socket对象的发送方法将数据发送出去
  • 4、释放资源,关闭Socket
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;


public class SendDemo1 {
    public static void main(String[] args) throws Exception {
        //1、创建发送端的Socket对象 DatagramSocket
        //DatagramSocket()
        //构造数据报套接字并将其绑定到本地主机上的任何可用端口。
        DatagramSocket ds = new DatagramSocket();
//2、创建数据,并把数据打包
        byte[] bytes = "你好".getBytes();
        //获取数组的长度
        int length = bytes.length;
        //要发送的IP地址
        InetAddress address = InetAddress.getByName("192.168.7.5");
        //设置端口号
        int port=10086;
        //3、调用Socket对象的发送方法将数据发送出去
        //DatagramPacket(byte[] buf, int length, InetAddress address, int port)
        //构造用于发送长度的分组的数据报包 length指定主机上到指定的端口号。
        DatagramPacket packet = new DatagramPacket(bytes, length, address, port);
        //从此套接字发送数据报包。
        //void send(Data
        // gramPacket p)
        ds.send(packet);
        //4、释放资源,关闭Socket
        ds.close();

    }
}

UDP协议接收数据:

  • 1、创建接收端的Socket对象
  • 2、创建一个数据包(接收容器)、
  • 3、调用Socket对象的接收方法接收数据
  • 4、解析数据包,得到数据并显示在控制台上
  • 5、释放资源,关闭Socket
  • 注意事项:
  • 接收端程序在绑定同一个端口的时候不能同时运行多个
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

public class ReceiveDemo1 {
    public static void main(String[] args) throws IOException {
        //1、创建接收端的Socket对象
        //DatagramSocket(int port)
        //构造数据报套接字并将其绑定到本地主机上的指定端口。
        DatagramSocket ds = new DatagramSocket(10086);
        //2、创建一个数据包(接收容器)
        //定义个字节数组
        byte[] bytes = new byte[1024];
        int length = bytes.length;
        //DatagramPacket(byte[] buf, int length)
        //构造一个 DatagramPacket用于接收长度的数据包 length 。
        DatagramPacket datagramPacket = new DatagramPacket(bytes, length);
        //3、调用Socket对象的接收方法接收数据
        ds.receive(datagramPacket);//程序阻塞,直到接收到数据
        InetAddress address = datagramPacket.getAddress();
        String ip = address.getHostAddress();
        //4、解析数据包,得到数据并显示在控制台上
        //public byte[] getData()返回数据缓冲区。
        byte[] data = datagramPacket.getData();
        int length1 = datagramPacket.getLength();
        String s = new String(data, 0, length1);
        System.out.println(ip+"发送的数据为"+s);
//释放资源,关闭Socket
        ds.close();

    }
}
结果:
192.168.7.5发送的数据为你好

从键盘录入数据进行发送,如果输入的是886那么客户端就结束输入数据。

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 SendDemo2 {
    public static void main(String[] args) throws IOException {
        //1、创建发送端的Socket对象
        DatagramSocket ds = new DatagramSocket();
        //2、键盘录入数据
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String line=null;
        while ((line=br.readLine())!=null){
            if ("886".equals(line)){
                break;
            }
            //如果输入的数据不是886,将数据打包发送出去
            DatagramPacket datagramPacket = new DatagramPacket(line.getBytes(), line.getBytes().length, InetAddress.getByName("192.168.7.5"), 10086);
            ds.send(datagramPacket);

        }
        ds.close();

    }
}
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;

public class ReciveDemo2 {
    public static void main(String[] args) throws Exception {
        //1、创建接收端的Socket对象
        DatagramSocket ds = new DatagramSocket(10086);
        while (true){
            byte[] bytes = new byte[1024];
            DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
            //3、调用Socket对象的接收数据的方法接收数
            ds.receive(datagramPacket);
            //4、解析数据
            String ip=datagramPacket.getAddress().getHostAddress();
            String hostName = datagramPacket.getAddress().getHostName();
            byte[] data = datagramPacket.getData();
            int length = datagramPacket.getLength();
            String s = new String(data, 0, length);
            System.out.println(ip+":"+hostName+"发送的信息为"+s);

        }

    }
}

TCP协议编写服务器端代码:

  • 1、创建服务器端Socket对象(ServerSocket)
  • 2、调用accept()方法,监听客户端的连接,返回一个对应客户端连接的Socket对象
  • 3、获取通道中的输入流对象,读取客户端发送的数据,并显示在控制台上
  • 4、释放资源,关闭Socket服务
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class ServerDemo1 {
    public static void main(String[] args) throws Exception {
        //1、创建服务器端Socket对象(ServerSocket)
        //ServerSocket(int port)
        //创建绑定到指定端口的服务器套接字。
        ServerSocket ss = new ServerSocket(10086);
        //调用accept()方法,监听客户端的连接,返回一个对应客户端连接的Socket对象
        Socket s = ss.accept();
        while (true){
            //获取通道中的输入流对象
            InputStream inputStream = s.getInputStream();
            byte[] bytes = new byte[1024];
            int length = inputStream.read(bytes);
            String s1 = new String(bytes, 0, length);
            String ip = s.getInetAddress().getHostAddress();
            String hostName = s.getInetAddress().getHostName();
            System.out.println(ip+":"+hostName+"发送的数据为"+s1);
        }
    }
}
import java.io.OutputStream;
import java.net.Socket;
import java.util.Scanner;
public class ClientDemo1 {
    public static void main(String[] args) throws Exception{
        //创建键盘录入对象
        Scanner scanner = new Scanner(System.in);

        //创建发送端的Socket对象
        //Socket(InetAddress address, int port)
        //创建流套接字并将其连接到指定IP地址的指定端口号。
//        Socket s = new Socket(InetAddress.getByName("192.168.7.42"), 12345);

        //Socket(String host, int port)
        //创建流套接字并将其连接到指定主机上的指定端口号。
        Socket os = new Socket("192.168.7.5", 10086);
        //获取输出流对象,想服务器写数据
        OutputStream s = os.getOutputStream();
        while (true){
            String next = scanner.next();
            if ("886".equals(next)){
                break;
            }
            s.write(next.getBytes());
        }
        os.close();
    }
}

改进

import java.io.InputStream;
import java.io.OutputStream;

import java.net.Socket;
import java.util.Scanner;
public class ClientDemo2 {
    public static void main(String[] args) throws Exception {
        //1、创建客户端Socket对象
        Socket ss = new Socket("192.168.7.5",10086);

        //2、获取通道中字节输入流对象,接收服务器给的反馈
        InputStream is = ss.getInputStream();
        //3、获取通道中字节输出流对象,往服务器发送消息
        OutputStream os = ss.getOutputStream();
        while (true){
            Scanner sc = new Scanner(System.in);
            String next = sc.next();
            os.write(next.getBytes());
            byte[] bytes = new byte[1024];
            int length = is.read(bytes);
            String s = new String(bytes, 0, length);
            System.out.println("服务给出反馈:" + s);
        }


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

public class ServerDemo2 {
    public static void main(String[] args) throws Exception {
        //1、创建服务器端的Socket对象
        ServerSocket ss = new ServerSocket(10086);
        //2、监听客户端的连接
        Socket s = ss.accept();
        InputStream is = s.getInputStream();
        //4、获取通道中的字节输出流对象,给客户端一个反馈
        OutputStream os = s.getOutputStream();
        while (true) {
            byte[] bytes = new byte[1024];
            int length = is.read(bytes);
            String string = new String(bytes, 0, length);
            String hostName = s.getInetAddress().getHostName();
            String ip = s.getInetAddress().getHostAddress();
            System.out.println(ip + ":" + hostName + "发送的数据为:" + string);
            os.write("服务器已经接收到数据".getBytes());
        }
    }
}

用多线程改进服务器端代码,实现可以让多个客户端与之建立连接

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.util.Scanner;

public class ClientDemo3 {
    public static void main(String[] args) throws Exception {
        //创建键盘录入对象
        Scanner sc = new Scanner(System.in);
        //创建客户端Socket对象
        Socket socket = new Socket("192.168.7.5", 10086);
        System.out.println("请输入想要发送的数据:");
        //获取通道中字节输出流对象
        OutputStream os = socket.getOutputStream();
        OutputStreamWriter osw = new OutputStreamWriter(os);
        BufferedWriter bw = new BufferedWriter(osw);
        while (true){
            String next = sc.next();
            if ("886".equals(next)){
                socket.shutdownOutput();
                break;
            }
            bw.write(next);
            bw.newLine();
            bw.flush();
        }
        socket.close();
    }
}
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Date;

public class ServerDemo3 {
    public static void main(String[] args) throws Exception {
        System.out.println("================服务器启动=======================");
        ServerSocket ss = new ServerSocket(10086);
        //监听的时候,对应一个客户端,返回一个对应的Socket对象
        //定义一个循环不断的接收客户端的连接请求
        while (true) {
            Socket socket = ss.accept();
            new ServerReaderThread(socket).start();

        }

    }
}
//定义与客户端连接连接操作的线程类
class ServerReaderThread extends Thread{
    private Socket socket;
    ServerReaderThread(Socket socket){
        this.socket=socket;
    }

    @Override
    public void run() {
        //获取与客户端通道中的字节输入流对象

        try {
            InputStream is = socket.getInputStream();
            //将字节输入流对象包装成字符流
            InputStreamReader isr = new InputStreamReader(is);
            //使用字符缓冲输入流
            BufferedReader br = new BufferedReader(isr);
            //ip
            String ip = socket.getInetAddress().getHostAddress();
            String hostName = socket.getInetAddress().getHostName();
            //按照行来读取
            String line;
            while ((line= br.readLine())!=null){
                //获取当前时间
                Date date = new Date();
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                String date2 = sdf.format(date);
                System.out.println(date2);
                System.out.println(ip+":"+hostName+"发送来数据:"+line);
            }

        } catch (IOException e) {
//            e.printStackTrace();
            System.out.println("客户端"+socket.getInetAddress().getHostName()+"下线了...");
        }
    }
}

按照我们正常的思路加入反馈,结果却没有反应,为什么

原因是,读取文本文件可以用null作为一个结束信息,但是呢,在通道内是不能这样结束信息的

所以服务器并不知道客户端已经上传结束了。而客户端还在等着服务器给我反馈,所以就出现了相互等待的现象。

如何解决呢?

1、再多写一条数据,告诉服务器,读到这条数据就说明文件上传完毕,没有数据过来了,让服务器也不用继续读了

这样做虽然能解决问题,但是呢,不好

2、Socket对象中shutdownOutput方法

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

public class UploadClient {
    public static void main(String[] args) throws Exception {
        //1、创建Socket对象
        Socket s = new Socket("192.168.7.5", 12345);
        //2、获取本地的字符输入流对象
        BufferedReader br = new BufferedReader(new FileReader("a.txt"));
        //3、获取通道中的字节输出流对象
        OutputStream outputStream = s.getOutputStream();
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
        BufferedWriter sw = new BufferedWriter(outputStreamWriter);
        String line=null;
        while ((line= br.readLine())!=null){
            sw.write(line);
            sw.newLine();
            sw.flush();
        }
        //自定义一个结束标记
//        bw.write("over");
//        bw.newLine();
//        bw.flush();
        //通知服务器我没有数据过来了,你也别等了
        s.shutdownOutput();
        //获取通道中字节输入流对象
        InputStream inputStream = s.getInputStream();
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
        BufferedReader br2 = new BufferedReader(inputStreamReader);
        String s1 = br2.readLine(); // 阻塞 等待服务给出反馈
        System.out.println(s1);


        //释放资源
        br.close();
        s.close();
    }

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

public class UploadServer {
    public static void main(String[] args) throws Exception {
        //1、创建服务器端的ServerSocket对象
        ServerSocket ss = new ServerSocket(12345);
        //2、监听客户端与之的连接,获取对应的Socket对象
        Socket s = ss.accept();
        //3、获取通道中的字节输入流对象
        InputStream inputStream = s.getInputStream();
        InputStreamReader isr = new InputStreamReader(inputStream);
        BufferedReader br = new BufferedReader(isr);
        //4、创建普通的字符输出流对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("a5.txt"));
        String line=null;
        while ((line= br.readLine())!=null){
//            if ("over".equals(line)){
//                break;
//            }
            bw.write(line);
            bw.newLine();
            bw.flush();
        }
        //服务器给出反馈
        //获取通道中字节输出流对象
        OutputStream outputStream = s.getOutputStream();
        OutputStreamWriter osw = new OutputStreamWriter(outputStream);
        BufferedWriter bw2 = new BufferedWriter(osw);
        bw2.write("文件上传成功");
        bw2.newLine();bw.flush();
        bw.close();
        s.close();
        bw2.close();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值