网络编程

复习

1.实现线程的三种方式
继承线程类 Thread 在子类中重写 run 方法。在run 方法中定义线程的任务。
实现 Runnable 接口。该接口的子类可以认为是任务类。将该子类对象作为实参传递给Thread 对象即可。
实现Callable 接口,实现call 方法。该子类是任务类。将该类的对象作为实参给 FutureTask 对象。FutureTask 对象对象是用来调度管理任务的。再将 FutureTask 对象 给Thread 类对象。
2. 线程同步的三种方式
同步代码块,建议使用当前类的Class 对象 作为监视器对象使用。
同步方法,实例方法同步的是this,静态方法同步的是当前类的Class对象。
Lock 接口的子类对象。ReentrantLock 使用 try-finally 一定要解锁。
3. wait、notify、notifyAll
wait(): 导致当前线程在当前对象上等待,进入阻塞状态,并释放对当前对象的锁。
notify(): 唤醒在当前对象上等待的某一个线程,从阻塞状态进入就绪状态。等待cpu的下次的调度执行。如果被唤醒的线程被cpu调度执行了,那么该线程一定要重新获得wait() 时候释放的锁的控制权。
notifyAll(): 唤醒在当前对象上等待的所有线程,从阻塞状态进入就绪状态。等待cpu的下次的调度执行。
4 sleep(long time) 、wait().
相同点:都会导致当前线程被阻塞、都可以通过控制参数的时间来解除线程阻塞状态。
不同点:1调用方式不同;2所定义的类不同,一个是Thread类的静态方法,一个是Object类的实例方法。3 sleep 可以在同步代码块内部或者是同步代码块外部使用,wait 必须在同步中使用。4 sleep 只能通过时间来解除阻塞,wait 可以通过其他的线程调用notify 唤醒 5 如果当前线程持有监视器对象的锁,那么在sleep 的过程中,是不会释放锁资源的。wait,等待之后,会释放锁资源。

第一节 网络通信三要素

1. 相关的概念 了解

网络:使用物理线路或者是无线线路将若干台计算机连接在一起的整体。
网络的分类:局域网 LAN、城域网 MAN、广域网 WAN。

2. 三要素

1.如何在网络中唯一标识一台计算机? ip地址
2.同一台计算机上的多个程序如何共用网络而不冲突? 网络端口
3.不同的计算机通信怎么才能互相理解? 使用相同的协议

3 .IP地址

IP地址: ⽤来在⽹络中标记⼀台电脑的⼀串数字, ⽐如192.168.1.1(c类); 在同一⽹络上是惟⼀的(用来标记唯一的一台电脑)IP地址由4个字节32bits 构成,每一个字节表示的范围是[0-255]。使用 【.】点分符将四个字节分隔组成。例子:192.168.151.241 。IP地址一共有232次幂 种状态,IPV4版本已经不足以支持现有的网络中的计算机的数量。
每⼀个IP地址包括两部分: ⽹络地址和主机地址
公网地址可以通过互联网直接访问,私网地址只能作为公司内部的局域网使用。
IP地址是作为计算机中唯一标识使用的。通过IP地址确定和互联网中的哪个计算机交互。
127.0.0.1 可以代表本机。进行本机的网络测试。
localhost 也可以代表本机IP地址。
主机号0,255两个数不能使用(网络号、广播地址)255.255.255.255:广播的IP地址。代表子网广播地址,代表了子网中的所有的主机。
互联网中访问其他的计算机,都是通过IP地址确定的。访问公司的域名本质上也是通过IP地址访问的。公司的域名和公司的服务器的IP地址有一个一一对应的关系。通过域名就可以获得对应到 IP 地址。DNS:域名解析器。作用就是进行域名到IP地址转换的。

A类IP地址 :由1字节的⽹络地址和3字节主机地址组成, ⽹络地址的最⾼位必须是“0”,地址范围1.0.0.1-126.255.255.254可⽤的A类⽹络有126个, 每个⽹络能容纳1677214个主机
B类IP地址:由2个字节的⽹络地址和2个字节的主机地址组成, ⽹络地址的最⾼位必须是“10”,地址范围128.1.0.1-191.255.255.254 可⽤的B类⽹络有16384个, 每个⽹络能容纳65534主机
C类IP地址:由3字节的⽹络地址和1字节的主机地址组成, ⽹络地址的最⾼位必须是“110”范围192.0.1.1-223.255.255.254 C类⽹络可达2097152个, 每个⽹络能容纳254个主机
D类IP地址:第⼀个字节以“1110”开始, 它是⼀个保留的地址。它并不指向特定的⽹络, ⽬前这⼀类地址被⽤在多点⼴播(一对多) 中多点⼴播地址⽤来⼀次寻址⼀组计算机 地址范围224.0.0.1-239.255.255.254
E类IP地址:以“1111”开始, 为将来使⽤保留 E类地址保留, 仅作实验和开发⽤

私有IP:本地局域网上的IP,专门为组织机构内部使用
在这么多⽹络IP中, 国际规定有⼀部分IP地址是⽤于我们的局域⽹使⽤, 属于私⽹IP, 不在公⽹中使⽤的, 它们的范围是:
10.0.0.0~10.255.255.255
172.16.0.0~172.31.255.255
192.168.0.0~192.168.255.255
私有IP:局域网通信,内部访问,不能在外网公用。私有IP禁止出现在Internet中,来自于私有IP的流量全部都会阻止并丢掉
公有IP:全球访问
IP地址127. 0. 0. 1~127. 255. 255. 255⽤于回路测试
测试当前计算机的网络通信协议
如: 127.0.0.1可以代表本机IP地址, ⽤ http://127.0.0.1 就可以测试本机中配置的Web服务器
常用来ping 127.0.0.1来看本地ip/tcp正不正常,如能ping通即可正常使用

⼦⽹掩码:是我们测量两个IP是否属于同一个网段的工具
⼦⽹掩码不能单独存在, 它必须结合IP地址⼀起使⽤
⼦⽹掩码只有⼀个作⽤, 就是将某个IP地址划分成⽹络地址和主机地址两部分
⼦⽹掩码的设定必须遵循⼀定的规则:与IP地址相同, ⼦⽹掩码的长度也是32位,左边是⽹络位, ⽤⼆进制数字“1”表示;右边是主机位, ⽤⼆进制数字“0”表示
假设IP地址为“192.168.1.1”⼦⽹掩码为“255.255.255.0”。其中, “1”有24个, 代表与此相对应的IP地址左边24位是⽹络号;“0”有8个, 代表与此相对应的IP地址右边8位是主机号

4. 端口号

端口号: 用来标记区分进程
⼀台拥有IP地址的主机可以提供许多服务, ⽐如HTTP(万维⽹服务) 、 FTP(⽂件传输) 、 SMTP(电⼦邮件) 等, 这些服务完全可以通过1个IP地址来实现。 那么, 主机是怎样区分不同的⽹络服务呢?
显然不能只靠IP地址, 因为IP地址与⽹络服务的关系是⼀对多的关系。实际上是通过“IP地址+端⼝号”来区分不同的服务的。

端⼝号是一个数字,只有整数, 范围是从0到65535 (分为知名和动态两种)是一个逻辑端口,不是一个物理设备。每个通信的软件都唯一的使用了某一个端口进行通信。
知名端⼝是众所周知的端⼝号(用来做固定事情), 范围从0到1023 80端⼝分配给HTTP服务(网站) oracle 1521 mysql 3306 21端⼝分配给FTP服务(文件下载)。可以理解为, ⼀些常⽤的功能使⽤的号码是固定的 。
动态端⼝的范围是从1024到65535,之所以称为动态端⼝, 是因为它⼀般不固定分配某种服务, ⽽是动态分配。动态分配是指当⼀个系统进程或应⽤程序进程需要⽹络通信时, 它向主机申请⼀个端⼝, 主机从可⽤的端⼝号中分配⼀个供它使⽤
建议我们自定义的网络程序使用的端口的范围[1024~5000]。
端口号确定了和主机中的哪个软件交互。

5. 协议

协议:约定好的规范 。 例如:IP、TCP、UDP、http

根据TCP/IP协议簇功能的不同,将它分为了几种层次( TCP/IP协议簇层次划分)(重点记住)
(写代码按四层划分)
应用层:应用程序间沟通的层,不同的文件系统有不同的文件命名原则和不同的文本行表示方法等,不同的系统之间传输文件还有各种不兼容问题,这些都将由应用层来处理
传输层:在此层中,它提供了节点间的数据传送服务,如传输控制协议(TCP)、用户数据报协议(UDP)等,这一层负责传送数据,并且确定数据已被送达并接收。
网络层:负责提供基本的数据包传送功能,让每一块数据包都能够到达目的主机。网络层接收由更低层发来的数据包,并把该数据包发送到更高层,相反,IP层也把从TCP或UDP层接收来的数据包传送到更低层。
网络接口层(链路层):对实际的网络媒体的管理,定义如何使用实际网络来传送数据(处理机械的、电气的和过程的接口)

(理论上由七层组成)
物理层
数据链路层
网络层
传输层
会话层
表示层
应用层

TCP:
1.传输控制协议。Transfer control protocol
2.特点:安全的、可靠、面向连接、全双工的、字节流协议。
3.面向连接:通信的两端,先建立一个独立的信息的通道,然后使用该通道进行信息的交互。类似打电话。
4.全双工的:收发信息可以同时进行。
5.安全可靠的实现的机制:三次握手、四次挥手。
TCP协议:三次握手过程和四次挥手详解

三次握手建立连接

第一次握手:客户端发送syn包(seq=x)到服务器,并进入SYN_SEND状态,等待服务器确认;第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。

传输数据过程:

a.超时重传超时重传机制用来保证TCP传输的可靠性。每次发送数据包时,发送的数据报都有seq号,接收端收到数据后,会回复ack进行确认,表示某一seq 号数据已经收到。发送方在发送了某个seq包后,等待一段时间,如果没有收到对应的ack回复,就会认为报文丢失,会重传这个数据包。b.快速重传接受数据一方发现有数据包丢掉了。就会发送ack报文告诉发送端重传丢失的报文。如果发送端连续收到标号相同的ack包,则会触发客户端的快速重 传。比较超时重传和快速重传,可以发现超时重传是发送端在傻等超时,然后触发重传;而快速重传则是接收端主动告诉发送端数据没收到,然后触发发送端重传。c.流量控制这里主要说TCP滑动窗流量控制。TCP头里有一个字段叫Window,又叫Advertised-Window,这个字段是接收端告诉发送端自己 还有多少缓冲区可以接收数据。于是发送端就可以根据这个接收端的处理能力来发送数据,而不会导致接收端处理不过来。 滑动窗可以是提高TCP传输效率的一种机制。d.拥塞控制滑动窗用来做流量控制。流量控制只关注发送端和接受端自身的状况,而没有考虑整个网络的通信情况。拥塞控制,则是基于整个网络来考虑的。考虑一下这 样的场景:某一时刻网络上的延时突然增加,那么,TCP对这个事做出的应对只有重传数据,但是,重传会导致网络的负担更重,于是会导致更大的延迟以及更多 的丢包,于是,这个情况就会进入恶性循环被不断地放大。试想一下,如果一个网络内有成千上万的TCP连接都这么行事,那么马上就会形成“网络风 暴”,TCP这个协议就会拖垮整个网络。为此,TCP引入了拥塞控制策略。拥塞策略算法主要包括:慢启动,拥塞避免,拥塞发生,快速恢复。

四次挥手断开连接:

第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不会再给你发数据了(当 然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但此时主动关闭方还可以接受数据。

第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。

第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。

第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。

UDP:
1.用户数据报协议 User datagram protocol
2.特点:不安全的,不可靠的,面向无连接的,基于数据报的协议。类似:写信。
3.优点:效率高
4.缺点:不安全、不可靠、包裹大小有限制。64K。

第二节 相关的类 java.net

1. InetAddress

说明:该类封装的是主机的 IP地址+主机名

/**
 *InetAddress
 */
public class TestInetAddress {
    public static void main(String[] args) throws Exception {
        //得到本机的InetAddress对象
        InetAddress host=InetAddress.getLocalHost();
        System.out.println(host);
        //获得名字
        System.out.println(host.getHostName());
        //获得IP地址
        System.out.println(host.getHostAddress());

        //获得别人的(没有获取到主机名)
        InetAddress otherAddress=InetAddress.getByName("192.168.151.183");
        System.out.println(otherAddress);

        //得到互联网中主机的InetAddress对象
        InetAddress[] address1=InetAddress.getAllByName("www.baidu.com");
        for (InetAddress add:address1) {
            System.out.println(add);
        }

    }
}

2. URL

URL是Uniform Resource Location的缩写,译为“统一资源定位符”。通俗地说,URL是Internet上用来描述信息资源的字符串。


/**
 * java.net.URL  (创建URL对象的时候里面的参数是——网址)
 * https://news.163.com/19/1030/09/ESNO4CC0000189FH.html
 * 一个URL由四部分组成:
 * 协议:https
 * 主机地址:news.163.com
 * 端口号:协议默认 80
 * 文件:/19/1030/09/ESNO4CC0000189FH.html
 */
public class TestUrl {
    public static void main(String[] args) throws Exception{
        test();
    }
    //使用url获得url的四个属性
    static void test() throws Exception{
        URL url=new URL("https://news.163.com/19/1030/09/ESNO4CC0000189FH.html");
        System.out.println( url.getProtocol()); //协议 https
        System.out.println(url.getHost());  // 主机地址 news.163.com
        System.out.println(url.getPort());   //端口 1
        System.out.println(url.getFile());  //文件/19/1030/09/ESNO4CC0000189FH.html
        //得到网络传输文件的字节输入流
        InputStream is=url.openStream();
        //还可以这样,这个知道有这么回事儿就行
       // URLConnection urlConnection=url.openConnection();
       // InputStream is=url.openStream();

        //isr负责从底层字节流中读取字节转换为字符,依赖utf-8
        InputStreamReader isr=new InputStreamReader(is,"GBK");
        //br负责从isr解码的字符数据中,一行一行的去读取。
        BufferedReader br=new BufferedReader(isr);
        String str=br.readLine();
        while(str!=null){
            System.out.println(str);
            str=br.readLine();
        }
        br.close();
    }
}

第三节 socket 编程 UDP

1 .使用UDP协议的DatagramSocket

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

/**
 *使用DatagramSocket进行数据的发送和接收--接收
 */
public class TestUdp {
    public static final int RECEIVE_PORT=1222;
    public static void main(String[] args) {
        try {
            receive();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    static void receive() throws Exception{
        //创建用于收发数据的socket对象,必须指定接收的端口号
        DatagramSocket ds=new DatagramSocket(RECEIVE_PORT);

        byte[] buf=new byte[1024];
        //创建dp对象存储收到的数据
        //dp的作用就是将收到的字节数据,存到buf中
        DatagramPacket dp=new DatagramPacket(buf,buf.length);//DatagramPacket的形参是(byte[],int length)
        //接收数据  是一个阻塞式的方法  没有数据就一直等待
        ds.receive(dp);

        //将接收到的字节数组还原为原来的数据  解析dp
        int length=dp.getLength();
        String str=new String(buf,0,length);
        System.out.println("来信"+str);

        //回信息
        str="收到来信,谢谢";
        buf=str.getBytes();
        //获得对方的InetAdress对象
        InetAddress address=dp.getAddress();
        //获得发送发的端口号
        int port=dp.getPort();
        dp=new DatagramPacket(buf,buf.length,address,port);
        ds.send(dp);

        ds.close();

    }
}

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

/**
 * 使用DatagramSocket进行数据的发送和接收
 */
public class TestUdp {
    public static void main(String[] args) {
        try {
            sendMsg();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    static void sendMsg() throws Exception{
        //创建发送包裹的快递员,可以指定端口号,也可以有系统分配
        DatagramSocket ds=new DatagramSocket();
        //提供书信的数据  信
        String str= "你好!";
        byte[] buf=str.getBytes();

        //将字节数组封装到信封中DatagramPacket
        DatagramPacket dp=new DatagramPacket(buf,buf.length, InetAddress.getByName("192.168.151.182"),1222);
        //发送数据
        ds.send(dp);

        //接收回信
        buf=new byte[1024];
        dp=new DatagramPacket(buf,buf.length);
        ds.receive(dp);
        System.out.println("收到回信"+new String(buf,0,dp.getLength()));

        //关闭
        ds.close();
    }
}

2. DatagramPacket收发对象

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 *发送对象Object o
 */
public class TestUdpSendObj {
    public static void main(String[] args) throws Exception{
        DatagramSocket ds=new DatagramSocket();
        User user=new User("用户1","123456");

        ByteArrayOutputStream baos=new ByteArrayOutputStream();
        ObjectOutputStream oos=new ObjectOutputStream(baos);
        oos.writeObject(oos);
        //得到user对象的字节数组
        byte[] buf=baos.toByteArray();
        //将字节数组封装到信封中,DataGramPAcket
        DatagramPacket dp=new DatagramPacket(buf,buf.length, InetAddress.getByName("192.168.151.182"),1222);
        //发送数据
        ds.send(dp);
        ds.close();
    }
}

//创建用户类继承序列化接口
class User implements Serializable{
    private String name;
    private String pwd;

    public User(String name, String pwd) {
        this.name = name;
        this.pwd = pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }
}
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 *接收对象Object o
 */
public class TestRecvObj {
    public static final int RECEIVE_PORT=1222;

    public static void main(String[] args) throws Exception {
        //创建用于收发数据的socket对象,必须指定接收的端口号
        DatagramSocket ds=new DatagramSocket(RECEIVE_PORT);
        byte[] buf=new byte[1024];
        //创建dp对象存储接收的数据
        //dp的作用就是将受到的字节数据存入到buf中
        DatagramPacket dp=new DatagramPacket(buf,buf.length);
        //接收数据 是一个阻塞式的方法  没有数据就一直等待
        ds.receive(dp);

        //使用字节数组还原user对象
        ObjectInputStream ois=new ObjectInputStream(new ByteArrayInputStream(buf));
        Object o=ois.readObject();
        System.out.println(o);
        ds.close();
    }
}

3.简易聊天室

//老板的代码
public class TestChat {

    static Frame frame;
    static TextArea area;
    static Label ipLabel;
    static Label msgLabel;
    static TextField ipField;
    static TextField msgField;

    static DatagramSocket ds;

    public static final int PORT = 1777;
    public static void main(String[] args)throws Exception {
        ds = new DatagramSocket(PORT);
        initFrame();
        receiveMsg();
    }

    //初始化窗口
    private static void initFrame() {
        frame = new Frame("尚学堂502聊天室");
        frame.setSize(500, 500);
        frame.setLocation(1360 - 500 >> 1, 700 - 500 >> 1);
        frame.setResizable(false);
        // 设置布局
        frame.setLayout(new FlowLayout());

        area = new TextArea(25, 68);
        area.setEditable(false);
        ipLabel = new Label("IP ADDRESS");
        msgLabel = new Label("SEND CONTENT");
        ipField = new TextField(50);
        msgField = new TextField(50);

        frame.add(area);
        frame.add(ipLabel);
        frame.add(ipField);
        frame.add(msgLabel);
        frame.add(msgField);

        addEnventListener();
        //默认不可见
        frame.setVisible(true);
    }

    //给frame 和 msgFiled 添加事件
    private static void addEnventListener() {
        //frame
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        //msgField
        msgField.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                //获得内容
                String text = msgField.getText();
                area.append("自己:" + text + "\r\n");
                msgField.setText("");
                
                //获得接收方的InetAddress对象
                InetAddress address = null;
                try {
                    address = InetAddress.getByName(ipField.getText());
                } catch (UnknownHostException e1) {
                    e1.printStackTrace();
                }
                //
                byte[] buf = text.getBytes();
                DatagramPacket dp = new DatagramPacket(buf, buf.length, address,PORT);
                try {
                    ds.send(dp);
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        });
    }

    //接收的方法
    static void receiveMsg()throws Exception{
//        主线程接收
        //创建dp 对象存储收到的数据
        byte[] buf = new byte[1024];
        //dp 的作用就是将接收到的字节数据,存到buf 中。
        DatagramPacket dp = new DatagramPacket(buf,buf.length);
        while(true){
            //接收数据   是一个阻塞式方法。没有数据就一直等待。
            ds.receive(dp);
            //将接收到的字节数组,还原为原来的数据  解析dp
            //得到dp接收到的字节数据的长度
            int length = dp.getLength();
            String str = new String(buf,0,length);
            //得到发送方的地址。
            String sendHostAddr = dp.getAddress().getHostAddress();
            //获得本机地址
            String localHostAddr = InetAddress.getLocalHost().getHostAddress();
            //屏蔽自己给自己发的消息
            if(sendHostAddr.equals(localHostAddr))
                continue;

            //将接收到的信息,添加到area 中
            area.append(sendHostAddr + " : "+str + "\r\n");
        }
    }

}

第四节. TCP 编程

Socket套接字(转)
所谓socket 通常也称作”套接字“,用于描述IP地址和端口,是一个通信链的句柄。应用程序通常通过”套接字”向网络发出请求或者应答网络请求。

Socket和ServerSocket类库位于java.net包中。ServerSocket用于服务器端,Socket是建立网络连接时使用的。在连接成功时,应用程序两端都会产生一个Socket实例,操作这个实例,完成所需的会话。对于一个网络连接来说,套接字是平等的,并没有差别,不因为在服务器端或在客户端而产生不同级别。不管是Socket还是ServerSocket它们的工作都是通过SocketImpl类及其子类完成的。

重要的Socket API:

java.net.Socket继承于java.lang.Object,有八个构造器,其方法并不多,下面介绍使用最频繁的三个方法,其它方法大家可以见JDK-1.3文档。

. Accept方法用于产生”阻塞”,直到接受到一个连接,并且返回一个客户端的Socket对象实例。”阻塞”是一个术语,它使程序运行暂时”停留”在这个地方,直到一个会话产生,然后程序继续;通常”阻塞”是由循环产生的。

. getInputStream方法获得网络连接输入,同时返回一个InputStream对象实例。
. getOutputStream方法连接的另一端将得到输入,同时返回一个OutputStream对象实例。

注意:其中getInputStreamgetOutputStream方法均会产生一个IOException,它必须被捕获,因为它们返回的流对象,通常都会被另一个流对象使用。
————————————————
原文链接:https://blog.youkuaiyun.com/siying8419/article/details/79735901

1.实现用户登陆

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

/**
 *客户端
 */
public class LoginClient {
    public static void main(String[] args) {
        Socket socket=null;
        try {
            socket=new Socket("192.168.151.182",LoginServer.SERVER_PORT);
            String str="name=maomao&pwd=123456";
            //使用输出流发送数据
            BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            bw.write(str);
            bw.newLine();
            bw.flush();

            //接收服务器端返回来的信息,是一个布尔值true or false
            DataInputStream dis=new DataInputStream(socket.getInputStream());
            boolean b=dis.readBoolean();
            System.out.println(b);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }

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

/**
 *服务器端
 */
public class LoginServer {
    public static final int SERVER_PORT=1222;
    public static void main(String[] args) {
        try {
            //指定端口提供创建连接服务
            ServerSocket ss=new ServerSocket(SERVER_PORT);
            System.out.println("等待客户端的连接。。。");
            //阻塞方法等待客户端的连接
            Socket socket=ss.accept();
            System.out.println("有连接过来:"+socket);
            BufferedReader br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
            String str=br.readLine();
            System.out.println("客户端发送的内容"+str);

            //解析str
            String name=str.split("&")[0].split("=")[1];
            String pwd=str.split("&")[1].split("=")[1];
            System.out.println("name="+name);
            System.out.println("pwd="+pwd);

            boolean result=false;
            if("maomao".equals(name)&&"123456".equals(pwd)){
                result=true;
            }
            DataOutputStream dos=new DataOutputStream(socket.getOutputStream());
            dos.writeBoolean(result);

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        }

    }
}

2. 文件上传

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

/**
 * 客户端上传文件
 */
public class UploadClient {
    public static void main(String[] args) {
        Socket socket=null;
        try {
            socket=new Socket("192.168.151.182", UpLoadSever.SEVER_PORT);

            File file=new File("E:/1.txt");

            //先将文件的名字和长度通知给服务器
            String name=file.getName();
            long len=file.length();
            DataOutputStream dos=new DataOutputStream(socket.getOutputStream());
            dos.writeUTF(name);
            dos.writeLong(len);
            dos.flush();

            //使用字节流读取本地的文件数据,数据通过输出流发送给服务器
            BufferedInputStream bis=new BufferedInputStream(new FileInputStream(file));
            BufferedOutputStream bos=new BufferedOutputStream(socket.getOutputStream());

            byte[] buf=new byte[100];
            int count=bis.read(buf);
            while(count!=-1){
                bos.write(buf,0,count);
                bos.flush();
                count=bis.read(buf);
            }

            //接收服务器反馈
            DataInputStream dis=new DataInputStream(socket.getInputStream());
            System.out.println(dis.readUTF());

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //关闭套接字
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

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

/**
 *服务器下载文件
 */
public class UpLoadSever {
    public static final int SEVER_PORT =1222;
    public static void main(String[] args) {
        ServerSocket ss=null;
        try {
            ss=new ServerSocket(SEVER_PORT);
            Socket socket=ss.accept();

            //接收客户端发送的名字和文件长度
            DataInputStream dis=new DataInputStream(socket.getInputStream());
            String str=dis.readUTF();
            long len=dis.readLong();

            //使用socekt输入流读取客户端发过来的文件数据
            BufferedInputStream bis=new BufferedInputStream(socket.getInputStream());
            //再用字节输出流写到本地
            BufferedOutputStream  bos=new BufferedOutputStream(new FileOutputStream("E:/sever/"+str));
            byte[] buf=new byte[10];
            int count=bis.read(buf);
            while(true){
                bos.write(buf,0,count);
                bos.flush();
                len -= count;
                if(len==0){
                    break;
                }
                count=bis.read(buf);
            }

            //给客户反馈
            DataOutputStream dos=new DataOutputStream(socket.getOutputStream());
            dos.writeUTF("文件长传成功");
            dos.flush();

            socket.close();

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                ss.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

3. 多线程网络编程

public class UploadServer {
    public static final int SERVER_PORT = 1777;
    //给服务器上传一个文件
    public static void main(String[] args) throws Exception{
        ServerSocket ss = new ServerSocket(SERVER_PORT);

        try {
            while(true){
                //主线程来响应客户端的请求
                Socket socket = ss.accept();
                new UploadThread(socket).start();
            }
        } finally {
            ss.close();
        }

    }


    static class UploadThread extends Thread{
        private Socket socket;
        UploadThread(Socket socket){
            this.socket = socket;
        }
        @Override
        public void run() {
            try {
                //接收客户端发送的名字
                DataInputStream dis = new DataInputStream(socket.getInputStream());
                String name = dis.readUTF();
                long len = dis.readLong();

                if(len > 1024*1024*12)
                    return;

                //使用socket 的输入流读取客户端发送过来的文件的数据。
                BufferedInputStream bis  = new BufferedInputStream(socket.getInputStream());
                String hostAddress = socket.getInetAddress().getHostAddress();

                //在用字节输出流写出到本地。
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("c:/server/"+hostAddress+name));
                byte[] buf = new byte[100];
                //读取的是客户端发送过来的文件的数据
                int count = bis.read(buf);
//        while(count != -1){
                while(true){
                    bos.write(buf,0,count);
                    bos.flush();
                    len -= count;
                    if(len == 0)
                        break;
                    count = bis.read(buf);
                }
                //给客户端反馈
                DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
                dos.writeUTF("文件上传成功!");
                dos.flush();

            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                if(socket != null){
                    try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

4.总结 网络编程

网络通信三要素:ip地址 决定了和网络中的哪个计算机通信; 端口号:决定了和计算机中的哪个软件通信; 通信协议:信息通信的规则。
TCP:长连接.

InetAddress 封装的是主机名+IP地址。
InetSocketAddress:封装的是IP地址+端口号。
DatagramSocket 使用底层的UDP协议。
Socket 使用底层 TCP协议。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值