Java中的网络编程—Socket通信

来源:互联网 发布:淘宝字体侵权 编辑:程序博客网 时间:2024/05/18 18:20

一、网络基础知识

两台计算机要想通过网络进行通信,那么他们必须满足ip地址、协议、端口号这三个必然的条件

   1、两台主机必须表明所在的身份和位置,也就是ip地址

   2、必须有共同的语言,不然无法交流,也就是我们所说的协议

   3、需要有相应的端口号,一台主机可以运行多个应用程序,怎么辨别不同通信的程序,那么就需要端口号来区分

  

TCP/IP协议

   TCP/IP是目前世界上应用最为广泛的协议,是以TCP和IP为基础的不同层次上多个协议的集合,也称TCP/IP协议族或TCP/IP协议栈

   TCP:Transmission Control Protocol 传输控制协议   

   IP:Internet Protocol 互联网协议

TCP/IP模型

   一般来说,会将网络进行分层,常见的会把网络分为五层,叫TCP/IP模型

  

IP地址

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

   IP地址格式:数字型,如:192.168.0.1

   IPv4:32位的二进制

  

端口

   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-20160808TVQLaddressIP地址:192.168.0.235字节数组的形式的IP:[-64, -88, 0, -21]直接输出InetAddress对象:MS-20160808TVQL/192.168.0.235address2获取计算机名:MS-20160808TVQLaddress2IP地址:192.168.0.235address3计算机名:MS-20160808TVQLaddress3IP地址: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()返回值为-1System.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>&copy;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通信模型

   三个步骤:建立连接(客户端和服务器端进行建立通信连接)、开始通信(客户端向服务器发送请求,服务器端响应请求,然后返回请求响应)、结束通信(关闭通信和连接请求)

  

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{//和本线程相关的socketSocket 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();}}

(以上是个人学习笔记,不对之处望指正) 



原创粉丝点击