22.网络编程

网络编程

第一节:常识普及
1.1 计算机网络

所谓计算机网络,就是把分布在不同区域的计算机与专门的外部设备用通信线路相互连接成一个规模大,而且功能强的网络系统,从而使得计算机之间可以相互传递信息,共享数据、软件等资源。
网络分类 :按照地理范围:广域网( Internet) ,城域网( 一个城市、多个城市,长城宽带),局域网(机房)

1.2 网络编程

所谓网络编程(不是网站编程),指的就是在同一个网络中不同机器之间的通信。

1.3 网络参考模型

OSI参考模型:包括七层:[物理层]、[数据链路层]、[网络层]、[传输层]、[会话层]、[表示层]和[应用层]

TCP/IP参考模型:包括四层:

1.链路层(数据链路层/物理层):包括操作系统中的设备驱动程序、计算机中对应的网络接口卡

2.网络层:处理分组在网络中的活动,比如分组的选路。

3.传输层:主要为两台主机上的应用提供端到端的通信。

4.应用层(应用层/表示层/会话层):负责处理特定的应用程序细节。

​ [外链图片转存失败(img-5EC07jsW-1565839270460)(tcp.png)]

1.4 通信协议

​ 需要通信的设备之间需要实现相同的通信协议

​ TCP/IP网络分层 :链路层、网络层、传输层、应用层

​ 通信协议分类:

​ 网络层IP协议:IPV4和IPV6,互联网协议

​ 传输层协议:TCP和UDP

​ 应用层协议:HTTP、FTP、SMTP、POP3

TCP协议:TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议。数据大小无限制。建立连接的过程叫三次握手,断开叫四次断开。

UDP协议:UDP 是User Datagram Protocol的简称, 中文名是用户数据报协议,是TCP/IP参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务,每个包的大小64Kb。

IP协议:[Internet Protocol]网际协议,能使连接到网上的所有计算机网络实现相互通信的一套规则,规定了计算机在因特网上进行通信时应当遵守的规则。IP协议中包含一块非常重要的内容就是为计算机分配了一个唯一标识即IP地址。

第二节:计算机之间通信
2.1 IP地址

IP地址指的是互联网地址(Internet Protocol Address ) ,是联网设备与互联网之间的唯一标识,在同一个网段中,IP地址是唯一的。
IP地址是数字型的,是一个32位的整数,通常将其分成4个8位的二进制数,每8位之间用圆点隔开, 每个8位整数可以转换为一个0~255的十进制整数,例如:202.9.128.88
分为IPV4和IPV6
查看ip:ipconfig
​IPV4地址分类
A类:保留给政府结构,1.0.0.1 ~ 126.255.255.254

B类:分配给中型企业,128.0.0.1 ~ 191.255.255.254

C类:分配给任何需要的个人,192.0.0.1 ~ 223.255.255.254

D类:用于组播,224.0.0.1 ~ 239.255.255.254

E类:用于实验,240.0.0.1 ~ 255.255.255.254

回环地址:127.0.0.1,指本机,一般用于测试使用,使用ping命令测试: ping 127.0.0.1
IP地址可以唯一的确定网络上的一个通信实体,但一个通信实体可以有多个通信程序同时提供网络服务,此时还需要使用端口。

2.2 端口

​ 数据的发送和接收都需要通过端口出入计算机,端口号用于唯一标识通信实体上进行网络通讯的程序,同一台机器上不能两个程序占用同一个端口。

​ 端口号的取值范围:0~65535

​ 查看端口占用: netstat -ano

​ 端口分类:

​ 公认端口:0~1023

​ 注册端口:1024~49151

​ 动态或私有端口:49152~65535

​ 常用端口:

​ mysql:3306

​ oracle:1521

​ tomcat:8080

​ web服务器(http):80

​ ftp服务器:21

​ SMTP 25

​ POP3 110

第三节:相关类的使用
3.1 InetAddress类

Java提供了InetAddress类来代表ip地址,是对ip地址的抽取和封装,有两个子类:Inet4Address,Inet6Address,分别表示IPv4和IPv6

​ 常用方法:
使用InetAddress 类的对象来表示 主机名和IP地址/域名和IP地址。getHostName返回域名/主机名。getHostAddress返回IP地址。创建时可以通过
InetAddress[] arr = InetAddress.getAllByName(“www.baidu.com”);的方式来创建(getByName/getAllByName)。

package per.damon.test05;

import java.net.InetAddress;

/**
 * @Classname Demo
 * @Date 2019/8/15 11:19
 * @Created by damon
 * @Description TODO
 */
public class Demo {
    public static void main(String[] args) throws Exception {
        //1.获取主机:主机名称和ip地址
        InetAddress id1 = null;
        id1 = InetAddress.getLocalHost();
        //DESKTOP-BE7UE7F/10.9.21.234
        System.out.println(id1);

        //2.获取ip地址的字符串表示形式
        String str1 = id1.getHostAddress();
        System.out.println(str1);//10.9.21.234

        //3.获取主机名
        String str2 = id1.getHostName();
        System.out.println(str2);
        
        //4 根据给定的域名获取ip地址
        InetAddress id3 = InetAddress.getByName("www.baidu.com");
        //www.baidu.com/115.239.211.112
        System.out.println(id3);

        //5.根据主机或者ip地址获取所有InetAddress对象
        InetAddress[] arr = InetAddress.getAllByName("www.baidu.com");
        for (InetAddress address : arr) {
            //www.baidu.com/115.239.210.27
            System.out.println(address.toString());
            //115.239.210.27
            System.out.println(address.getHostAddress());
            //www.baidu.com
            System.out.println(address.getHostName());
            System.out.println("--------------------");
        }
    }
}

3.2 URLEncoder类和URLDecoder类

​ <>URL把中文字符转换成网络传输数据格式

package per.damon.test05;

import java.net.URLDecoder;
import java.net.URLEncoder;

/**
 * @Classname Demo2
 * @Date 2019/8/15 12:41
 * @Created by damon
 * @Description TODO
 */
public class Demo2 {
    public static void main(String[] args) throws Exception{
        String str = "好久不见";
        String encode = URLEncoder.encode(str, "utf-8");//%E5%A5%BD%E4%B9%85%E4%B8%8D%E8%A7%81
        System.out.println(encode);
        String say = URLDecoder.decode(encode, "utf-8");//好久不见
        System.out.println(say);
    }
}

第四节:基于TCP的网络编程
4.1 概念

​ TCP:Transmission Control Protocol,传输控制协议,基于字节流的传输层通信协议。

​ 特点:

​ 1.面向连接

​ 2.安全、可靠

​ 3.传输数据大小限制,一旦连接建立,双方可以按统一的格式传输任意多的数据。

​ 4.效率低

​ TCP的三次握手

​ a.客户端向服务端发送一个请求

​ b.服务端收到请求后,回客户端一个响应

​ c.客户端向收到服务端的响应后,回服务端一个确认信息

4.2 基于TCP的Socket(套接字)通信模型

Socket是操作系统提供的一种底层的通信机制,Java仅仅是对底层的socket的一种封装。供开发人员方便使用。
注意,当一个程序设计读和写两个操作的时候,读写流要在最后一起关闭,以免出现java.net.SocketException: socket closed异常。

第五节:Socket和ServerSocket

accept抛异常

Throws:
IOException - if an I/O error occurs when waiting for a connection.
SecurityException - if a security manager exists and its checkAccept method doesn’t allow the operation.
SocketTimeoutException - if a timeout was previously set with setSoTimeout and the timeout has been reached.
IllegalBlockingModeException - if this socket has an associated channel, the channel is in non-blocking mode, and there is no connection ready to be accepted

socket.getInputStream抛出的异常:

if an I/O error occurs when creating the input stream, the socket is closed, the socket is not connected, or the socket input has been shutdown using shutdownInput()

5.1 客户端发送消息,服务端接收消息并回复。

客户端

package per.damon.test06;

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

/**
 * @Classname Client
 * @Date 2019/8/15 12:52
 * @Created by damon
 * @Description TODO
 */
public class Client {
    public static void main(String[] args) throws Exception{
        //创建并连接写法
//        Socket socket = new Socket();
//        socket.connect(new InetSocketAddress("10.9.21.234",7777));
        //另外一种写法
        Socket socket = new Socket("10.9.21.234", 7777);
        OutputStream os = socket.getOutputStream();
        //BufferedWriter的构造需要传入Writer对象,OutputStremWriter(字符)需要传入outputStream对象(字节)
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(os));
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        bw.write("真的是好久不见鸭");
        bw.newLine();
        bw.flush();//如果不flush服务器接收不到数据,close()会清空缓存,达到flush的效果
        System.out.println("发送成功");
        String data = br.readLine();//readLine有返回值,为String
        System.out.println(socket.getInetAddress().getHostAddress()+"说:"+data);
        br.close();
        bw.close();
        socket.close();
    }
}

服务器端:

package per.damon.test06;

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

/**
 * @Classname Server
 * @Date 2019/8/15 13:06
 * @Created by damon
 * @Description 客户端
 */
public class Server {
    public static void main(String[] args) throws Exception {
        ServerSocket listener = new ServerSocket(7777);
        System.out.println("客户端开启成功");//放在accept前面,accept为阻塞方法

        Socket socket = listener.accept();
        BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        String data = br.readLine();//readLine有返回值,为String
        System.out.println(socket.getInetAddress().getHostAddress() + "说:" + data);
        bw.write("你也是好久不见鸭");
        bw.flush();
        bw.newLine();
        System.out.println("发送成功");
        br.close();
//        bw.close();//Socket closed
        socket.close();
        listener.close();
    }
}

5.2 多个客户端和一个服务端通信【线程版本】

​ 客户端(几乎不做改变)

package per.damon.test07;

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

/**
 * @Classname Client
 * @Date 2019/8/15 14:48
 * @Created by damon
 * @Description 客户端,实际跟连接的客户,
 * 运行后向服务器发送数据
 */
public class Client {
    public static void main(String[] args) throws Exception{
        Socket socket = new Socket("10.9.21.234", 7777);
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        Scanner input = new Scanner(System.in);
        while(true){//只要用户不输入溜了溜了,则一直接收数据
            String d = input.next();
            bw.write(d);
            bw.newLine();
            bw.flush();
            if (d.equals("溜了溜")){
                break;
            }
        }
        bw.close();
        socket.close();
    }
}

核心代码:服务器线程类

package per.damon.test07;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

/**
 * @Classname ClientThread
 * @Date 2019/8/15 15:01
 * @Created by damon
 * @Description 聊天线程
 * 相当于new socket,但是这个套接字是在另一个线程(相对于主线程)中运作
 * 是这个聊天室程序的核心关键
 */
public class ChatThread extends Thread {
    private Socket socket;//接受信息,打印到控制台,作为连接

    public ChatThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        if(socket != null){
            BufferedReader br = null;//在finally语句块中关闭,需要再try外面声明
            //socket.getInputStream():异常退出时会抛出异常,使用try-catch接收
            //并打印到控制台,xxx异常退出了
            try {
                br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                while (true){
                    String data = br.readLine();
                    System.out.println(socket.getInetAddress().getHostAddress()+"说:"+data);
                    if(data.equals("溜了溜了")){
                        break;
                    }
                }
            } catch (IOException e) {
                System.out.println(socket.getInetAddress().getHostAddress()+"异常退出了");
            }
            finally {
                try {
                    socket.close();
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

聊天室主程序

package per.damon.test07;

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

/**
 * @Classname Server
 * @Date 2019/8/15 14:48
 * @Created by damon
 * @Description 主程序,负责开启线程
 */
public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket listener = new ServerSocket(7777);
        System.out.println("聊天室已启动");
        try {
            while(true){
            	//每次都会创建一个新的套接字,每次套接字的属性都不同,并不会出现问题
            	//就像在循环中也可以多次创建int类型的变量一个,每次都不同,与上次无关。
                Socket socket = listener.accept();//accept会抛出异常
                new ChatThread(socket).start();
            }
        } catch (IOException e) {
            System.out.println("聊天室异常关闭!");
        } finally {
            listener.close();
        }
    }
}

5.4网络传输二进制文件(以图片为例):

核心关键:socket.getInputStream/getOutputStream本身返回的就是字节流,另外,文件的复制是将端口中得到的数据先存入堆中,然后再将其写入硬盘。
客户端:

package per.damon.test08;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.net.Socket;

/**
 * @Classname Demo
 * @Date 2019/8/15 15:38
 * @Created by damon
 * @Description 上传文件,客户端
 */
public class FileClient {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("10.9.21.234", 7777);
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("ascii.jpg"));
        //因为是字节流,所以创建的对象会少一个
        BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream());
        byte[] bytes = new byte[1024];
        int len = -1;
        while((len = bis.read(bytes)) > 0){
            bos.write(bytes, 0, len);
            bos.flush();
        }
        //关闭
        bis.close();
        bos.close();
        socket.close();
    }
}
package per.damon.test08;

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

/**
 * @Classname FileServer
 * @Date 2019/8/15 16:37
 * @Created by damon
 * @Description TODO
 */
public class FileServer {
    public static void main(String[] args) throws Exception{
        ServerSocket listener = new ServerSocket(7777);
        Socket socket = listener.accept();
        InputStream is = socket.getInputStream();//getInputStream返回的就是一个字节输入流
        OutputStream os = new FileOutputStream("copy.jpg");
        byte[] bytes = new byte[1024];
        int len = -1;
        //拿出数据,放入堆中,然后再将其存储到硬盘
        while((len = is.read(bytes)) >0){
            os.write(bytes);
        }
        os.close();
        listener.close();
        socket.close();
        is.close();
    }
}

Tcp聊天室转发消息

聊天主程序:负责创建监听器和同步集合,将新客户加入集合

package per.damon.test09;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * @Classname Server
 * @Date 2019/8/15 19:36
 * @Created by damon
 * @Description 主程序,负责接受并创建新的聊天套接字
 * 线程,同时创建一个线程安全的map集合用来存放客户端
 */
public class Server {
    public static void main(String[] args) throws Exception {
        ServerSocket listener = new ServerSocket(7777);//仅需要绑定端口号
        //创建一个互斥map集合,解决线程安全问题。
        Map<String,Socket> maps = Collections.synchronizedMap(new HashMap<>());
        System.out.println("聊天室开始运作");
        try {
            while (true) {
                Socket socket = listener.accept();
                System.out.println(socket.getInetAddress().getHostAddress()+"连接成功");	
                //将新的客户加入集合,且这里的key不可能相同,
                //因为就算客户端相同,端口号也一定不同
                maps.put(socket.getInetAddress().getHostAddress(), socket);
                new ChatThread(socket,maps).start();//线程的创建需要传入map
            }
        } catch (IOException e) {
            System.out.println("聊天室结束!");
        }finally {
            listener.close();
        }
    }
}

ChatThread线程,在主程序中实际处理客户端消息的线程

package per.damon.test09;

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

/**
 * @Classname ChatThread
 * @Date 2019/8/15 19:36
 * @Created by damon
 * @Description 每次主程序接收到新的套接字则在
 * 创建一个线程处理该套接字中输入和接受。数据传入
 * 到主程序中创建的map集合中的其他套接字中
 */
public class ChatThread extends Thread {
    private Socket socket;
    //这个map集合是客户端间传输数据的关键
    private Map<String, Socket> maps;//实际传入的应当是一个互斥键值对,由主程序控制

    public ChatThread(Socket socket, Map<String, Socket> maps) {
        this.socket = socket;
        this.maps = maps;
    }
    
    @Override
    public void run() {
        //获取客户端向服务器发送的消息
        if (socket != null) {
            BufferedReader br = null;
            BufferedWriter bw = null;//什么时候才能关闭?
            //bw不关闭,bw负责写入本客户端给另一个客户端发送的数据何时关闭应当由
            //客户端那头决定,而不是本线程,本线程只是为客户端套接字服务的一个线程
            try {
                br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                while (true) {
                    String data = br.readLine();
                   //客户端如果是那头的不仅仅是溜了溜了退出,还有886退出,那么客户端
                    //是正常退出,但这头仅能获得流.close()发送的null,此时需要同时关闭socket
                    if (data == null) {
                        System.out.println(socket.getInetAddress().getHostAddress() + "退出了");
                        maps.remove(socket.getInetAddress().getHostAddress());
                        break;
                    }
                    System.out.println(socket.getInetAddress().getHostAddress() + "说:" + data);
                    if (data.equals("溜了溜了")) {
                        maps.remove(socket.getInetAddress().getHostAddress());
                        break;
                    }
                    //截取数据并转发
                    System.out.println("---------------------------");
                    //如果不需要转发:则直接继续循环获取客户端发送的数据
                    if (data.indexOf(":") < 0) {
                        continue;
                    }
                    String ip = data.substring(0, data.indexOf(":"));
                    String pattern = "((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})(\\.((2(5[0-5]|[0-4]\\d))|[0-1]?\\d{1,2})){3}";
                    if (ip != null) {
                        boolean matches = ip.matches(pattern);
                        if (matches) {
                        	//创建该线程时传入的socket对象是新加入进来的客户端套接字
                            //如果ip匹配则创建一个套接字对象与被转发ip进行连接,两者是
                            //完全不同的意义(ip不同,可以自己给自己发,此时就是ip相同的套接字,端口号不同)
                            Socket socket = maps.get(ip);
                            //上面socket的端口号随机分配,可以参考serversoket新建套接字
                            if (socket != null) {
                                //2 转发
                                synchronized (socket) {
                                    //转发时创建的套接字地址与传入ip的地址相同,端口随机分配。
                                    bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                                    String address = this.socket.getInetAddress().getHostAddress();
                                    //字符串的substring如果只传入一个参数则其后都要
                                    bw.write(address + "说:" + data.substring(data.indexOf(":") + 1));
                                    bw.newLine();
                                    bw.flush();
                                    System.out.println(address + "给" + ip + "转发了数据");
                                }
                            }
                        }
                    }

                }
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println(socket.getInetAddress().getHostAddress() + "异常退出了");
                maps.remove(socket.getInetAddress().getHostAddress());
            } finally {
                try {
                    br.close();
                    socket.close();
			//这是服务器端的套接字线程,何时关闭写应当由客户端来判断,不需要关闭
//                        bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

客户端:创建两个线程分别处理事务

package per.damon.test09;

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

/**
 * @Classname Client
 * @Date 2019/8/15 19:36
 * @Created by damon
 * @Description 客户端类
 * 使用两个线程分别完成发送数据和接收数据
 * 接受到的数据直接在客户端的控制台打印
 * 千万记得创建的线程需要开始
 *
 */
public class Client {
    public static void main(String[] args) throws Exception{
        Socket socket = new Socket("10.9.21.234",7777);//不仅要指定端口号,还要指定ip地址,不指定则只能对本机发送消息。

       //发送消息线程
        new Thread(new Runnable() {

            @Override
            public void run() {
                BufferedWriter bw = null;
                try {
                    bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                    Scanner input = new Scanner( System.in);
                    while(true){
                        String data = input.next();
                        bw.write(data);
                        bw.newLine();
                        bw.flush();
                        if(data.equals("溜了溜了")){
                            break;
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }finally {
                    try {
                        bw.close();
                        //如果考虑服务器不会关闭你的套接字发送端结束即可
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

        //获取转发数据
        new Thread(new Runnable() {
            @Override
            public void run() {
                BufferedReader br = null;
                try {
                    //从套接字中获取消息
                    br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    while (true) {
                        String data = br.readLine();
                        if(data == null){
                            break;
                        }
                        System.out.println(data);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                finally {
                    try {
                        br.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();

    }
}
5.5 TCP实现注册登录

首先是最熟悉的客户端线程

package per.damon;

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

/**
 * @Classname Client
 * @Date 2019/8/23 22:48
 * @Created by damon
 * @Description 客户端程序,发出连接请求,以及发送用户名和密码
 * 因为只是一次调用,且都是向主程序发送数据,具体判断由主程序的
 * 线程来做,所以具体代码相同,使用两个端口来确认是登录还是注册
 */
public class Client {
    public static void main(String[] args) {
        reg();
        log();
    }
    public static void reg(){
        doaction(7777);
    }
    public static void log(){
        doaction(777);
    }
    public static void doaction(int port){
        Socket socket =null;
        BufferedWriter bw =null;
        BufferedReader br = null;
        try {
             socket = new Socket("127.0.0.1",port);
             bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
             br = new BufferedReader(new InputStreamReader( socket.getInputStream()));
            Scanner input = new Scanner(System.in);
            System.out.println("请输入您的用户名");
            String userName = input.next();
            System.out.println("请输入您的密码");
            String passWord = input.next();
            String info = userName +"#"+ passWord;
            bw.write(info);
            bw.newLine();
            bw.flush();
            String reply = br.readLine();
            System.out.println(reply);
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                bw.close();
                br.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

然后是登录主线程

package per.damon;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Properties;

/**
 * @Classname Server
 * @Date 2019/8/23 21:41
 * @Created by damon
 * @Description 加载properties内的数据,
 * 创建登录和注册的线程用以读取客户端发送的两种数据
 */
public class Server {
    public static void main(String[] args) {

        //1 因为注册和登录都需要验证,所以将其取出放入主程序中
        Properties properties = new Properties();
        String f = "userinformation.properties";
        File file = new File(f);
        if(!file.exists()){
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //2 加载属性文件
        try {
            //properties.load从InputStream中读取数据
            properties.load(new FileReader(file));
        } catch (IOException e) {
            e.printStackTrace();
        }
        new Register(properties).start();
        new Login(properties).start();
    }
}

登录线程

package per.damon;

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

/**
 * @Classname Login
 * @Date 2019/8/23 21:43
 * @Created by damon
 * @Description 登录线程,需要注意的是properties的使用
 */
public class Login extends Thread{
    private Properties properties;
    public Login(Properties properties) {
        this.properties = properties;
    }

    @Override
    public void run() {
        BufferedReader br = null;
        BufferedWriter bw = null;
        ServerSocket listener = null;
        Socket socket = null;

        //注册
        //1 创建ServerSocket
        try {
            System.out.println("登录线程已启动");
            listener = new ServerSocket(777);
            socket = listener.accept();
            //2 创建字符缓冲流读取客户端发送的数据
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            //3 接收数据
            String data = br.readLine();
            //4 处理数据
            String[] infos = data.split("#");
            if(infos!=null){
                //判断是否包含用户名
                if(!properties.containsKey(infos[0])){
                    String username = infos[0];
                    String password = infos[1];
                    bw.write("登录失败,用户名不存在");
                    bw.newLine();
                    bw.flush();
                }else{
                    //判断密码是否正确
                    String password = (String) properties.get(infos[0]);
                    if(password.equals(infos[1])){
                        bw.write("登录成功");
                        bw.newLine();
                        bw.newLine();
                    }else{
                        bw.write("登陆失败,密码错误");
                        bw.newLine();
                        bw.newLine();
                    }


                }
            }


        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                bw.close();
                br.close();
                listener.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

注册线程

package per.damon;

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

/**
 * @Classname Register
 * @Date 2019/8/23 21:42
 * @Created by damon
 * @Description 注册线程
 */
public class Register extends Thread {

    private Properties properties;
    public Register(Properties properties) {
        this.properties = properties;
    }

    @Override
    public void run() {
        BufferedReader br = null;
        BufferedWriter bw = null;
        ServerSocket listener = null;
        Socket socket = null;

        //注册
        //1 创建ServerSocket
        try {
            System.out.println("注册线程已启动");
            listener = new ServerSocket(7777);
            socket = listener.accept();
            //2 创建字符缓冲流读取客户端发送的数据
            br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            //3 接收数据
            String data = br.readLine();
            System.out.println();
            //4 处理数据
            String[] infos = data.split("#");
            if(infos!=null){
                if(!properties.containsKey(infos[0])){
                    String username = infos[0];
                    String password = infos[1];
                    properties.setProperty(username, password);
                    FileWriter fw = new FileWriter("userinformation.properties");
                    properties.store(fw,"");
                    fw.flush();
                    fw.close();
                    bw.write("注册成功");
                    bw.newLine();
                    bw.flush();
                }else{
                    bw.write("用户名已存在");
                    bw.newLine();
                    bw.newLine();
                }
            }


        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                bw.close();
                br.close();
                listener.close();
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

第六节:UDP编程(了解)
6.1 概念

​ User Datagram Protocol的简称,用户数据包协议,提供面向事务的简单不可靠信息传送服务

​ 特点:

​ a.不安全

​ b.无连接

​ c.效率高

​ d.UDP传输数据时是有大小限制的,每个被传输的数据报必须限定在64KB之内

6.2 DatagramSocket和DatagramPacket

DatagramSocket: 数据报套接字,表示用来发送和接收数据报包的套接字。

DatagramPacket:此类表示数据报包。每个包最大64kb。

案例:

发送方

public class Sender {
	public static void main(String[] args) {
		//端口号表示的是指定的接收方的端口号,而发送方的端口是由系统自动分配的
		sendMessage("10.31.165.42", 6666,"你好啊");
	}
	
	/**
	 * 发送数据
	 * @param ip	指定接收方的ip地址
	 * @param port	指定接收方的端口号
	 * @param message 需要发送的信息
	 */
	public static void sendMessage(String ip,int port,String message) {
		//1.实例化DatagramSocket的对象
		//注意:和流的使用类似,使用套接字完成之后需要关闭
		DatagramSocket socket = null;
		
		try {
			socket = new DatagramSocket();
			
			//2.将需要发送的数据封装为数据报包
			/**
			 * DatagramPacket(byte[] buf, int length, InetAddress address, int port) 
          构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
			 */
			DatagramPacket packet = new DatagramPacket(message.getBytes(), message.length(), InetAddress.getByName(ip), port);
			//3.发送
			/**
			 *  void send(DatagramPacket p) 
          从此套接字发送数据报包。 
			 */
			//将数据写入网络的过程
			socket.send(packet);
			
		} catch (SocketException e) {
			// 父类为IOException
			e.printStackTrace();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

接收方

public class Receiver {
	public static void main(String[] args) {
		//1.实例化DatagramSocket的对象
		//需要进行绑定端口号:由发送方发送来的端口号进行决定
		/**
		 *   DatagramSocket(int port) 
          创建数据报套接字并将其绑定到本地主机上的指定端口。 
		 */
		DatagramSocket socket = null;
		try {
			socket = new DatagramSocket(6666);
			
			//2.将接收到的数据封装到数据报包中
			/**
			 * DatagramPacket(byte[] buf, int length) 
          构造 DatagramPacket,用来接收长度为 length 的数据包。
			 */
			byte[] arr = new byte[1024];
			DatagramPacket packet = new DatagramPacket(arr, arr.length);
			
			System.out.println("等待接收数据~~~~~~~");
			
			//3.接收数据
			/**
			 *  void receive(DatagramPacket p) 
          从此套接字接收数据报包。 
			 */
			//注意:将数据从网络中读取出来
			socket.receive(packet);
			
			//4.获取发送方的详细信息
			//信息
			/**
			 *  byte[] getData() 
          返回数据缓冲区。 
			 */
			byte[] messages = packet.getData();
			String result = new String(messages, 0, messages.length);
			
			//获取发送方的ip地址
			/**
			 * InetAddress getAddress() 
          返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。 
			 */
			InetAddress address = packet.getAddress();
			String ip = address.getHostAddress();
			
			//获取消息是从发送发的哪个端口号发出来的
			/**
			 *  int getPort() 
          返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。 
			 */
			int port = packet.getPort();
			
			System.out.println(ip + ":" + port + "说:" + result);
				
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

总结:

1 计算机网络:目的:信息传递,资源共享

2 osi参考模型 物理层 数据链路层 网络层 传输层 会话层 表示层 应用层

3 Tcp Ip参考模型 网络层 互联网层 输出层 应用层

4 协议 TCP 传输控制协议 UDP 用户数据包协议 IP 网际协议

5 Ip地址 ipv4 32位 ipv6 128位

​ A 1-126

​ B 128-191

​ C 192-223

​ D

​ E

6 InetAddress 表示ip地址

7 TCP的socket编程

​ ServerSocket 服务器套接字

​ Socket 客户端套接字

8 Udp

​ DatagramSocket

​ DatagramPacket

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值