Java中的网络编程—Socket通信

一、网络基础知识

两台计算机要想通过网络进行通信,那么他们必须满足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-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>&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{
	//和本线程相关的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();
	}
}

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



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

十点摆码

有用你就打赏一下

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值