从零开始网络编程,看完这一篇并且要多多练习两三次,入门够够的!

  • Post author:
  • Post category:其他


网络编程主要有两个问题,1.如何定义一台或多台主机?2.找到主机后如何进行通信?

对问题1需要得到某台主机的IP地址和端口,不同进程有不同端口,用来区分软件,端口号规定范围:0~65535,在单个协议下(TCP/UDP),端口号是不能重复的

对问题2主要有TCP和UDP两种方式进行计算机间通信。TCP/IP 中有两个具有代表性的

传输层协议

,分别是 TCP 和 UDP。下面这篇博客对TCP,UDP特点,区别,应用场景有详细介绍:



TCP和UDP的区别


解决问题1:学习InetAddress和InetSocketAddress两个类


InetAddress类

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.sql.SQLOutput;

public class TestInetAddress {
    public static void main(String[] args) throws UnknownHostException {
        //获取本地的一个InetAddress对象
        InetAddress ia1=InetAddress.getLocalHost();
        System.out.println(ia1);
        //通过IP获取一个InetAddress对象
        InetAddress ia2=InetAddress.getByName("127.0.0.1");
        System.out.println(ia2);
        //通过域名获取一个InetAddress对象
        InetAddress ia3=InetAddress.getByName("www.baidu.com");
        System.out.println(ia3);
       /**获取InetAddress类对象的三种方式:
       1.通过getLocalhost获取本机InetAddress对象
       2.通过getByName方法并传入IP地址
       3.通过getByName方法并传入域名
       */
       //常用的两个方法
        sout(ia1.getHostAddress())   //获取ia1的ip地址
        sout(ia1.getHostName()) //获取ia1的主机名

    }
}


InetSocketAddress类

InetSocketAddress和InetAddress类的主要区别是有端口的设置

import java.net.InetSocketAddress;

public class TestInetSocketAddress {
    public static void main(String[] args) {
        //通过localhost创建对象
        InetSocketAddress isa1=new InetSocketAddress("localhost",9090);
        System.out.println(isa1);
        //通过IP创建对象
        InetSocketAddress isa2=new InetSocketAddress("127.0.0.1",9090);
        System.out.println(isa2);

        System.out.println(isa1.getAddress());//获取isa1的IP
        System.out.println(isa1.getPort());//获取端口
        System.out.println(isa1.getHostName());//获取isa1的主机名

}

}


解决问题2:方式一TCP通信

TCP通信只能实现一对一传信息,发送文件等,有明确的客户端和服务器,连接比udp更加稳定,但是效率比udp低,连接涉及三次握手,断开连接涉及四次挥手

三次握手:

客户端首先向服务器发送连接请求,这是第一次握手;服务器做出应答并进入等待状态,这是第二次握手;客户端发送信号给服务器,建立连接成功,这是第三次握手。

四次挥手:

断开时,客户端向服务器发断开请求,这是第一次挥手;服务器收到请求后让应用层要释放 TCP 链接。然后会发送 ACK 包,并进入等待,这是第二次挥手;服务器有未发送的信息会在这时再次发送,完毕后会向 A 发送连接释放请求,这是第三次挥手;客户端发送最后信号确认断开给服务端,断开成功,这是第四次挥手。


TCP应用1:客户端给服务器发送消息

万物皆对象,创建两个类,客户端类和服务器类


客户端:

import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
public class TcpClientDemo1 {

    public static void main(String[] args) throws Exception{
        //1.要知道服务器的端口,ip
        InetAddress serverIP=InetAddress.getByName("localhost");
        int port=9999;
        //2.要建立一个socket连接
        Socket s=new Socket(serverIP,port);
        //3.输出流要传递的信息
        OutputStream os=s.getOutputStream();
        os.write("你好啊,服务器!".getBytes());//字符串是无法传递的,需要getBytes()变成字节流
        //4.关闭资源
        s.close();
        os.close();


    }
}


服务器:

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class TcpServerDemo1 {
    public static void main(String[] args) throws Exception {
        //1.首先服务器要有一个地址
        ServerSocket serverSocket=null;
        serverSocket=new ServerSocket(9999);
        //2.等待客户端连接过来
        Socket socket=null;
        socket=serverSocket.accept();
        //3.读取客户端发来的信息
        InputStream is=socket.getInputStream();
        //4.将收到的字节流放入缓冲区
        ByteArrayOutputStream baos=new ByteArrayOutputStream();
        byte[] buffer=new byte[1024];
        int len;
        while((len=is.read(buffer))!=-1){//读取is里的信息放入buffer
            baos.write(buffer,0,len);//将buffer里的信息写到baos
        }
        //5.输出接收的信息
        System.out.println(baos.toString());
        //6.关闭资源
        socket.close();
        is.close();
        baos.close();
        serverSocket.close();
    }
}


TCP应用2:客户端给服务器传文件

万物皆对象,创建两个类,客户端类和服务器类


客户端:

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

public class TcpClientDemo2 {
    public static void main(String[] args) throws Exception {
        //1.要知道服务器的端口,ip
        InetAddress serverIP=InetAddress.getByName("localhost");
        int port=9999;
        //2.要建立一个socket连接
        Socket s=new Socket(serverIP,port);
        //3.读取要传给服务器的图片文件
        FileInputStream fis=new FileInputStream(new File("src/go.jpg"));
        //4,将文件输入流放入缓冲区并通过os输出
        //创建一个输出流
        OutputStream os=s.getOutputStream();
        byte[] buffer =new byte[1024];//缓冲区
        int len;
        while ((len=fis.read(buffer))!=-1){//从哪读,固定代码
            os.write(buffer,0,len);//信息都写入了os,os相当于载体
        }
        s.close();
        fis.close();
        os.close();

    }
}


服务器:

import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class TcpServerDemo2 {
    public static void main(String[] args) throws Exception{
        //1.首先服务器要有一个地址
        ServerSocket serverSocket=null;
        serverSocket=new ServerSocket(9999);
        //2.等待客户端连接过来
        Socket socket=null;
        socket=serverSocket.accept();
        //3.读取客户端发来的信息
        InputStream is=socket.getInputStream();
        //4,将收到的is放入缓冲区,并写入文件输出流fos
        FileOutputStream fos=new FileOutputStream("receive1.jpg");//如果是输出信息用baos流,此题输出文件流用fos

        byte[] buffer=new byte[1024];
        int len;
        while((len=is.read(buffer))!=-1){
            fos.write(buffer,0,len);
        }
        //5.关闭资源
        socket.close();
        serverSocket.close();
        is.close();
        fos.close();

    }
}

左侧文件夹多出了receive1.jpg说明传输成功了


解决问题2:方式二UDP通信


UDP应用1:单方面发送消息


客户端

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.Socket;

public class UdpClientDemo1 {
    public static void main(String[] args) throws Exception{
        //1.要知道对方的端口,ip
        InetAddress serverIP=InetAddress.getByName("localhost");
        int port=9999;
        //2.要建立一个socket连接,这里与TCP用的不同类
        DatagramSocket s=new DatagramSocket();
        //3.建立包并打包好
        String  msg="你好啊,对面的朋友!";//信息
        DatagramPacket packet=new DatagramPacket(msg.getBytes(),0,msg.getBytes().length,serverIP,port);//把信息打成包
        //4.发送包
        s.send(packet);
        //5.关闭资源
        s.close();

    }
}


服务器端

import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UdpServerDemo1 {
    public static void main(String[] args) throws Exception{
        //1.开放端口,不用ip了,因为这个例子是用本机测试,client会把包发给localhost
        DatagramSocket socket=new DatagramSocket(9999);
        //2.接受包,并输出包裹内容
        byte[] buffer=new byte[1024];//将包放到缓冲区
        DatagramPacket packet=new DatagramPacket(buffer,0,buffer.length);//这里需要新建一个packet空间来接收
        socket.receive(packet);//阻塞接收
        System.out.println(packet.getAddress().getHostName());
        System.out.println(new String(packet.getData(),0, packet.getLength()));//输出包裹内容
        //3.关闭资源
        socket.close();
    }
}


UDP应用2:单方面不断发送消息

物皆对象,创建两个类,Sender类和Receiver类


Sender类:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Scanner;

//UDP聊天实现
public class UdpSenderDemo1 {
    public static void main(String[] args) throws Exception {
        //1.要知道对方的端口,ip
        InetAddress serverIP=InetAddress.getByName("localhost");
        int port=6666;
        //2.要建立一个socket连接,这里与TCP用的不同类
        DatagramSocket s=new DatagramSocket();
        //3.准备发送消息,控制台输入
        BufferedReader reader=new BufferedReader(new InputStreamReader(System.in));
        //4.不断读取数据,将数据打包
        while (true){
            String data=reader.readLine();//读取数据
            DatagramPacket packet=new DatagramPacket(data.getBytes(),0,data.getBytes().length,serverIP,port);
            s.send(packet);
            if(data.equals("bye")){break;}
        }
        //5.关闭资源
        s.close();
    }
}


Receiver类

import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class UdpReceiverDemo1 {
    public static void main(String[] args) throws Exception{
        //1.开放端口
        DatagramSocket socket=new DatagramSocket(6666);
        //2.不断接受包,并输出包裹内容
        while (true){
            byte[] buffer=new byte[1024];//将包放到缓冲区
            DatagramPacket packet=new DatagramPacket(buffer,0,buffer.length);//这里需要新建一个packet空间来接收
            socket.receive(packet);//阻塞接收
            String receiveData=new String(packet.getData(),0, packet.getLength());
            System.out.println(receiveData);//输出包裹内容
            if(receiveData.equals("bye")){break;}
        }
        //3.关闭资源
        socket.close();

    }
}


UDP应用3:双方都能发送和接受消息(即聊天实现)


万物皆对象:


双方都能够发送和接受信息,因此要有一个Send类和一个Receive类


双方都能同时操作,因此双方是两个线程


Send类

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;

public class TalkSend1 implements Runnable {
    //1.定义基本的信息
    private int toPort;
    private int localPort;
    private String toIP;
    //2.要建立一个socket连接
    DatagramSocket s = null;

    public TalkSend1(int localPort, String toIP, int toPort) {
        this.toPort = toPort;
        this.localPort = localPort;
        this.toIP = toIP;
        //2.向s传参数localPort
        try {
            s = new DatagramSocket(localPort);
        } catch (SocketException e) {
            throw new RuntimeException(e);
        }
    }
    @Override
    public void run() {
        //3.准备发送消息,控制台输入
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while (true) {
            try {
                //4.创建一个包裹
                String data = reader.readLine();//读取数据字符串
                DatagramPacket packet = new DatagramPacket(data.getBytes(), 0, data.getBytes().length, new InetSocketAddress(this.toIP, this.toPort));
                //5.发送包裹
                s.send(packet);
                if (data.equals("bye")) {
                    break;
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        //6.关闭资源
        s.close();
    }
}


Receive类

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

public class TalkReceive1 implements Runnable {
    //1.定义基本的信息

    private int localPort2;
    private String msgFrom;
    //2.要建立一个socket连接
    DatagramSocket s=null;

    public TalkReceive1(int localPort2, String msgFrom) {
        this.localPort2 = localPort2;
        this.msgFrom = msgFrom;
        try {//2.向s传参数localPort2
            s = new DatagramSocket(localPort2);
        } catch (SocketException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void run() {
        while (true) {
            try {

                //3.准备接受包裹
                byte[] buffer = new byte[1024];
                DatagramPacket packet = new DatagramPacket(buffer, 0, buffer.length);
                s.receive(packet);
                //4.输出包裹内容(理解为拆包裹)
                String receiveData = new String(packet.getData(), 0, packet.getData().length);
                System.out.println(msgFrom + ":" + receiveData.trim());
                if (receiveData.equals("bye")) {
                    break;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

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

启动学生线程:

public class TalkStudent {
    public static void main(String[] args) throws Exception {
        //开启两个线程
        new Thread(new TalkSend1(1111,"localhost",2222)).start();//学生作为发送人时,fromport是发件人的端口,toport是接收人的端口
        new Thread(new TalkReceive1(3333,"老师")).start();//学生作为接收人时
    }

}

启动老师线程:

public class TalkTeacher {
    public static void main(String[] args) throws Exception {
        //开启两个线程
        new Thread(new TalkSend1(4444,"localhost",3333)).start();
        new Thread(new TalkReceive1(2222,"学生")).start();
    }
}

这里遇到一个错误是Address already in use:cannot find

原因是我开始写receive1类的时候忘记添加关闭资源语句,以及我在写send1类的时候开始是把 DatagramSocket s的定义放在run()方法里,为了防止程序找不到s,我把s.close()放在了while循环内catch语句后。

重新调整了之后就不会报错了,借这个错误认识到了关闭资源的重要性。



版权声明:本文为m0_67042480原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。