Java中的网络编程—Socket通信 超、凢脫俗 2022-06-13 00:17 260阅读 0赞 **一、网络基础知识** **两台计算机要想通过网络进行通信,那么他们必须满足ip地址、协议、端口号这三个必然的条件** 1、两台主机必须表明所在的身份和位置,也就是ip地址 2、必须有共同的语言,不然无法交流,也就是我们所说的协议 3、需要有相应的端口号,一台主机可以运行多个应用程序,怎么辨别不同通信的程序,那么就需要端口号来区分 ![Center][] **TCP/IP协议** TCP/IP是目前世界上应用最为广泛的协议,是以TCP和IP为基础的不同层次上多个协议的集合,也称TCP/IP协议族或TCP/IP协议栈 TCP:Transmission Control Protocol 传输控制协议 IP:Internet Protocol 互联网协议 **TCP/IP模型** 一般来说,会将网络进行分层,常见的会把网络分为五层,叫TCP/IP模型 ![Center 1][] **IP地址** 为实现网络中不同计算机之间的通信,每台机器都必须有一个唯一的标识---IP地址 IP地址格式:数字型,如:192.168.0.1 IPv4:32位的二进制 ** ![Center 2][]** **端口** 1、用于区分不同应用程序 2、端口号范围为0~65535,其中0~1023为系统所保留 3、IP地址和端口号组成了所谓的Socket,Socket是网络上运行的程序之间双向通信链接的终结点,是TCP和UDP的基础 4、http:80 ftp:21 telnet:23 **二、Java中网络相关API的应用** **java中的网络支持** **针对网络通信的不同层次,java提供的网络功能有四大类:** 1、InetAddress:用于标识网络上的硬件资源 2、URL:统一资源定位符,通过url可以直接读写或写入网络上的数据 3、Socket:使用TCP协议实现网络通信的Socket相关的类 4、Datagram:使用UDP协议,将数据保存在数据报中,通过网络进行通信 **InetAddress类** 1、InetAddress类用于标识网络上的硬件资源,表示互联网协议(IP)地址 2、实例如下 输入: package rmd_intl_app.Test; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; /** * @Description: InetAddress类 */ public class InetAddressTest { public static void main(String[] args) throws UnknownHostException { //获取本机InetAddress类的操作实例 InetAddress address = InetAddress.getLocalHost(); System.out.println("address计算机名:"+address.getHostName()); System.out.println("addressIP地址:"+address.getHostAddress()); byte[] bs = address.getAddress();//获取字节数组形式的ip地址 System.out.println("字节数组的形式的IP:"+Arrays.toString(bs)); System.out.println("直接输出InetAddress对象:"+address); //根据计算机名获取InetAddress实例 InetAddress address2 = InetAddress.getByName("MS-20160808TVQL"); System.out.println("address2获取计算机名:"+address2.getHostName()); System.out.println("address2IP地址:"+address2.getHostAddress()); //根据ip地址获取InetAddress实例 InetAddress address3 = InetAddress.getByName("192.168.0.235"); System.out.println("address3计算机名:"+address3.getHostName()); System.out.println("address3IP地址:"+address3.getHostAddress()); } } 输出: address计算机名:MS-20160808TVQL addressIP地址:192.168.0.235 字节数组的形式的IP:[-64, -88, 0, -21] 直接输出InetAddress对象:MS-20160808TVQL/192.168.0.235 address2获取计算机名:MS-20160808TVQL address2IP地址:192.168.0.235 address3计算机名:MS-20160808TVQL address3IP地址:192.168.0.235 **URL** 1、URL(Uniform Resoure Locator)统一资源定位符,表示Internet上某一资源的地址 2、URL由两部分组成,协议名称和资源名称,中间用冒号隔开 3、在java.net包中,提供了URL类来表示URL 4、实例如下 输入: package rmd_intl_app.Test; import java.net.MalformedURLException; import java.net.URL; /** * @Description: url */ public class Urltest { public static void main(String[] args) { try { //创建一个url实例 URL url = new URL("https://www.taobao.com/"); //?后面是参数,#后面是锚点 URL u = new URL(url, "index.html?username=taobao#test"); System.out.println("协议:"+u.getProtocol()); System.out.println("主机:"+u.getHost()); //如果没有指定端口号,则返回默认端口号,此时getPort()返回值为-1 System.out.println("端口:"+u.getPort()); System.out.println("文件路径:"+u.getPath()); System.out.println("文件名称:"+u.getFile()); System.out.println("相对路径:"+u.getRef()); System.out.println("查询字符串:"+u.getQuery()); } catch (MalformedURLException e) { e.printStackTrace(); } } } 输出: 协议:https 主机:www.taobao.com 端口:-1 文件路径:/index.html 文件名称:/index.html?username=taobao 相对路径:test 查询字符串:username=taobao **使用URL读取网页内容** 1、通过urll对象的openStream()方法可以得到指定资源的输入流 2、通过输入流可以读取、访问网络上的数据 3、实例如下 输入: package rmd_intl_app.Test; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; /** * @Description: 使用URL读取网页内容 */ public class UrlTest02 { 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 (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=http://s1.bdstatic.com/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></span><span class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn"></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>新闻</a> <a href=http://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&tpl=mn&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>©2017 Baidu <a href=http://www.baidu.com/duty/>使用百度前必读</a> <a href=http://jianyi.baidu.com/ class=cp-feedback>意见反馈</a> 京ICP证030173号 <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html> **三、通过Socket实现TCP编程** **Socket通信** TCP协议是面向连接,可靠的、有序的、以字节流的方式发送数据 基于TCP协议实现网络通信的类:客户端的Socket类和服务器端的ServerSocket类 **Socket通信模型** 三个步骤:建立连接(客户端和服务器端进行建立通信连接)、开始通信(客户端向服务器发送请求,服务器端响应请求,然后返回请求响应)、结束通信(关闭通信和连接请求) ![Center 3][] **Socket通信实现步骤** 1、创建socket和ServerSocket 2、打开连接到Socket输入/输出流 3、按照协议对Socket进行读/写操作 4、关闭输入/输出流,关闭Socket **服务器端** 1、创建ServerSocket对象,绑定监听端口 2、通过accept方法监听客户端请求 3、连接建立后,通过输入流读取客户端发送的请求信息 4、通过输出流向客户端发送响应信息 5、关闭相关资源 6、实例如下 package rmd_intl_app.Test; 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.ServerSocket; import java.net.Socket; /** * @Description: 服务器端 */ public class Server { public static void main(String[] args) { try { System.out.println("服务器端已经启动,等待连接..........."); //1、创建ServerSocket服务器对象,指定绑定的端口并监听 ServerSocket serverSocket = new ServerSocket(8080); Socket socket = serverSocket.accept();//进行监听,等待客户端连接 //2、字节输入流 InputStream is = socket.getInputStream(); InputStreamReader isr = new InputStreamReader(is);//将字节流转换为字符流 BufferedReader br = new BufferedReader(isr);//为输入流添加缓冲 String info = null; //3、循环读取客户端信息 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(); } } } **客户端** 1、创建Socket对象,指明需要连接的服务器的地址和端口号 2、连接建立后,通过输出流向服务器端发送请求信息 3、通过输入流获取服务器响应的信息 4、关闭相关资源 5、实例如下 package rmd_intl_app.Test; 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; /** * @Description: 客户端 */ public class Client { public static void main(String[] args) { try { //1、创建socket客户端,指定服务器地址和端口 Socket socket = new Socket("192.168.0.235",8080); //2、获取输出流,向服务器发送信息 OutputStream os = socket.getOutputStream();//字节输出流 PrintWriter pw = new PrintWriter(os);//将输出流包装为打印流 pw.write("hello word"); pw.flush(); //3、获取输入流,读取服务器端响应的信息 InputStream is = socket.getInputStream();//字节输入流 BufferedReader br = new BufferedReader(new InputStreamReader(is));//为输入流添加缓冲 String info = null; while ((info = br.readLine()) != null) { System.out.println("我是客户端,服务器端说:"+info); } socket.shutdownInput();//关闭输出流 //4、关闭资源 br.close(); is.close(); pw.close(); os.close(); socket.close(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } **多线程下的通信** 应用多线程来实现服务器与多客户端之间的通信 ** 基本步骤:** 1、服务器端创建ServerSocket,循环调用accept()等待客户端连接 2、客户端创建一个socket并请求和服务器端连接 3、服务器端接受客户端请求,创建socket与该客户端建立专线连接 4、建立两个连接的socket在一个单独的线程上对话 5、服务器端继续等待新的连接 实例如下: 1、服务器线程处理类 package rmd_intl_app.Test; 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; /** * @Description: 服务器线程处理类 */ 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; //3、循环读取客户端信息 while ((info = br.readLine()) != null) { System.out.println("我是服务器,客户端说:" + info); } socket.shutdownInput();//关闭输入流 //获取输出流,响应客户端请求 os = socket.getOutputStream(); pw = new PrintWriter(os); pw.write("我是服务器,非常欢迎你!"); pw.flush(); } catch (Exception e) { e.printStackTrace(); }finally{ try { if(pw != null){ pw.close(); } if(pw != null){ os.close(); } if(pw != null){ br.close(); } if(pw != null){ isr.close(); } if(pw != null){ is.close(); } } catch (IOException e) { e.printStackTrace(); } } } } 2、服务器端 package rmd_intl_app.Test; import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; /** * @Description: 服务器端 */ public class Server { public static void main(String[] args) { try { //创建ServerSocket服务器对象,指定绑定的端口并监听 ServerSocket serverSocket = new ServerSocket(8080); System.out.println("服务端已经启动,等待客户端连接....."); Socket socket = null; //记录客户端的数量 int count = 0; while(true){ //进行监听,等待客户端连接 socket = serverSocket.accept(); //创建一个新线程 ServerThread thread = new ServerThread(socket); //启动线程 thread.run(); count++;//统计客户端的数量 System.out.println("客户端的数量为:"+count); InetAddress address = socket.getInetAddress(); System.out.println("客户端IP为:"+address.getHostAddress()); } } catch (IOException e) { e.printStackTrace(); } } } 3、客户端 package rmd_intl_app.Test; 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; /** * @Description: 客户端 */ public class Client { public static void main(String[] args) { try { //1、创建socket客户端,指定服务器地址和端口 Socket socket = new Socket("192.168.0.235",8080); //2、获取输出流,向服务器发送信息 OutputStream os = socket.getOutputStream();//字节输出流 PrintWriter pw = new PrintWriter(os);//将输出流包装为打印流 pw.write("hello word"); pw.flush(); //3、获取输入流,读取服务器端响应的信息 InputStream is = socket.getInputStream();//字节输入流 BufferedReader br = new BufferedReader(new InputStreamReader(is));//为输入流添加缓冲 String info = null; while ((info = br.readLine()) != null) { System.out.println("我是客户端,服务器端说:"+info); } socket.shutdownInput();//关闭输出流 //4、关闭资源 br.close(); is.close(); pw.close(); os.close(); socket.close(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } 注意事项: 1、资源必须释放,关闭资源的代码可写的严谨点,且倒序以此关闭 2、服务器端优先启动 3、多线程下客户端可以多次启动,体验多线程效果 **四、通过Socket实现UDP编程** **UDP编程** UDP协议(用户数据报协议)是无连接、不可靠、无序的,相对来说传输速度比较快 UDP协议以数据报作为数据传输的载体。就是进行数据传输时,首先需要将要传输的数据定义成数据报(Datagram),在数据报中指明数据所要达到的Socket(主机地址和端口号),然后在将数据报发送过去 操作相关类: DatagramPacket:表示数据报包 DatagramSocket:进行端到端通信的类 **UDP通信模型** **服务器端实现步骤** 1、创建DatagramSocket,指定端口号 2、创建DatagramPacket 3、接受客户端发送的数据信息 4、读取数据 5、实例如下 package rmd_intl_app.Test; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; /** * @Description: 服务器端 */ public class UDPServer { public static void main(String[] args) throws IOException { /** * 服务器端接受数据 */ //1、创建服务器DatagramSocket,指定端口 DatagramSocket socket = new DatagramSocket(8080); //2、创建数据报,用于接受客户端发送来的数据 byte[] b = new byte[2014];//创建字节数组,指定接受数据包的大小 DatagramPacket packet = new DatagramPacket(b, b.length); //3、接受客户端发送来的数据 System.out.println("服务器已经启动,等待连接....."); socket.receive(packet);//此方法在接受数据之前会一直堵塞 //4、读取数据 String info = new String(b,0,packet.getLength()); System.out.println("我是服务器,客户端说:"+info); /** * 服务器端响应数据 */ //1、定义服务器的地址,端口号,数据 InetAddress address = packet.getAddress(); int port = packet.getPort(); byte[] by = "欢迎您!".getBytes(); //2、创建数据报,包含响应的数据信息 DatagramPacket packet2 = new DatagramPacket(by, by.length, address, port); //3、响应客户端 socket.send(packet2); //4、关闭资源 socket.close(); } } **客户端实现步骤** 1、定义发送信息 2、创建DatagramPacket,包含将要发送的信息 3、创建DatagramSocket 4、实例如下 package rmd_intl_app.Test; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetAddress; /** * @Description: 客户端 * @author lc * @date 2017年6月24日 */ public class UDPClient { public static void main(String[] args) throws IOException { /** * 向服务器端发送数据 */ //1、定义服务器的地址,端口号,数据 InetAddress address = InetAddress.getByName("192.168.0.235"); int port = 8080; byte[] data = "您好,我是客户端".getBytes(); //2、创建数据报,包含发送的数据信息 DatagramPacket packet = new DatagramPacket(data, data.length, address,port); //3、创建DatagramSocket对象 DatagramSocket socket = new DatagramSocket(); //4、向服务器端发送数据报 socket.send(packet); /** * 接受服务器端响应的数据 */ //1、创建数据报,用于接受服务器端响应的数据 byte[] b = new byte[1024]; DatagramPacket packet2 = new DatagramPacket(b,b.length); //2、接受服务器端响应的数据 socket.receive(packet2); //3、读取数据 String info = new String(b,0,packet2.getLength()); System.out.println("我是客户端,服务器说:"+info); //4、关闭资源 socket.close(); } } (以上是个人学习笔记,不对之处望指正) [Center]: /images/20220613/876dc2f61df74636940bbde933da53e9.png [Center 1]: /images/20220613/15d34a5a9c8242b08b80b31a5f5f15d1.png [Center 2]: /images/20220613/b4e3cafa60fc44b49a629a95644e54ea.png [Center 3]: /images/20220613/fb2c64fe035f42cfb191c8a91e9bad44.png
相关 网络编程通信问题:Java Socket API中的难题 在使用Java Socket API进行网络编程时,可能会遇到以下几个难点: 1. **连接建立**:Socket需要与对方服务器建立连接。这通常通过TCP协议完成,过程中可 Bertha 。/ 2024年09月20日 07:09/ 0 赞/ 3 阅读
相关 Java网络编程:Socket通信的常见问题 在Java网络编程中,Socket通信过程中可能会遇到一些常见问题。以下是这些问题及解决方法: 1. **连接失败**: - 确保服务器的端口号正确。 - 检查客 ╰+攻爆jí腚メ/ 2024年09月18日 14:30/ 0 赞/ 4 阅读
相关 Java网络编程中服务器端Socket通信问题 在Java的网络编程中,服务器端Socket通信主要涉及到以下几个问题: 1. **Socket创建**:首先需要使用ServerSocket类来创建一个监听特定端口的服务器 爱被打了一巴掌/ 2024年09月18日 03:30/ 0 赞/ 10 阅读
相关 Java网络编程:Socket通信示例 在Java中,Socket是一种用于客户端和服务器之间进行双向通信的API。以下是一个简单的Socket通信示例: 1. 创建ServerSocket(服务器端): ```j 蔚落/ 2024年09月17日 18:21/ 0 赞/ 10 阅读
相关 Java网络编程中的socket通信问题案例 在Java网络编程中,Socket是一种API,它允许两个进程通过网络进行交互。以下是一个简单的Socket通信问题案例: 案例:两人电脑通过互联网进行文件传输 1. ** 迈不过友情╰/ 2024年09月16日 15:03/ 0 赞/ 10 阅读
相关 Java网络编程中socket通信失败问题 在Java的网络编程中,Socket通信失败可能有以下几个原因: 1. **连接异常**:可能是由于网络问题,如防火墙阻止,或者服务器端未正确启动。 2. **地址错误** 柔情只为你懂/ 2024年09月16日 14:06/ 0 赞/ 13 阅读
相关 Java网络编程:Socket通信中的错误处理 在Java的Socket通信中,确实需要进行错误处理。以下是一些常见的错误和处理方式: 1. **Socket创建失败**: - 错误原因:网络连接问题、服务器端未启动 快来打我*/ 2024年09月10日 22:51/ 0 赞/ 20 阅读
相关 Java中的网络编程—Socket通信 一、网络基础知识 两台计算机要想通过网络进行通信,那么他们必须满足ip地址、协议、端口号这三个必然的条件 1、两台主机必须表明所在的身份和位置,也就是ip地址 超、凢脫俗/ 2022年06月13日 00:17/ 0 赞/ 261 阅读
还没有评论,来说两句吧...