第二十三章 JAVASocket应用--通信是这样练成的

第一节 网络基础简介

这里我们首先先简单介绍一下socket编程的基础知识,java对网络提供了高速的支持,使得客户端和服务器的沟通变成了现实,而在网络编程中使用最多的就是socket,大家所熟悉的网络程序,像QQ,MSN等等,这些网络应用都使用了socket相关的技术,JAVA平台为我们提供了一组功能强大的类,为我们使用socket进行网络程序的开发,本专题就为大家一起揭开socket的神秘面纱,看看网络通信是怎样练成的,本节课我们会讲下面这些内容:网络基础知识、InetAddress类、URL类、TCP编程、UDP编程。
下面我们来看网络相关的基础知识,两台计算机要想通过网络进行通信,那么他们需要满足一些必然的条件:

  • 两台主机他们需要有一个唯一的标识,用来表示他们所处的身份,所在的位置,这就是IP地址
  • 需要有共同的语言,否则就会出现言语不通,无法交流,这就是我们的协议
  • 每台主机都需要有相应的端口号,一台主机上可以运行多个应用程序,那如何辨别不同应用程序的通信,我们需要使用端口号来进行区分。

这是两台主机通过网络进行通信的必备条件:IP地址,协议,端口号。那么TCP/IP协议是什么呢?
TCP/IP协议:TCP/IP是目前世界上应用最为广泛的协议,它是以TCP和IP为基础的不同层次上多个协议的集合,也成TCP/IP协议族,或TCP/IP协议栈。两个主机之间要进行通信,他们都要遵守这个TCP和IP的协议。
TCP:Transmission Control Protocol,它的全称叫传输控制协议
IP:Internet Protocol 叫互联网协议
我们在实际的应用当中,一般会将网络进行分层,常见的分为5层:
5 应用层
4 传输层
3 网络层
2 数据链路层
1 物理层
这叫TCP/IP的5层模型,最底层物理层是我们用户最直观接触到的网线、双绞线、网卡等等都处于我们的物理层;而TCP和IP协议,在我们的第4层也就是传输层,最上方的应用层也是用户直接接触到的,那么应用层有许多协议是大家所熟知的,像HTTP协议超文本传输协议,各位小伙伴们会经常上网看新闻,看网页或者电子商务等等,那么他们所使用的协议就是HTTP超文本传输协议;还有像FTP文件传输协议,可以进行文件的上传和下载,文件的共享等等;SMTP简单邮件传送协议可以进行文件的发送;Telnet远程登陆服务等等,这些都是应用层的协议。
IP地址:它是每台计算机唯一的标识,用来区分网络中的不同的主机,是两台主机通信所必不可少的。这个IP地址就非常类似我们现实当中的人的手机号码,比如说张三想给李四发消息,首先他要知道李四的手机号,通过这个手机号码他就可以定位到这个人,然后才能与之通信,IP地址也是一样的,它就是计算机的唯一的一个身份,唯一的标识,如果你想实现与另外一台计算机的通信,那么你就要知道对方的IP地址。我们说现实中的手机号码它有一个固定的格式,11位,有关部门制定的。而IP地址它也是有固定格式的。IP地址格式:数字型,如192.168.0.1 这是IPV4,也就是32位的长度。
端口:端口号实际上就是为了区分不同的应用程序,我们的一个主机它可以同时运行多个程序,比方说可以同时安装QQ,迅雷,暴风影音,idea,那么我们如何知道我们所发送的信息被另一台主机的对应的软件所接收呢?可不可能出现我们使用QQ发送消息,被对方的MSN接收到呢?这个是不会的,就是因为有端口号的存在。

  • 每个应用它都有唯一的端口号,用于区分不同的应用程序
  • 端口号的范围为0-65535,其中0-1023为系统所保留。如果我们去自定义端口号,建议使用1023往后的端口号
  • IP地址+端口号就组成了所谓的Socket,Socket是网络上运行的程序之间双向通信链路的终结点,是TCP和UDP的基础。
    举个例子:说我们的小伙伴们之间想互相通信,我们就需要知道对方的电话号码,而我们的电话号码在拨通以后,很可能会提示用户输入分机号,也就是说我们要真正与一个小伙伴通信,不单要知道他的电话号码,还需要知道他的分机号,通过电话号和分机号,才组成了一个完成的通信的信道,链路,与对方建立起一个通信的连接。那么IP地址和端口号的作用,就相当于是电话号码和分机号的作用,而socket它是网上网络通信的基础,我们也称之为是网络上双向通信线路的终结点,什么是终结点呢?简单来说,小伙伴通过电话号码加分机号组成这样一个双向的通信,此时AB小伙伴都可以说话双向交流,此时A和B就是这条链路的两个终结点,也就是两个终端,那么socket是TCP和UDP通信的基础,也是我们整个网络通信的基础。
  • 对于常用的协议所使用的端口号,http:80 ftp:21 telnet:23

JAVA对网络的支持
针对网络通信的不同层次,JAVA提供的网络功能有四大类:

  • InetAddress:用于标识网络上的硬件资源。说白了它主要标识IP地址等相关信息
  • URL:统一资源定位符,通过URL可以直接读取或写入网络上的数据
  • Sockets:使用TCP协议实现网络通信的Socket相关的类
  • Datagram:使用UDP协议,将数据保存在数据报中,通过网络进行通信

第二节 JAVA中网络相关API的应用

1.JAVA中InetAddress的应用,了解了网络基础知识以后,我们来了解一下InetAddress类InetAddress类用于标识网络上的硬件资源,标识互联网协议(IP)地址的相关信息。打开API查看该类,没有构造方法,也就是没有办法直接通过new的方式来创建一个它的对象,我们接着往下看一下方法摘要,它提供了一些静态方法。如:

方法体 作用
getLocalHost() 返回本地主机
getByName() 在给定主机名的情况下,确定主机的IP地址
getByAdress() 在给定原始IP地址的情况下,返回InetAddress的对象
getByAdress() 根据提供的主机名和IP地址,创建InetAddress对象
getHostName() 获取此IP地址的主机名
getHostAddress() 返回IP地址的字符串(以文本表现形式)

下面我们来写一个实际的例子

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
/**
 * Created by Administrator on 2017/7/25.
 * InetAddress类
 */
public class Test01 {
    public static void main(String[] args) {
        //获取本机的InetAddress类的实例
        try {
            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 address2=InetAddress.getByName("PC-20160824WQYZ");
            System.out.println("计算机名:"+address2.getHostName());
            System.out.println("IP地址:"+address2.getHostAddress());
            InetAddress address3=InetAddress.getByName("192.168.1.103");
            System.out.println("计算机名:"+address3.getHostName());
            System.out.println("IP地址:"+address3.getHostAddress());
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
    }
}

2.JAVA中URL的应用,首先我们需要先了解下URL:

  • URL中文名称为统一资源定位符(Uniform Resource Locator),表示Internet上某一资源的地址。
    比如说大家经常访问我们的imooc网站,那么在我们的网站上提供了非常多的资源,在访问网站的时候,我们要输入一个地址,也就是网址 www.imooc.com,它呢实际上就是一个url,标识着我们imooc网站上大家共享的资源信息。
  • URL由两部分组成:协议名称和资源名称,中间用冒号隔开。我们所说的网址,实际就是HTTP协议,超文本传输协议:Http://www.imooc.com/learn/85 这就是浏览网页所使用到的一个协议。像HTTP就是协议名称,后面的就是资源名称。
  • 在java当中,java.net包,提供了URL类来表示URL。

这里我们该如何使用它呢?打开API文档来查看一下。构造方法,可以根据字符串方式来创建,参数要求不同,也可以根据协议名,主机、端口号、和文件的信息来创建URL。也可以根据一个已存在的URL,以及相应的字符串信息来创建一个新的URL,也就是说根据一个父URL来创建一个子URL,这是我们看到的它的构造方法,这个类还提供了许多其他方法来获取URL的信息。
关于getPort()方法返回值问题:

  • HTTP协议在当初制定时规定,其默认端口号为80,即未申明(省略)端口号的情况下,浏览器自动为其补充上URL中缺失的“:80”部分。关于HTTP协议的其它详情,可查阅RFC 2616。
  • java.net.URL.getPort()规定,若URL的实例未申明(省略)端口号,则返回值为-1。
    两件事情请区分开来。

使用URL读取网页内容,通过URL对象的openStream()方法可以得到指定资源的输入流,通过输入流可以读取、访问网络上的数据。
下面是案例:

import java.net.MalformedURLException;
import java.net.URL;
/**
 * Created by Administrator on 2017/7/25.
 * URL常用方法
 */
public class Test02 {
    public static void main(String[] args) {
        //创建一个URL的实例
        try {
            URL imooc=new URL("http://www.imooc.com");
            //根据已创建的URL,来创建新的URL
            URL url=new URL(imooc,"/index.html?username=tom#test");
            //?后面表示参数,#后面表示锚点
            System.out.println("协议:"+url.getProtocol());
            System.out.println("主机:"+url.getHost());
            //如果未指定端口号,根据协议不同使用默认端口号,那我们这里HTTP使用的是80
            //此时因为是默认的端口号,所以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.*;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * 使用URL来读取网页中的内容
 * Created by Administrator on 2017/7/25.
 */
public class Test03 {
    public static void main(String[] args) {
        //创建一个URL实例
        try {
            URL url=new URL("http://www.baidu.com");
            //通过url的openstream方法来获取url对象所表示资源的字节输入流
            InputStream is=url.openStream();
            //之后把它转化成字符输入流
            InputStreamReader isr=new InputStreamReader(is);
            //为字符输入流添加缓冲
            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编程

在了解了网络的相关知识,下面我们来看下Socket编程及基于TCP的网络通信。
TCP协议是面向连接、可靠的、有序的,以字节流的方式发送数据。在数据通信之前需要建立连接,保证数据传输的可靠性,那么它的传输是有顺序的,并且以字节流的方式进行传输,在我们JAVA当中如何去基于TCP协议实现网络通信呢?
有这么两个相关的类,基于TCP协议实现网络通信的类:

  • 客户端的Socket类
  • 服务器端的ServerSocket类

我们来看一下它具体的通信的过程,也就是Socket通信模型。两台主机需要通信,就必然存在着一台主机为服务器端Server,一台为客户端Client。那么通信的时候是这样的,首先我们在服务端,建立一个ServerSocket(服务端倾听Socket),绑定相应的端口,并且在指定的端口进行侦听,等待客户端的连接。当我们在客户端创建Socket并且向服务器端发送请求,服务器收到请求并且接受客户端的请求信息,一旦接受客户端请求信息以后,会创建一个连接Socket,用来与客户端的Socket进行通信。到底该如何进行通信,就是通过相关的输入流和输出流 InputStream,OutpuStream进行数据的交换,发送接收以及数据的响应等等。那么在客户端和服务器端双方通信完以后,我们需要分别关闭两边的Socket及相关资源,进行通信的断开。这是基于TCP-socket通信,整个通信的过程,这点需要各位小伙伴们清楚。
Socket通信模型
那么具体该怎么进行操作呢?Socket实现通信操作的基本步骤:

  • 1.分别在服务器端和客户端完成ServerSocket和Socket的创建,这也是我们实现网络通信的基础
  • 2.打开连接到Socket的相关的输入输出流,进行数据的通信
  • 3.按照协议对Socket进行读写操作
  • 4.在通信完成以后关闭输入输出流,关闭socket

那如何使用SeverSocket和Socket呢?我们可以打开API文档来看一下这里是怎么说明这两个类的。
我们知道编程实现是基于TCP的Socket通信之服务端,下面我们通过socket的一个案例来看一下,如果实现基于TCP的socket通信,我们要实现的是一个用户的登陆。实现的过程,实际上就是用户信息在客户端向服务器端发送过去,服务器端接收到用户信息以后,进行响应这样一来一回的过程,它的运行结果也非常的简单。在客户端向服务端发送登陆请求,提交用户名和密码,那服务器端呢进行响应,欢迎之类的信息。它有一个相对固定的实现的步骤,我们要在服务器端创建一个服务器端的Socket。
服务器端的步骤:

  • 1.创建服务器端的ServerSocket对象,绑定监听端口
  • 2.调用accept()方法进行侦听客户端的请求,等待客户端的连接
  • 3.与客户端建立连接以后,通过输入流读取客户端发送的请求信息
  • 4.通过输出流用来响应客户端
  • 5.关闭输入输出流以及socket等相应的资源

客户端的步骤:

  • 1.创建socket对象,并且指明需要连接的服务器端的地址以及端口号,用来与服务器端进行连接
  • 2.连接建立后,获取一个输出流,通过输出流向服务器端发送请求信息
  • 3.通过输入流,读取服务器端响应的信息
  • 4.关闭相应的资源

仅仅发送信息到服务器端是不够的,如果要实现服务器端对客户端信息的响应,我们该如何做呢?其实也很简单,响应是通过输出流来实现的。
在了解了基于TCP的socket编程以后,我们有的小伙伴们就会发出这样的疑问,那我们所编写的程序只能实现服务器和一个客户端的对话,而在实际的应用中,往往是在服务器中运行一个永久的程序,它可以和多个客户端进行通信,并且可以接受多个客户的请求,提供相应的服务。那么我们如何实现一个服务器和多客户端之间的通信呢?对于这种需求我们又该使用何种技术去实现呢?
我们可以使用之前所讲过的多线程来实现服务器与多客户端之间的通信。它的基本步骤:

  • 1.服务器端创建ServerSocket,循环调用accept()方法等待客户端连接。当然由于我们要实现多客户端的通话,也就意味着我们循环等待客户端的连接
  • 2.每当客户端创建一个socket并请求和服务端连接,服务器端就会产生一个专门的线程,来响应该客户与之通信
  • 3.服务器端接收请求,创建socket与该客户进行连接
  • 4.建立连接的两个socket在一个单独的线程上对话,而同时服务器本身在启动线程以后,马上又会进入侦听状态
  • 5.服务器继续等待新的连接
    具体案例代码:
import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * Created by Administrator on 2017/7/26.
 * Socket基于TCP通信,服务器端SeverSocket
 */
public class Server {
    public static void main(String[] args) {
        try {
            //1.创建一个服务器端ServerSocket,并且绑定指定的端口号
            ServerSocket serverSocket=new ServerSocket(8888);
            Socket socket=null;
            //记录客户端的数量
            int count=0;
            //2.调用accept()方法开始监听,等待客户端的连接
            System.out.println("***服务器即将启动,等待客户端的连接***");
            while(true){
                socket=serverSocket.accept();
                ServerThread st=new ServerThread(socket);
                st.start();
                count++;
                System.out.println("客户端的数量"+count);
                InetAddress address=socket.getInetAddress();
                System.out.println("当前客户端的IP地址:"+address.getHostAddress());
            }
//            Socket socket=serverSocket.accept();//一旦调用这个方法它就会处于阻塞的状态,等待客户端的请求信息
//            //如果客户端发送连接请求,这个时候我们会接收客户端的请求,并且我们看到accpet方法会返回一个socket的实例
//            //用来与客户端进行通信
//            //一旦与客户端建立socket通信以后,我们下面就需要在客户端和服务器端实现数据的交互,获取客户端提交的登陆
//            //信息,那么如何获取呢?需要通过输入输出流来实现。
//            //3.获取一个输入流,然后来读取客户信息
//            InputStream is=socket.getInputStream();//字节输入流
//            //为了提升效率可以把它包装成一个字符输入流
//            InputStreamReader isr=new InputStreamReader(is);
//            //我们可以为字符流添加缓冲,以缓冲的方式去进行数据的读取
//            BufferedReader br=new BufferedReader(isr);
//            String info=null;
//            while((info=br.readLine())!=null){
//                System.out.println("我是服务器,客户端说"+info);
//            }
//            socket.shutdownInput();//关闭输入流
//            //4.获取输出流,用来服务器端响应客户端的信息
//            OutputStream os=socket.getOutputStream();
//            PrintWriter pw=new PrintWriter(os);
//            pw.write("欢迎您!");
//            pw.flush();
//            //5.关闭相关的资源
//            pw.close();
//            os.close();
//            br.close();
//            isr.close();
//            is.close();
//            socket.close();
            //serverSocket.close();有一个死循环所以无法关闭也不会进展到这一步,所以删掉
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
import java.io.*;
import java.net.Socket;

/**
 * Created by Administrator on 2017/7/26.
 * 客户端
 */
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("用户名:admin;密码:123");
            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 (IOException e) {


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

/**
 * Created by Administrator on 2017/7/26.
 * 服务器端线程处理类
 */
public class ServerThread extends Thread {
    //每个客户端请求了以后,服务器端都会创建一个socket,与之进行通信,所以我们每个线程都对应了一个与
    //本线程相关的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();
            //3.获取输入流读取客户端的信息
            //为了提升效率可以把它包装成一个字符输入流
            isr = new InputStreamReader(is);
            //我们可以为字符流添加缓冲,以缓冲的方式去进行数据的读取
            br = new BufferedReader(isr);
            String info = null;
            while ((info = br.readLine()) != null) {
                System.out.println("我是服务器,客户端说" + info);
            }
            socket.shutdownInput();//关闭输入流
            //4.获取输出流,用来服务器端响应客户端的信息
            os = socket.getOutputStream();
            pw = new PrintWriter(os);
            pw.write("欢迎您!");
            pw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //5.关闭相关的资源,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编程

UDP协议(用户数据报协议)是无连接、不可靠、无序的,不固定的。特点在于它的速度相对来说会比较快.UDP它以数据报作为数据传输的载体。在我们使用UDP进行数据传输时,会将数据封装成Datagram(定义成数据报),然后在数据报中指明数据所要到达的socket(主机地址和端口号),然后再将数据报发送出去。
那么到底该如何实现udp-socket编程呢?这里我们需要使用如下两个类:
DatagramPacket:表示数据报包
DatagramSocket:进行端到端通信的类,实现UDP的socket通信
下面我们来做一个实现应用登陆的案例
服务器端实现步骤:
1.创建DatagramSocket,并且指定端口号
2.创建一个数据报 DatagramPacket,用来接收客户端发送的数据
3.接收客户端发送的数据信息
4.读取数据
客户端实现步骤:
1.定义发送的数据信息
2.创建数据报,DatagramPacket,包含将要发送的信息
3.创建DatagramSocket对象,用来实现数据的发送
4.我们把数据发送过去
这里我们需要思考一下如何像TCP一样实现多客户端的通信

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

/**
 * Created by Administrator on 2017/7/26.
 * 服务器端,基于UDP的用户登陆
 */
public class UDPServer {
    public static void main(String[] args) {
        /**
         * 接受客户端发送的数据
         */
        try {
            //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 adress=packet.getAddress();
            int port=packet.getPort();
            byte []data2="欢迎您".getBytes();
            //2.创建数据报,包含响应的数据信息,
            DatagramPacket packet2=new DatagramPacket(data2,data2.length,adress,port);
            //3.响应客户端
            socket.send(packet2);
            //4.关闭相应的资源信息
            socket.close();

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


    }
}   
import java.io.IOException;
import java.net.*;

/**
 * Created by Administrator on 2017/7/26.
 */
public class UDPClinet {
    public static void main(String[] args) {
        /**
         * 向服务器端发送数据
         */
        try {
            //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.使用datagramsocket向服务器端发送数据报
            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();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
}

第五节Socket总结

总结一下socket通信的相关内容,本节课的重点是socket通信原理以及基于tcp的socket通信。希望各位小伙伴可以通过示例代码能反复的练习。这里主要有以下几个问题需要和大家多强调以下:

  • 1.关于多线程的问题,我们之前在socket编程当中使用到了多线程,比如说服务器需要不停的与多客户端通信,那么也就是需要我们的服务器端需要不停地去监听客户端的请求,这个时候就需要使用多线程,既然是死循环,这个线程就需要注意优先级的问题,如果说没有设置优先级,可能会导致效率低下,速度会比较慢 thread.setPriority(4);这是一个工作上的经验,前面这个方法是设置线程的优先级【1-10】,默认为5,建议适当的降低,否则就会出现运行速度比较慢的问题
    多线程的优先级
  • 2.关于TCP的输入流输出流是否关闭问题,对于同一个socket,如果说我们提前把输出流关闭,这个时候会导致与该输出流关联的socket也被关闭,所以一把不用关闭流,直接关闭socket即可
    是否关闭输入输出流
  • 3.使用TCP通信传输对象,我们在之前进行用户登陆的时候,客户端向服务器端发送了数据,以及服务器端响应的数据都是通过字符串的形式进行交互的,实际的应用当中,客户端向服务器端发送数据更多的是以对象的形式发送的,我们把用户名和密码封装成user对象,传递的是一个user,这就是我们面向对象的思想。这个时候我们就可以继续使用在IO中所讲到的ObjectInputStream,ObjectOutputStream writeObject来实现对象的传输。
  • 4.Socket编程传递文件
    之前我们在进行数据传递的时候,传递的是字符串,如果我们想实现文件的传递呢?我现在想在客户端给服务器端发送一个文件,这个时候又该怎么办呢?我们可以结合我们之前IO的相关知识通过输入输出流去读取文件中的数据,并且把它发送给服务器端。

你可能感兴趣的:(java基础学习笔记)