【黑马程序员】网络编程——Java复习笔记

c——Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ——-

网络编程

什么是网络编程?

用Java语言实现计算机间数据的信息传递和资源共享

网络编程的三要素

  1. IP地址:网络中计算机的唯一标识
    a:点分十进制 xxx.xxx.xxx.xxx
    b:IP地址的组成 网络号段+主机号段
    c:InetAddress类 是个没有构造方法的类 用于获取ip等
  2. 端口
    是应用程序的标识。范围:0-65535。其中0-1024不用。
  3. 协议
    UDP:数据打包,每一次不超过64k,不建立连接,效率高,不可靠
    TCP:建立数据通道,无限制,效率稍低,可靠

Socket机制

  1. 通信两端都应该有Socket对象
  2. 所有的通信都是通过Socket间的IO进行操作的. 网络通信其实就是Socket通信
    Socket套接字:网络上具有唯一标识的IP地址和端口号组合在一起构成唯一能识别的标识符套接字

UDP协议发送和接收数据

发送:

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

/**
 * Created by mo on 15/11/21.
 * UDP发送数据
 *  创建Socket对象
 *  创建数据包
 *  发送数据包
 *  释放资源
 */
public class UDPsend {
    public static void main(String[] args) throws IOException {
        DatagramSocket datagramSocket = new DatagramSocket();

        byte[] arr = "Hello,UDP,我来了".getBytes();
//        InetAddress address = InetAddress.getByName("192.168.0.100");//接收端ip
//        int port = 10765;//接收端端口
//        DatagramPacket datagramPacket = new DatagramPacket(arr,arr.length,address,port);

        //精简版
        DatagramPacket datagramPacket = new DatagramPacket(arr,arr.length,InetAddress.getByName("192.168.0.100"),10765);

        datagramSocket.send(datagramPacket);

        datagramSocket.close();
    }
}

接收:

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


/**
 * Created by mo on 15/11/21.
 * UDP接收数据
 *
 *  创建接收端Socket对象
 *  创建数据包(接收数据)
 *  调用Socket对象的接收方法
 *  解包并显示在控制台
 *  释放资源
 */
public class UDPreceive {
    public static void main(String[] args)throws IOException{
        //接收数据要指定端口
        DatagramSocket datagramSocket = new DatagramSocket(10765);

        //创建数据包接收
        byte[] arr = new byte[1024];
        DatagramPacket datagramPacket = new DatagramPacket(arr,arr.length);

        //接收数据
        datagramSocket.receive(datagramPacket);

        //解析数据内容
//        int len = datagramPacket.getLength(); //获取数据实际长度
//        byte[] received = datagramPacket.getData();//获取数据缓冲区
        String s = new String(datagramPacket.getData(),0,datagramPacket.getLength());
        //获取对方ip
        String ip = datagramPacket.getAddress().getHostAddress();

        System.out.println(ip+":"+s);

        datagramSocket.close();
    }
}

案例1:键盘录入数据 直到录入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;


/**
 * Created by mo on 15/11/21.
 * 需求:键盘录入数据 直到录入886 客户端结束输入数据
 */
public class Send {
    public static void main(String[] args)throws IOException {
        //这是发送端
        System.out.println("请输入要发送的数据");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String line ;
        //创建UDP用的DatagramSocket对象
        DatagramSocket datagramSocket = new DatagramSocket();
        //读取键盘录入的数据
        while ((line = br.readLine())!=null){
            if (line.equals("886")){
                break;//如果是886则退出循环
            }
            //不是886,则创建数据包并把数据和接受者的端口+ip封装
            byte[] arr = line.getBytes();
            DatagramPacket datagramPacket = new DatagramPacket(arr,arr.length, InetAddress.getByName("192.168.0.100"),10765);
            //发送数据包
            datagramSocket.send(datagramPacket);

        }

        datagramSocket.close();

    }
}

/**
 * Created by mo on 15/11/21.
 * 这是接收端
 */
public class Received {
    public static void main(String[] args) throws IOException{
        //创建接收端DatagramSocket对象,传入端口号
        DatagramSocket datagramSocket = new DatagramSocket(10765);
        //循环接收数据
        while (true){
            byte[] arr = new byte[1024];
            DatagramPacket datagramPacket = new DatagramPacket(arr,arr.length);

            datagramSocket.receive(datagramPacket);

            String s = new String(datagramPacket.getData(),0,datagramPacket.getLength());

            String ip = datagramPacket.getAddress().getHostAddress();

            System.out.println(ip+":"+s);
        }

        //接收端应该一直开着 等待接收数据 所以不close
    }
}

案例2:多线程实现接收发送在同一窗口

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;

/**
 * Created by mo on 15/11/21.
 * 多线程实现接收发送在同一窗口
 */
public class SendAndReceived {
    public static void main(String[] args) throws SocketException {
        //创建接收和发送的Socket对象
        DatagramSocket sendSocket = new DatagramSocket();
        DatagramSocket receiveSocket = new DatagramSocket(10765);

        //创建和启动线程
        Thread send = new Thread(new SendThread(sendSocket));
        Thread receive = new Thread(new ReceiveThread(receiveSocket));

        send.start();
        receive.start();

    }
}

/**
 * 这是发送线程类
 */
class SendThread implements Runnable{
    //构造方法接收socket对象
    private DatagramSocket datagramSocket;

    public SendThread(DatagramSocket datagramSocket) {
        this.datagramSocket = datagramSocket;
    }

    @Override
    public void run() {
        //键盘录入数据
        System.out.println("请输入要发送的数据");
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String line ;

        try {
            while ((line = br.readLine())!=null){
                //如果输入的是886则退出循环
                if (line.equals("886")){
                    break;
                }
                //将输入的数据转为字节数组,然后封装到数据包并发送
                byte[] arr = line.getBytes();
                DatagramPacket datagramPacket = new DatagramPacket(arr,arr.length, InetAddress.getByName("192.168.0.100"),10765);

                datagramSocket.send(datagramPacket);

            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        datagramSocket.close();
    }
}

/**
 * 这是接收线程类
 */
class ReceiveThread implements Runnable{
    private DatagramSocket datagramSocket;

    public ReceiveThread(DatagramSocket datagramSocket) {
        this.datagramSocket = datagramSocket;
    }

    @Override
    public void run() {

        while (true){
            byte[] arr = new byte[1024];
            //创建接收用的数据包
            DatagramPacket datagramPacket = new DatagramPacket(arr,arr.length);
            //接收数据包
            try {
                datagramSocket.receive(datagramPacket);
            } catch (IOException e) {
                e.printStackTrace();
            }
            //解析数据包
            String s = new String(datagramPacket.getData(),0,datagramPacket.getLength());
            //获取发送者的ip
            String ip = datagramPacket.getAddress().getHostAddress();

            System.out.println(ip+":"+s);
        }
    }
}

TCP协议发送和接收数据

发送:

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

/**
 * Created by mo on 15/11/21.
 *
 * TCP发送数据
 *  创建发送端的Socket
 *      如果创建成功  就说明连接已建立
 *  获取输出流写数据
 *  释放资源
 */
public class ClientDemo {
    public static void main(String[] args) throws IOException{
//        Socket socket = new Socket(InetAddress.getByName("192.168.0.100",10888)); //太复杂了,简化一下
        Socket socket = new Socket("192.168.0.100",10888);

        OutputStream os = socket.getOutputStream();
        os.write("hello,TCP,我来了".getBytes());

        //客户端接收服务器端的反馈
        InputStream is = socket.getInputStream();
        byte[] arr = new byte[1024];
        int len = is.read(arr);
        String s = new String(arr,0,len);
        System.out.println(s);

        //关闭Socket
        socket.close();
    }
}

接收:

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

/**
 * Created by mo on 15/11/21.
 *
 * TCP接收数据
 * 创建接收端的ServerSocket
 * 监听客户端,返回一个对应的Socket对象
 * 获取输入流,读取数据并显示
 * 释放资源
 */
public class ServerDemo {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10888);


        //监听,并接收到此套接字的连接,此方法在连接传入前一直阻塞
        Socket s = ss.accept();

        //获取数据
        InputStream is = s.getInputStream();
        byte[] arr = new byte[1024];
        int len = is.read(arr);//阻塞式方法
        String str = new String(arr, 0, len);
        System.out.println(s.getInetAddress().getHostAddress() + ":" + str);

        //服务器向客户端发送反馈,上面的read是阻塞式,所以不用担心上面还没执行完就执行下面
        OutputStream os = s.getOutputStream();
        os.write("服务器已收到信息".getBytes());

        //关闭客户端的Socket
        s.close();


//        ss.close();//服务器要保持开启 不能关
    }
}

案例1:客户端键盘录入,服务器输出到控制台
客户端

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


/**
 * Created by mo on 15/11/21.
 */
public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("192.168.0.100", 10588);
        //创建线程用于接收服务端发来的反馈
        Thread rec = new Thread(new Receive(socket));
        rec.start();

        //键盘录入
        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));

        //把通道内的流给包装一下.因为Socket走的是字节流OutputStream
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));

        String line;

        while ((line = bf.readLine()) != null) {
            if (line.equals("886")){
                break;
            }
            bw.write(line);
            bw.newLine();
            bw.flush();
        }

        //这里也可以采用字节流,但要保证发送端接收端都用的字节流(具体代码为以下注释部分)
//        BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
//        OutputStream bw = socket.getOutputStream();
//
//        String line;
//
//        while ((line = bf.readLine()) != null) {
//            if (line.equals("886")){
//                break;
//            }
//            bw.write(line.getBytes());
//
//        }


        //关闭Socket
        rec.interrupt();
        socket.close();
    }
}

/**
 * 这是用于接收服务器端发来的反馈的线程类
 */
class Receive implements Runnable{
    //构造方法接收Socket对象
    private Socket socket ;

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

    @Override
    public void run() {
        while (true){
            //判断Socket连接是否被关闭
            if (socket.isClosed()){
                break;
            }
            //连接还存在就开始等待接收反馈
            InputStream is;
            byte[] arr = new byte[1024];
            int len;

            try {
                is = socket.getInputStream();
                len = is.read(arr);//阻塞式方法
                System.out.println(new String(arr,0,len));
            } catch (IOException e) {
                System.out.println("已退出聊天室");//输入886退出后捕获并处理异常
            }

        }
    }
}

服务端

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

/**
 * Created by mo on 15/11/21.
 * 客户端键盘录入,服务器输出到控制台
 */
public class Server {
    public static void main(String[] args) throws IOException {
        ServerSocket ss = new ServerSocket(10588);

        //在循环里监听并建立Socket连接
        while (true){
            Socket s = ss.accept();
            //把通道内的流给包装一下.因为Socket走的是字节流
            BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));

            String line ;
            //IO流获取数据并给予反馈
            while ((line = br.readLine()) != null){
                System.out.println(s.getInetAddress().getHostAddress()+":"+line);
                OutputStream os = s.getOutputStream();
                os.write("服务器已接收到信息".getBytes());
            }

            s.close();
        }

        //这里也可以采用字节流,但要保证发送端接收端都用的字节流(具体代码为以下注释部分)
//        while (true){
//            Socket s = ss.accept();
//            InputStream br=s.getInputStream();
//
//            int line ;
//            byte[] arr = new byte[1024];
//            while ((line = br.read(arr)) != -1){
//                System.out.println(s.getInetAddress().getHostAddress()+":"+new String(arr,0,line));
//                OutputStream os = s.getOutputStream();
//                os.write("服务器已接收到信息".getBytes());
//            }
//
//            s.close();
//        }

    }
}

案例2:多个客户端上传文件到服务器(多线程解决)
服务器端

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

/**
 * Created by mo on 15/11/21.
 *
 * 只用while(true)包起来的缺点:都以一个名称储存 前几个客户端传的被覆盖 只剩最后一个
 *
 * 所以要用多线程
 */
public class Server {
    public static void main(String[] args) throws IOException {
        //创建ServerSocket对象
        ServerSocket serverSocket = new ServerSocket(25255);

        //因为不知道有多少个客户端发送数据,所以这里用while循环监听并为每一个建立连接的客户端启动一个新线程
        //接收文件的代码放在线程里处理
        while (true) {
            Socket s = serverSocket.accept();
            Thread thread = new Thread(new serverThread(s));
            thread.start();
        }
    }
}

/**
 * 这是服务端接收文件的线程类
 */
class serverThread implements Runnable {
    private Socket s;

    public serverThread(Socket s) {
        this.s = s;
    }

    @Override
    public void run() {
        byte[] arr = new byte[4096];
        int len;
        try {
            BufferedInputStream br = new BufferedInputStream(s.getInputStream());
            //这里 多个线程 会覆盖一个文件 所以要改进
//            BufferedOutputStream bw = new BufferedOutputStream(new FileOutputStream("account.txt"));

            //改进方法:因为目前无法获取到上传的文件名,所以只能用当前时间毫秒值来命名
            String newName = System.currentTimeMillis()+".txt";
            BufferedOutputStream bw = new BufferedOutputStream(new FileOutputStream(newName));

            while ((len = br.read(arr)) != -1) {
                bw.write(arr, 0, len);
                //重点!这里非常有必要flush()一下,不然会导致,最后一次没有接收,丢失最后一部分
                bw.flush();
            }

            //保存完毕后给予客户端反馈
            OutputStream sd = s.getOutputStream();
            sd.write("文件上传完毕".getBytes());

            bw.close();
            s.close();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
}

客户端(每个客户端都一样,这里只放一个客户端的代码)

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

/**
 * Created by mo on 15/11/21.
 *
 * 多个客户端  1个服务器端
 *
 * 客户端不用修改 只修改服务器端
 */
public class Client1 {
    public static void main(String[] args)throws IOException {
        //创建Socket对象 传入目标ip+端口
        Socket socket = new Socket("192.168.0.100",25255);
        //封装要上传的文件
        BufferedInputStream br = new BufferedInputStream(new FileInputStream("/Users/mo/account.txt"));
        //获取通道的写入流
        BufferedOutputStream bw = new BufferedOutputStream(socket.getOutputStream());

        int len;
        byte[] arr = new byte[4096];
        while ((len=br.read(arr)) != -1){
            bw.write(arr,0,len);
            //重点!这里非常有必要flush()一下,不然会导致,最后一次没有发送出去,导致传过去的文件丢失一部分
            bw.flush();
        }


        //上传完了就给个结束标记,告诉服务器已经传完了,别等了
        socket.shutdownOutput();

        //接收服务器端发来的反馈
        InputStream rec = socket.getInputStream();
        int end ;
        byte[] arr1 = new byte[1024];
        end = rec.read(arr1);
        System.out.println(new String(arr1,0,end));


        br.close();
        socket.close();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值