Socket通信

来源:互联网 发布:c语言四种基本数据类型 编辑:程序博客网 时间:2024/06/06 16:26

一、端口、IP、Socket
总体来说端口和IP共同组成了Socket
这里写图片描述

二、Java中与网络相关的类
这里写图片描述

1、InetAddress类
InetAddress类的对象用于IP地址和域名等

public class InetAddressDemo1{    public static void main(String[] args) throws UnknownHostException    {        /*直接获取本机的InetAddress实例,InetAddress的实例由IP地址和可能的对应主机名组成*/        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实例;根据IP地址获取InetAddress实例*/        InetAddress address2=InetAddress.getByName("LAPTOP-0QPS2MS9");        System.out.println("计算机名称:"+address2.getHostName());        System.out.println("计算机IP地址:"+address2.getHostAddress());        InetAddress address3=InetAddress.getByName("10.70.58.237");        System.out.println("计算机名称:"+address3.getHostName());        System.out.println("计算机IP地址:"+address3.getHostAddress());    }}

程序运行结果:
本机名称:LAPTOP-0QPS2MS9
本机IP地址:172.20.10.2
字节数组形式的IP地址:[-84, 20, 10, 2]
LAPTOP-0QPS2MS9/172.20.10.2
计算机名称:LAPTOP-0QPS2MS9
计算机IP地址:172.20.10.2
计算机名称:windows10.microdone.cn
计算机IP地址:10.70.58.237

2、URL类
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

/* * URL常用方法 */public class URLDemo{    public static void main(String[] args)    {        try        {            //创建一个URL实例            URL imooc=new URL("https://www.imooc.com");            //在已经创建的url的基础上创建一个新的url            //?后面表示参数,#后面表示锚点            URL url=new URL(imooc, "/index.html?username=tom#test");            //获取url协议名、主机名、端口号            System.out.println("协议:"+url.getProtocol());            System.out.println("主机地址:"+url.getHost());            //由于创建url协议的时候没有指定端口号,因此使用默认端口号80,但是返回值为-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)        {            // TODO Auto-generated catch block            e.printStackTrace();        }    }}

程序运行结果
协议:https
主机地址:www.imooc.com
端口号:-1
文件路径:/index.html
文件名:/index.html?username=tom
相对路径:test
查询字符串:username=tom

/* * 使用URL读取网页内容 */public class URLDemo2{    public static void main(String[] args)     {        try        {            //创建一个URL实例            URL url=new URL("https://www.baidu.com");            //通过URL的openStream方法获取URL对象所表示的资源的字节输入流            InputStream is=url.openStream();            //将字节输入流转换为字符输入流,指定GBK编码            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();        }    }}

程序运行结果

<!DOCTYPE html><!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baidu.min.css><title>百度一下,你就知道</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus=autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn" autofocus></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=https://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>地图</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>视频</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>');                </script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">更多产品</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使用百度前必读</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a>&nbsp;京ICP证030173号&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>

三、Socket通信及实现步骤
1、基于TCP的Socket通信
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

ServerSocket 一般仅用于设置端口号和监听,真正进行通信的是服务器端的Socket与客户端的Socket,在ServerSocket 进行accept之后,就将主动权转让了。

/* * 实现基于TCP协议的Socket通信,实现用户登录 * 服务器端 */public class Server{    public static void main(String[] args)    {        try        {            //1、创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口            ServerSocket serverSocket=new ServerSocket(8888);            //2、调用accpet()方法开始监听,等待客户端连接,同时accept方法会返回一个Socket对象用来与客户端进行通信 Listens for a connection to be made to this socket and accepts it.            System.out.println("服务器即将启动,等待客户端连接");            Socket socket=serverSocket.accept();            //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);            }            //获取输出流,响应客户端请求            OutputStream os=socket.getOutputStream();            PrintWriter pw=new PrintWriter(os);//将输出流包装成打印流            pw.write("欢迎您!");            pw.flush();//调用flush方法将缓冲输出给客户端            //关闭输入流            socket.shutdownInput();            //关闭资源            br.close();            isr.close();            is.close();            socket.close();            serverSocket.close();            pw.close();            os.close();        }         catch (IOException e)        {            e.printStackTrace();        }    }}
/* * 客户端 */public class Client{    public static void main(String[] args)     {        try        {            //1、创建客户端Socket,指定服务器地址和端口,由于这个案例中服务器和客户端在同一台主机上,因此用的是同一个ip地址,localhost表示的就本机地址            Socket socket=new Socket("localhost", 8888);            //2.获取输出流,向服务器发送登陆信息            OutputStream os=socket.getOutputStream();            PrintWriter pw=new PrintWriter(os);//将输出流包装为打印流            pw.write("用户名:admin;密码:123");            pw.flush();            socket.shutdownOutput();//关闭输出流,一定要写在使用输出流的语句前面!            //获取输入流,用来读取服务器相应的信息            InputStream is=socket.getInputStream();            BufferedReader br=new BufferedReader(new InputStreamReader(is));            String info=null;            while((info=br.readLine())!=null)            {                System.out.println("我是客户端,服务器说:"+info);            }            pw.close();            os.close();            is.close();            br.close();            socket.close();        }         catch (UnknownHostException e)        {            e.printStackTrace();        }         catch (IOException e)        {            e.printStackTrace();        }    }}

在执行程序的时候要注意步骤,必须先启动服务器再启动客户端。
程序运行结果
首先启动服务器,显示如下

服务器即将启动,等待客户端连接

然后启动客户端,服务器显示如下

服务器即将启动,等待客户端连接我是服务器,客户端说:用户名:admin;密码:123

客户端显示如下

我是客户端,服务器说:欢迎您!

但实际上在编写这个程序的过程中我遇上了一些问题,在编写客户端的时候,socket.shutdownOutput 原来是放在读取服务器发送的信息并相应的功能之后的,但是这样做的结果是服务器无法接收到客户端发送的数据,这个疑惑尚待解决。

下面在基于上面的例子上,我们再完成一个利用多线程实现一台服务器与多个客户端通信的Demo。要想让一台服务器与多个客户端进行响应,那我们就需要创建一个线程类,在线程中完成服务器与客户端的通信,而不是直接在服务器类中完成通信。

/* * 服务器线程处理类,在线程中完成服务器与客户端的通信 */public class ServerThread extends Thread{    //和本线程相关的Socket    Socket socket=null;    //构造方法,为Socket赋值    public ServerThread(Socket socket)    {        this.socket=socket;    }    //线程执行的操作,响应客户端的请求    @Override    public void run()     {        super.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);            }            //获取输出流,响应客户端请求            os=socket.getOutputStream();            pw=new PrintWriter(os);//将输出流包装成打印流            pw.write("欢迎您!");            pw.flush();//调用flush方法将缓冲输出        }         catch (IOException e)        {            // TODO Auto-generated catch block            e.printStackTrace();        }        finally        {            try            {                //关闭输入流                socket.shutdownInput();                //关闭资源                br.close();                isr.close();                is.close();                pw.close();                os.close();                socket.close();            }            catch (IOException e)            {                e.printStackTrace();            }        }    }}
/* * 实现基于TCP协议的Socket通信,实现用户登录 * 服务器端 */public class Server{    public static void main(String[] args)    {        try        {            //1、创建一个服务器端Socket,即ServerSocket,指定绑定的端口,并监听此端口,服务器的IP就是本机IP地址            ServerSocket serverSocket=new ServerSocket(8888);            Socket socket=null;            //记录连接的客户端的数量            int count=0;            System.out.println("服务器即将启动,等待客户端连接");            //循环监听等待客户端的连接,利用多线程以实现一个服务器与多个客户端通信            while(true)            {                //2、调用accpet()方法开始监听,等待客户端连接                socket=serverSocket.accept();                //客户端与服务器连接后就创建一个新的线程,在线程里完成两者之间的通信操作                ServerThread serverThread=new ServerThread(socket);                //启动线程                serverThread.start();                //统计客户端的数量                count++;                System.out.println("客户端的数量为:"+count);                //获取客户端的IP地址                InetAddress address=socket.getInetAddress();                System.out.println("当前客户端的IP:"+address.getHostAddress());            }                   }         catch (IOException e)        {            e.printStackTrace();        }    }}
/* * 客户端 */public class Client{    public static void main(String[] args)     {        try        {            //1、创建客户端Socket,指定服务器IP地址和端口,由于这个案例中服务器和客户端在同一台主机上,因此用的是同一个ip地址,localhost表示的就本机地址            Socket socket=new Socket("localhost", 8888);//执行了这条语句后,客户端就已经与服务器产生了连接。            //2.获取输出流,向服务器发送登陆信息            OutputStream os=socket.getOutputStream();            PrintWriter pw=new PrintWriter(os);//将输出流包装为打印流            pw.write("用户名:admin;密码:123");            pw.flush();            socket.shutdownOutput();//关闭输出流,一定要写在使用输出流的语句前面!            //获取输入流,用来读取服务器相应的信息            InputStream is=socket.getInputStream();            BufferedReader br=new BufferedReader(new InputStreamReader(is));            String info=null;            while((info=br.readLine())!=null)            {                System.out.println("我是客户端,服务器说:"+info);            }            pw.close();            os.close();            is.close();            br.close();            socket.close();        }         catch (UnknownHostException e)        {            e.printStackTrace();        }         catch (IOException e)        {            e.printStackTrace();        }    }}

这样我们就完成了一个服务器与多个客户端同时通信的例子,下面运行代码。
先启动服务器:

服务器即将启动,等待客户端连接

再启动第一个客户端,则客户端显示

我是客户端,服务器说:欢迎您!

这时服务器端显示

服务器即将启动,等待客户端连接客户端的数量为:1当前客户端的IP:127.0.0.1我是服务器,客户端说:用户名:admin;密码:123

再启动第二个客户端,则客户端显示

我是客户端,服务器说:欢迎您!

这时服务器端显示

服务器即将启动,等待客户端连接客户端的数量为:1当前客户端的IP:127.0.0.1我是服务器,客户端说:用户名:admin;密码:123客户端的数量为:2当前客户端的IP:127.0.0.1我是服务器,客户端说:用户名:admin;密码:123

由此可见我们已经实现了两个客户端与一个服务器进行通信。

2、基于UDP的Socket通信
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
需要注意的是在基于UDP的Socket通信机制中,Socket类不再用来指定目标(如客户端、服务器),取而代之的DatagramPacket,DatagramPacket不仅仅用来打包发送的数据,还起到了接收数据和指定目标的作用,而Socket只起到了发送DatagramPacket和接收DatagramPacket的作用。

/*服务器端*/public class Server{    public static void main(String[] args) throws IOException    {        /*         * 接收客户端的数据         */        //创建服务器端DatagramSocket,并指定端口号        DatagramSocket socket=new DatagramSocket(8802);        //创建DatagramPacket,用于接收客户端发送的数据,并存放在字节数组当中        byte[] bytes=new byte[1024];        DatagramPacket packet=new DatagramPacket(bytes, bytes.length);        //等待接收客户端发送的数据.在未接收到数据前处于阻塞状态        System.out.println("服务器已启动,等待客户端发送数据");        socket.receive(packet);        //接收到客户端发送的数据后读取之        String info=new String(bytes, 0, packet.getLength());        System.out.println("我是服务器,客户端说:"+info);        /*         * 响应客户端         */        InetAddress address=packet.getAddress();//获取客户端的地址        int port=packet.getPort();//获取端口        byte[] bytes2="欢迎登陆!".getBytes();//将响应给客户端的数据内容存放在字节数组中        DatagramPacket packet2=new DatagramPacket(bytes2, bytes2.length, address, port);        socket.send(packet2);        //关闭资源        socket.close();    }}
/*客户端*/public class Client{    public static void main(String[] args) throws IOException    {        /*         * 向服务器端发送数据         */        //定义要通信的服务器的地址、端口号、数据        InetAddress address=InetAddress.getByName("localhost");        int port=8802;        byte[] bytes="用户名:admin;密码:123".getBytes();//将要发送的数据存放在字节数组中        //创建数据报,包含发送的数据信息:如数据内容、数据长度、发送到的服务器信息(地址和端口号)        DatagramPacket packet=new DatagramPacket(bytes, bytes.length, address, port);        //创建DatagramSocket来发送数据报        DatagramSocket socket=new DatagramSocket();        //发送数据报        socket.send(packet);        /*         * 接收服务器端发送的数据         */        //创建数据报,用于接收服务器端发送的数据        byte[] bytes2=new byte[1024];        DatagramPacket packet2=new DatagramPacket(bytes2, bytes2.length);        //用socket接收,读取服务器返回的数据        socket.receive(packet2);        String info=new String(bytes2);        System.out.println("我是客户端,服务器说:"+info);        //关闭资源        socket.close();    }}
0 0