Java Socket应用-通信是这样练成的

网络基础知识

两台计算机通过网络进行通讯,需要满足一些必然条件

  • 两台主机需要唯一标识,用来表示它们的身份,它们所在位置
  • 共同的语言:协议
  • 相应的端口号:辨别一台主机的多个应用程序

TCP/IP是目前世界上应用最为广泛的协议

  • 是以TCP和IP为基础的不同层次上多个协议的集合,也成TCP/IP协议族 或 TCP/IP协议栈

TCP:Transmission Control Protocol 传输控制协议
IP:Internet Protocol 互联网协议

TCP/IP五层模型

  • 5、应用层:用户直接接触到的,HTTP超文本传输协议、FTP文件传输协议、SMTP简单邮件传递协议、Telnet远程登录服务
  • 4、传输层:TCP/IP协议
  • 3、网络层
  • 2、数据链路层
  • 1、物理层 :网线、网卡
五层模型

IP地址:为实现网络中不同计算机之间的通讯,每台机器都必须有一个唯一的标识。

IPV4:IP规范的第四个版本

端口:1、用于区分不同应用程序;2、端口号范围为065535,其中01023为系统所保留(像HTTP、FTP)
3、IP地址和端口号组成了所谓的Socket,Socket是网络上运行的程序之间双向通信链路的终结点,是TCP和UDP的基础。
4、http:80 ftp:21 telnet:23

Java中的网络支持

针对网络通信的不同层次,Java提供的网络功能有四大类:
1、InetAddress:用于标识网络上的硬件资源,表示互联网协议(IP)地址;
2、URL:统一资源定位符,通过URL可以直接读取或写入网络上的数据;
3、Sockets:使用TCP协议实现网络通信的Socket相关的类;
4、Datagram:使用UDP协议,将数据保存在数据报中,通过网络进行通讯。

InetAddress

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;

public class InetAddressTest {

    public static void main(String[] args) throws UnknownHostException {
        // 获取本机的InetAddress实例
        InetAddress address = InetAddress.getLocalHost();
        System.out.println("计算名:" + address.getHostName());
        System.out.println("IP地址:" + address.getHostAddress());
        byte[] bytes = address.getAddress();// 获取字节数组形式的IP地址
        System.out.println("字节数组形式的IP:" + Arrays.toString(bytes));
        System.out.println(address);// 直接输出InetAddress对象

        // 根据机器名获取InetAddress实例
        // InetAddress address2=InetAddress.getByName("laurenyang");
        InetAddress address2 = InetAddress.getByName("1.1.1.10");
        System.out.println("计算名:" + address2.getHostName());
        System.out.println("IP地址:" + address2.getHostAddress());
    }
}

URL(Uniform Resource Locator)

  • 统一资源定位符,表示Internet上某一资源的地址;
  • URL由两部分组成:协议名称和资源名称,中间用冒号隔开;
  • 在java.net包中,提供了URL类来表示URL。
import java.net.MalformedURLException;
import java.net.URL;

public class URLTest {
    
    public static void main(String[] args) {
        try {
            // 创建一个URL实例
            URL imooc = new URL("http://www.imooc.com");
            // ?后面表示参数,#后面表示锚点
            URL url = new URL(imooc, "/index.html?username=tom#test");
            System.out.println("协议:" + url.getProtocol());
            System.out.println("主机:" + url.getHost());
            // 如果未指定端口号,则使用默认的端口号,此时getPort()方法返回值为-1
            System.out.println("端口:" + url.getPort());
            System.out.println("文件路径:" + url.getPath());
            System.out.println("文件名:" + url.getFile());
            System.out.println("相对路径:" + url.getRef());
            System.out.println("查询字符串:" + url.getQuery());
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
    }
}

获取网络内容

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;

/**
  * 使用URL读取网页内容
  */
public class URLReadTest {

    public static void main(String[] args) {
        try {
            // 创建一个URL实例
            URL url = new URL("http://www.baidu.com");
            // 通过URL的openStream方法获取URL对象所表示的资源的字节输入流
            InputStream is = url.openStream();
            // 将字节输入流转换为字符输入流
            InputStreamReader isr = new InputStreamReader(is, "utf-8");
            // 为字符输入流添加缓冲
            BufferedReader br = new BufferedReader(isr);
            String data = br.readLine();// 读取数据
            while (data != null) {// 循环读取数据
                System.out.println(data);// 输出数据
                data = br.readLine(); // 一次读取一行
            }
            br.close();
            isr.close();
            is.close();
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Socket通信

TCP协议是面向连接、可靠的、有序的,以字节流的方式发送数据

Java中基于TCP协议实现网络通信的类

  • 客户端的Socket类
  • 服务器端的ServerSocket类
实现网络通信的类

Socket通信实现步骤

1、创建ServerSocket和Socket
2、打开连接到Socket的输入/输出流
3、按照协议对Socket进行读/写操作
4、关闭输入输出流、关闭Socket

实现步骤

服务器端:
1、创建 ServerSocket 对象,绑定监听端口
2、通过 accept() 方法监听客户端请求
3、连接建立后,通过输入流读取客户端发送的请求信息
4、通过输出流向客户端发送响应信息
5、关闭相关资源

客户端:
1、创建Socket对象,指明需要连接的服务器的地址和端口号
2、连接建立后,通过输出流向服务器端发送请求信息
3、通过输入流获取服务器响应的信息
4、关闭相关资源

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

/*
 * 基于TCP协议的Socket通信,实现用户登陆
 * 服务器端
 */
public class Server {
    public static void main(String[] args) {
        try {
            // 1.创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口
            ServerSocket serverSocket = new ServerSocket(8888);
            Socket socket = null;
            // 记录客户端的数量
            int count = 0;
            System.out.println("***服务器即将启动,等待客户端的连接***");
            // 循环监听等待客户端的连接
            while (true) {
                // 调用accept()方法开始监听,等待客户端的连接
                socket = serverSocket.accept();
                // 创建一个新的线程
                ServerThread serverThread = new ServerThread(socket);
                serverThread.setPriority(4); // 设置线程的优先级,范围为[0-10],默认为5
                // 启动线程
                serverThread.start();

                count++;// 统计客户端的数量
                System.out.println("客户端的数量:" + count);
                InetAddress address = socket.getInetAddress();
                System.out.println("当前客户端的IP:" + address.getHostAddress());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

/*
 * 客户端
 */
public class Client {
    public static void main(String[] args) {
        try {
            // 1.创建客户端Socket,指定服务器地址和端口
            Socket socket = new Socket("localhost", 8888);
            // 2.获取输出流,向服务器端发送信息
            OutputStream os = socket.getOutputStream();// 字节输出流
            PrintWriter pw = new PrintWriter(os);// 将输出流包装为打印流
            pw.write("用户名:alice;密码:789");
            pw.flush();
            socket.shutdownOutput();// 关闭输出流
            // 3.获取输入流,并读取服务器端的响应信息
            InputStream is = socket.getInputStream();
            BufferedReader br = new BufferedReader(new InputStreamReader(is));
            String info = null;
            while ((info = br.readLine()) != null) {
                System.out.println("我是客户端,服务器说:" + info);
            }
            // 4.关闭资源
            br.close();
            is.close();
            pw.close();
            os.close();
            socket.close();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;

/*
 * 服务器线程处理类
 */
public class ServerThread extends Thread {
    // 和本线程相关的Socket
    Socket socket = null;

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

    // 线程执行的操作,响应客户端的请求
    public void run() {
        InputStream is = null;
        InputStreamReader isr = null;
        BufferedReader br = null;
        OutputStream os = null;
        PrintWriter pw = null;
        try {
            // 获取输入流,并读取客户端信息
            is = socket.getInputStream();
            isr = new InputStreamReader(is);
            br = new BufferedReader(isr);
            String info = null;
            while ((info = br.readLine()) != null) {// 循环读取客户端的信息
                System.out.println("我是服务器,客户端说:" + info);
            }
            socket.shutdownInput();// 关闭输入流
            // 获取输出流,响应客户端的请求
            os = socket.getOutputStream();
            pw = new PrintWriter(os);
            pw.write("欢迎您!");
            pw.flush();// 调用flush()方法将缓冲输出
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            // 关闭资源
            try {
                if (pw != null)
                    pw.close();
                if (os != null)
                    os.close();
                if (br != null)
                    br.close();
                if (isr != null)
                    isr.close();
                if (is != null)
                    is.close();
                if (socket != null)
                    socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

UDP Socket编程

UDP协议(用户数据报协议)是无连接、不可靠的、传输无序的。

UDP进行数据传输时,首先需要将要传输的数据定义成数据报(Datagram),在数据报中指明数据所要达到Socket(主机地址和端口号),然后再将数据报发送出去。

Java相关操作类

  • DatagramPacket:表示数据报包
  • DatagramSocket:进行端到端通信的类

服务端实现步骤:
1、创建DatagramSocket,指定端口号
2、创建DatagramPacket
3、接收客户端发送的数据信息
4、读取数据

客户端实现步骤:
1、定义发送信息
2、创建DatagramPacket,包含将要发送的信息
3、创建DatgramSocket
4、发送数据

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

/*
 * 服务器端,实现基于UDP的用户登陆
 */
public class UDPServer {
    public static void main(String[] args) throws IOException {
        /*
         * 接收客户端发送的数据
         */
        // 1.创建服务器端DatagramSocket,指定端口
        DatagramSocket socket = new DatagramSocket(8800);
        // 2.创建数据报,用于接收客户端发送的数据
        byte[] data = new byte[1024];// 创建字节数组,指定接收的数据包的大小
        DatagramPacket packet = new DatagramPacket(data, data.length);
        // 3.接收客户端发送的数据
        System.out.println("****服务器端已经启动,等待客户端发送数据");
        socket.receive(packet);// 此方法在接收到数据报之前会一直阻塞
        // 4.读取数据
        String info = new String(data, 0, packet.getLength());
        System.out.println("我是服务器,客户端说:" + info);

        /*
         * 向客户端响应数据
         */
        // 1.定义客户端的地址、端口号、数据
        InetAddress address = packet.getAddress();
        int port = packet.getPort();
        byte[] data2 = "欢迎您!".getBytes();
        // 2.创建数据报,包含响应的数据信息
        DatagramPacket packet2 = new DatagramPacket(data2, data2.length, address, port);
        // 3.响应客户端
        socket.send(packet2);
        // 4.关闭资源
        socket.close();
    }
}
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/*
 * 客户端
 */
public class UDPClient {
    public static void main(String[] args) throws IOException {
        /*
         * 向服务器端发送数据
         */
        // 1.定义服务器的地址、端口号、数据
        InetAddress address = InetAddress.getByName("localhost");
        int port = 8800;
        byte[] data = "用户名:admin;密码:123".getBytes();
        // 2.创建数据报,包含发送的数据信息
        DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
        // 3.创建DatagramSocket对象
        DatagramSocket socket = new DatagramSocket();
        // 4.向服务器端发送数据报
        socket.send(packet);

        /*
         * 接收服务器端响应的数据
         */
        // 1.创建数据报,用于接收服务器端响应的数据
        byte[] data2 = new byte[1024];
        DatagramPacket packet2 = new DatagramPacket(data2, data2.length);
        // 2.接收服务器响应的数据
        socket.receive(packet2);
        // 3.读取数据
        String reply = new String(data2, 0, packet2.getLength());
        System.out.println("我是客户端,服务器说:" + reply);
        // 4.关闭资源
        socket.close();
    }
}

多线程的优先级:未设置优先级可能会导致运行时速度非常慢,可降低优先级

是否关闭输出流和输入流:对于同一个socket,如果关闭了输出流,则与该输出流关联的socket也会被关闭,所以一般不用关闭流,直接关闭socket即可。

综合练习

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • 文章首发于个人blog欢迎指正补充,可联系lionsom_lin@qq.com原文地址:《网络是怎样连接的》阅读整...
    lionsom_lin阅读 14,126评论 6 31
  • 个人认为,Goodboy1881先生的TCP /IP 协议详解学习博客系列博客是一部非常精彩的学习笔记,这虽然只是...
    贰零壹柒_fc10阅读 5,051评论 0 8
  • 网络编程 一.楔子 你现在已经学会了写python代码,假如你写了两个python文件a.py和b.py,分别去运...
    go以恒阅读 1,999评论 0 6
  • 网络编程的概述 网络编程的实质就是用来实现网络互连的不同计算机上运行的程序间可以进行数据交换。 一.OSI网络模型...
    思念挥霍阅读 373评论 0 0
  • 在现实网络传输应用中,通常使用TCP、IP或UDP这3种协议实现数据传输。在传输数据的过程中,需要通过一个双向的通...
    凌川江雪阅读 981评论 0 4