网络编程基础,套接字编程

12 篇文章 0 订阅

2020.6.8 课堂笔记

网络编程

网络编程三要素

  1. IP地址:网络中设备的唯一标识,可用主机名。
  2. 端口号:用于标识进程的逻辑地址,区分不同应用程序之间的标识。(端口范围0-65535,0-1023被系统资源占用)。
    • 端口"是0到65535之间的一个整数,正好16个二进制位。0到1023的端口被系统占用,用户只能选用大于1023的端口。不管是浏览网页还是在线聊天,应用程序会随机选用一个端口,然后与服务器的相应端口联系。
  3. 传输协议:通讯的规则TCP/UDP协议。
    • UDP:特点:
      1. 通信两端不需要建立链接,因为不需要建立连接,效率高。属于不可靠协议,可能会发生数据丢失。
      2. 发送数据包的大小限制在64K。
    • TCP特点:
      1. 发送段和接受端需要建立链接,可靠协议。
      2. 无大小限制。
  4. 获取主机名hostname

IP地址概述

  1. 规定网络地址的协议,叫做IP协议。它所定义的地址,就被称为IP地址。
  2. 目前,广泛采用的是IP协议第四版,简称IPv4。这个版本规定,网络地址由32个二进制位(4位)组成。习惯上,我们用分成四段的十进制数表示IP地址,从0.0.0.0一直到255.255.255.255。
  3. IP地址由两部分组成,前面是网络地址,后面是主机地址。处于同一个子网络下的的IP地址,网络地址都相同。主机地址有区别。
    • IP地址 = 网络地址+主机地址
      • A类IP地址:第一段号码为网络地址,剩下的三段号码为本地计算机的号码
      • B类IP地址:前二段号码为网络地址,剩下的二段号码为本地计算机的号码
      • C类IP地址:前三段号码为网络地址,剩下的一段号码为本地计算机的号码
  4. 我们怎么判断,是否属于同一个字网络?
    • 为了解决这个问题就出现了子网络掩码,将两个IP地址与子网络掩码进行(&)位运算,两个运算结果相同就是同一个字网络,不相同就不是同一个子网络。
    • 子网络掩码就是:将网络地址的二进制全给成1,主机地址全给成0(子网络掩码就是11111111.11111111.11111111.00000000),写成十进制就是255.255.2555.0,这个就是子网络掩码。知道这个我们就可以判断了。
  5. IP的作用:就是为每一台电脑分配一个IP,然后确定哪些IP在同一个子网络下。

InetAddress

  1. 为了我们对ip的操作,Java提供的类,可以在里面获取ip和主机名

  2. 当我们知道主机名时,通过主机名获取IP地址

    • getByName(String host):根据主机名获取IP地址。
  3. 知道IP地址时,通过IP地址获取主机名

    • getByName(String host):根据IP获取主机名。
  4. 不知道IP或者主机名时

    • getLocalHost():返回主机名和IP地址
    • 也可以通过DOS命令获取。
  5. DOS 命令意义
    net view获取局域网中的所有主机名
    ipconfig -all获取本地IP,主机名,MAC地址
    arp -a获取本局域网中的所有IP地址和物理地址
    ping -a x.x.x.x获取x.x.x.x的主机名
    nbtstat -a 主机名获取MAC地址

代码:

package demo6; 
 
/*Author:LH
CreatTime:2020.06.16.16:22*/

import java.net.InetAddress;
import java.net.UnknownHostException;

public class Test {
    public static void main(String[] args) throws UnknownHostException {
//        根据主机名获取IP地址
        InetAddress name = InetAddress.getByName("DESKTOP-ITO7TPJ");
        System.out.println(name);
//        获取IP地址
        System.out.println(name.getHostAddress());
        System.out.println("===========");
//        根据主机名获取所有的IP地址
        InetAddress[] allByName = InetAddress.getAllByName("DESKTOP-ITO7TPJ");
        for (InetAddress inetAddress : allByName) {
            System.out.println(inetAddress);
        }
        System.out.println("===========");
//       返回本地主机名和地址
        InetAddress localHost = InetAddress.getLocalHost();
        System.out.println(localHost);
        System.out.println("===========");
//        根据IP地址获取主机名
        InetAddress address = InetAddress.getByName("192.168.1.105");
        System.out.println(address.getHostName());
    }
}
/*
结果
DESKTOP-ITO7TPJ/192.168.1.105
        192.168.1.105
        ===========
        DESKTOP-ITO7TPJ/192.168.1.105
        DESKTOP-ITO7TPJ/fe80:0:0:0:99ec:fbf4:eb1b:d19d%5
        ===========
        DESKTOP-ITO7TPJ/192.168.1.105
        ===========
        DESKTOP-ITO7TPJ*/

Socket编程

  1. 网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。

  2. 要求:

    • 通信两端两端都要有socket

    • 数据在两个Socket间通过IO传输。

    • 网络通信其实就是Socket之间通信

DUP协议编程
  1. DatagramSocket:此类表示用于发送和接收数据报数据包的套接字。采用的是DUP通信协议。
构造方法功能
DatagramSocket()构造数据报套接字并将其绑定到本地主机上的任何可用端口。
DatagramSocket(int port)构造数据报套接字并将其绑定到本地主机上的指定口。
DatagramSocket(int port, InetAddress laddr)创建一个数据报套接字,绑定到指定的本地地址。
  1. 常用方法:
方法功能
close()关闭套接字
getInetAddress()返回此套接字连接到的地址。
getLocalAddress()获取套接字所绑定的本地地址。
getLocalPort()返回此套接字绑定到的本地主机上的端口号。
getPort()返回此套接字连接到的端口号。
数据报包类
  • DatagramPacket :数据报包用来实现无连接包投递服务。每条报文仅根据该包中包含的信息从一台机器路由到另一台机器。从一台机器发送到另一台机器的多个包可能选择不同的路由,也可能按不同的顺序到达。不对包投递做出保证。

  • 为了传送数据,数据报包就要封装(数据,数据长度,IP地址,端口),封装完才能正确传输。

    • 构造方法:

      1. DatagramPacket(byte[] buf, int offset, int length, InetAddress address, int port)

        构造数据报包,用来将长度为 length偏移量为 offset的包发送到指定主机上的指定端口号。

      2. DatagramPacket(byte[] buf, int offset, int length, SocketAddress address)

        构造数据报包,用来将长度为 length 偏移量为 offset 的包发送到指定主机上的指定端口号。

      3. DatagramPacket(byte[] buf, int length, InetAddress address, int port)

        构造数据报包,用来将长度为 length的包发送到指定主机上的指定端口号。

代码:

package demo1; 
 
/*Author:LH
CreatTime:2020.06.13.10:14*/
//UDP协议接受和发送数据
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
//服务端套接字编程
public class UDPCilent {
    public static void main(String[] args) throws IOException {
//        服务端是接受数据,因此要暴露自己的端口
        DatagramSocket socket = new DatagramSocket(8888);
        byte[] bytes = new byte[1024];
//        创建一个数据包,将指定长度的数据接收到字节数组中
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
        System.out.println("等待接收");
//        接收数据报包
        socket.receive(packet);
//        获取数据报包里面的数据
        byte[] data = packet.getData();
        String s = new String(data);
        System.out.println(s);
    }
}
package demo1; 
 
/*Author:LH
CreatTime:2020.06.13.10:24*/
//UDP协议接受和发送数据
import java.io.IOException;
import java.net.*;
//客户端UDP套接字编程
//客户端数发送数据,因此需要封装接收端IP和端口
public class UDPServe {
    public static void main(String[] args) throws IOException {
        InetAddress ip = InetAddress.getByName("DESKTOP-ITO7TPJ");
        System.out.println(ip);

        DatagramSocket socket = new DatagramSocket();
        byte[] bytes = "你好,UDP测试".getBytes();
        DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("192.168.3.6"), 8888);
        socket.send(packet);
        socket.close();
    }
}

使用线程开启

package demo22;

/*Author:LH
CreatTime:2020.06.13.13:56*/
//使用线程开启网络编程
public class Test {
    public static void main(String[] args) {
        new MyCilentThread().start();
        new MyServertThread().start();
    }
}
package demo22;
 
/*Author:LH
CreatTime:2020.06.17.9:35*/

import java.io.*;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class MyCilentThread extends Thread {

    @Override
    public void run() {
        try {
            DatagramSocket socket = new DatagramSocket();
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            while (true) {
                String s = reader.readLine();
                byte[] bytes =s.getBytes();
//            封装接收端的ip和端口
                DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("192.168.3.6"), 9909);
                socket.send(packet);
                if ("再见".equals(s)){
                    break;
                }
            }
            socket.close();
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
package demo22;
 
/*Author:LH
CreatTime:2020.06.17.9:42*/

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

public class MyServertThread extends Thread {
    @Override
    public void run() {
        try {
//            端口号只能暴露一次,放在循环中就会报端口号被占用的异常
            DatagramSocket socket = new DatagramSocket(9909);
            while (true) {
                byte[] bytes = new byte[1024];
                DatagramPacket  packet = new DatagramPacket(bytes, bytes.length);
//            暴露自己本地端口
                System.out.println("等待接收");
                socket.receive(packet);
                byte[] data = packet.getData();
                String hostName = packet.getAddress().getHostName();
                String s = new String(data);
                System.out.println(hostName+"给你发来消息:"+s);
                if ("再见".equals(s)) {
                   break;
                }
            }
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

客户端与服务器聊天模式

package demo25; 
 
/*Author:LH
CreatTime:2020.06.17.11:34*/
//和服务器对话模式,
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class MyCilentTest {
    public static void main(String[] args) {
        new MyCilentRevecie().start();
        new MyCilentSend().start();

    }
}

class MyCilentSend extends Thread {

    @Override
    public void run() {
        try {
            DatagramSocket socket = new DatagramSocket();
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            while (true) {
                String s = reader.readLine();
                byte[] bytes = s.getBytes();
//            封装接收端的ip和端口
                DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("192.168.3.6"), 9909);
                socket.send(packet);
                if ("再见".equals(s)) {
                    break;
                }
            }
            socket.close();
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
class  MyCilentRevecie extends Thread{

    @Override
    public void run() {
        try {
//            端口号只能暴露一次,放在循环中就会报端口号被占用的异常
            DatagramSocket socket = new DatagramSocket(9908);
            System.out.println("等待接收");
            while (true) {
                byte[] bytes = new byte[1024];
                DatagramPacket  packet = new DatagramPacket(bytes, bytes.length);
//            暴露自己本地端口
                socket.receive(packet);
                byte[] data = packet.getData();
                String hostName = packet.getAddress().getHostName();
                String s = new String(data);
                System.out.println(hostName+"给你发来消息:"+'\n'+s);
                if ("再见".equals(s)) {
                    break;
                }
            }
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
package demo25; 
 
/*Author:LH
CreatTime:2020.06.17.11:39*/
//和客户端对话模式,
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class MyServerTest {
    public static void main(String[] args) {
        new MyServerReverice().start();
        new MyServerSend().start();
    }
}
class MyServerSend extends Thread{

    @Override
    public void run() {
        try {
            DatagramSocket socket = new DatagramSocket();
            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            while (true) {
                String s = reader.readLine();
                byte[] bytes = s.getBytes();
//            封装接收端的ip和端口
                DatagramPacket packet = new DatagramPacket(bytes, bytes.length, InetAddress.getByName("192.168.3.6"), 9908);
                socket.send(packet);
                if ("再见".equals(s)) {
                    break;
                }
            }
            socket.close();
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
class MyServerReverice extends Thread{

    @Override
    public void run() {
        try {
//            端口号只能暴露一次,放在循环中就会报端口号被占用的异常
            DatagramSocket socket = new DatagramSocket(9909);
            System.out.println("等待接收");
            while (true) {
                byte[] bytes = new byte[1024];
                DatagramPacket  packet = new DatagramPacket(bytes, bytes.length);
//            暴露自己本地端口
                socket.receive(packet);
                byte[] data = packet.getData();
                String hostName = packet.getAddress().getHostName();
                String s = new String(data);
                System.out.println(hostName+"发来消息:"+'\n'+s);
                if ("再见".equals(s)) {
                    break;
                }
            }
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

TCP协议编程
  1. Socket客户端编程的套接字

  2. ServeSocket服务端套接字

  3. 端口被占有异常

  4. 读取文件时,读取通道数据结束标记的确定

    • 方案1:手写标记,如果文件中有这个标记,就可能造成文件的不完整。
    • 方案2:就是客户端把输出流用完之后关闭,服务端那边就知道输出结束了,就不再等待了。调用shutdownOutPut()方法结束输出流。
  5. TCP协议编程的步骤

    • 发送数据:
      • 创建客户端套接字对象
      • 获取输出流
      • 输出数据
      • 释放资源
    • 接收数据
      • 创建服务端套接字对象
      • 监听客户端
      • 获取输入流对象
      • 读取数据
      • 释放资源
  6. 由于TCP协议传输数据时,两端必须建立连接,因此要先打开服务端,然后再开启客户端,否则会报异常Exception in thread “main” java.net.ConnectException: Connection refused: connect

  7. 在客户端和服务端连接上之后,没有数据的传输,因为read方法和readline方法都是阻塞方法。没有读取到就一直等待。

package demo27;
 
/*Author:LH
CreatTime:2020.06.17.14:16*/
//客户端发送消息,服务端给出反馈
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class MyCilent {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("192.168.3.6",5555);
//        通过输出流将数据传输到服务端
        OutputStream out = socket.getOutputStream();
        out.write("你好,服务端".getBytes());
//        获取服务端的反馈
        byte[] bytes = new byte[1024];
//        创建输入流,将数据传送给客户端
        InputStream in = socket.getInputStream();
         in.read(bytes);
        String s = new String(bytes);
        System.out.println(s);
        socket.shutdownInput();
        socket.shutdownOutput();
        socket.close();
    }
}
package demo27;
 
/*Author:LH
CreatTime:2020.06.17.14:34*/

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

public class MyServer {
    public static void main(String[] args) throws IOException {
        ServerSocket socket = new ServerSocket(5555);
        Socket accept = socket.accept();
        InputStream in = accept.getInputStream();
        byte[] bytes = new byte[1024];
        in.read(bytes);
        String s = new String(bytes);
        System.out.println(s);

//        给客户端一个反馈信息
        OutputStream out = accept.getOutputStream();
        out.write("接受完毕".getBytes());
        out.close();
        socket.close();
    }
}

package demo28; 
 
/*Author:LH
CreatTime:2020.06.17.15:01*/

import java.io.*;
import java.net.Socket;
//文本文件的上传到服务器,服务器保存到文件
public class MyCilent {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("192.168.3.6", 7777);

        OutputStream out = socket.getOutputStream();
//        创建一个字符流,将文件通信到服务端
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
//        读取文件
        BufferedReader reader = new BufferedReader(new FileReader("a.md"));
        String s = null;
        while ((s = reader.readLine()) != null) {
            writer.write(s);
            writer.flush();
        }
//        这的输出流关闭是个重点,因为,读取文件的时候,可以读到null,能判断到结束,但是服务器从输出流是读取不到null的,因此需要关闭字符输出流,来告诉服务器,你已经传数据结束了.
        socket.shutdownOutput();
//       接收复制完毕的消息
        InputStream in = socket.getInputStream();
        byte[] bytes = new byte[1024];
        in.read(bytes);
        String s1 = new String(bytes);
        System.out.println(s1);
        reader.close();
        out.close();
        in.close();
        socket.close();
    }
}
package demo28; 
 
/*Author:LH
CreatTime:2020.06.17.15:16*/

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

public class Myserver {
    public static void main(String[] args) throws IOException {
        ServerSocket socket = new ServerSocket(7777);
        Socket accept = socket.accept();
        InputStream in = accept.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));
        BufferedWriter writer = new BufferedWriter(new FileWriter("a-new.md"));
        String s;
//        其实这里服务器端从输出流中是读取不到null的,所以这个就是个死循环,必须读取到结束标识,来结束读取
//        一般会使用客户端的方法,关闭输出流,来告诉服务端,数据传输结束.
        while ((s=reader.readLine())!=null){
            writer.write(s);
            writer.flush();
        }
//        将接收完毕的消息传回去
        OutputStream out = accept.getOutputStream();
        out.write("接收完毕".getBytes());
        out.close();
        socket.close();
    }
}

一个服务器连接多个客户端

  1. 循环侦探客户端

代码

package demo29; 
 
/*Author:LH
CreatTime:2020.06.17.18:49*/
//循环监听客户端
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class Myserver {
    public static void main(String[] args) throws IOException {
        ServerSocket socket = new ServerSocket(45454);
        int i=0;
        while (true){
            Socket accept = socket.accept();
            System.out.println("第"+(++i)+"个客户端进来了");
            new MyServerThread(accept,socket,i).start();
        }

    }
}
class MyServerThread extends Thread{
    private Socket accept;
    private ServerSocket socket;
    private int i;

    public MyServerThread(Socket accept,ServerSocket socket,int i) {
        this.accept = accept;
        this.socket = socket;
        this.i = i;
    }

    @Override
    public void run() {
        try {
            InputStream in = accept.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in));
            BufferedWriter writer = new BufferedWriter(new FileWriter(i+"a-new.md"));
            String s;
            while ((s=reader.readLine())!=null){
                writer.write(s);
                writer.flush();
            }
//        将接收完毕的消息传回去
            OutputStream out = accept.getOutputStream();
            out.write("接收完毕".getBytes());
            out.close();
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
package demo28; 
 
/*Author:LH
CreatTime:2020.06.17.15:01*/

import java.io.*;
import java.net.Socket;
//文本文件的上传到服务器,服务器保存到文件
public class MyCilent {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("192.168.3.6", 45454);

        OutputStream out = socket.getOutputStream();
//        创建一个字符流,将文件通信到服务端
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));
//        读取文件
        BufferedReader reader = new BufferedReader(new FileReader("a.md"));
        String s = null;
        while ((s = reader.readLine()) != null) {
            writer.write(s);
            writer.flush();
        }
//        这的输出流关闭是个重点,因为,读取文件的时候,可以读到null,能判断到结束,但是服务器从输出流是读取不到null的,因此需要关闭字符输出流,来告诉服务器,你已经传数据结束了.
        socket.shutdownOutput();
//       接收复制完毕的消息
        InputStream in = socket.getInputStream();
        byte[] bytes = new byte[1024];
        in.read(bytes);
        String s1 = new String(bytes);
        System.out.println(s1);
        reader.close();
        out.close();
        in.close();
        socket.close();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值