Java基础6网络编程

12.1 基本概念

Java基础6网络编程_第1张图片
如今,计算机已经成为人们学习、工作、生活必不可少的工具。我们利用计算机可以和亲朋好友网上聊天,也可以玩网游、发邮件等等,这些功能实现都离不开计算机网络。计算机网络实现了不同计算机之间的通信,这必须依靠编写网络程序来实现。下面,我们将教大家如何编写网络程序
在学习编程之前,我们首先要了解关于网络通信的一些概念。

什么是计算机网络?

计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。

从其中我们可以提取到以下内容:
1.计算机网络的作用:资源共享、信息传递、负载均衡与分布处理
2.计算机网络的组成:
a) 计算机硬件:计算机(大中小型服务器,台式机、笔记本等)、外部设备(路由器、交换机等)、通信线路(双绞线、光纤等)。
b) 计算机软件:网络操作系统(Windows 2000 Server/Advance Server、Unix、Linux等)、网络管理软件(WorkWin、SugarNMS等)、网络通信协议(如TCP/IP协议栈等)。
3. 计算机网络的多台计算机是具有独立功能的,而不是脱离网络就无法存在的。

什么是网络通信协议?

通过计算机网络可以实现不同计算机之间的连接与通信,但是计算机网络中实现通信必须有一些约定即通信协议,对速率、传输代码、代码结构、传输控制步骤、出错控制等制定标准。就像两个人想要顺利沟通就必须使用同一种语言一样,如果一个人只懂英语而另外一个人只懂中文,这样就会造成没有共同语言而无法沟通。

国际标准化组织(ISO,即International Organization for Standardization)定义了网络通信协议的基本框架,被称为OSI(Open System Interconnect,即开放系统互联)模型。要制定通讯规则,内容会很多,比如要考虑A电脑如何找到B电脑,A电脑在发送信息给B电脑时是否需要B电脑进行反馈,A电脑传送给B电脑的数据格式又是怎样的?内容太多太杂,所以OSI模型将这些通讯标准进行层次划分,每一层次解决一个类别的问题,这样就使得标准的制定没那么复杂。OSI模型制定的七层标准模型,分别是:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层

OSI七层协议模型如图12-1所示:
Java基础6网络编程_第2张图片
虽然国际标准化组织制定了这样一个网络通信协议的模型,但是实际上互联网通讯使用最多的网络通信协议是TCP/IP网络通信协议。

TCP/IP 是一个协议族,也是按照层次划分,共四层:应用层,传输层,互连网络层,网络接口层(物理+数据链路层)
那么TCP/IP协议和OSI模型有什么区别呢?OSI网络通信协议模型,是一个参考模型,而TCP/IP协议是事实上的标准。TCP/IP协议参考了OSI 模型,但是并没有严格按照OSI规定的七层标准去划分,而只划分了四层,这样会更简单点,当划分太多层次时,你很难区分某个协议是属于哪个层次的。
Java基础6网络编程_第3张图片
TCP/IP协议和OSI模型也并不冲突,TCP/IP协议中的应用层协议,就对应于OSI中的应用层,表示层,会话层。就像以前有工业部和信息产业部,现在实行大部制后只有工业和信息化部一个部门,但是这个部门还是要做以前两个部门一样多的事情,本质上没有多大的差别。TCP/IP中有两个重要的协议,传输层的TCP协议和互连网络层的IP协议,因此就拿这两个协议做代表,来命名整个协议族了,再说TCP/IP协议时,是指整个协议族。
Java基础6网络编程_第4张图片

网络协议的分层

由于网络结点之间联系很复杂,在制定协议时,把复杂成份分解成一些简单的成份,再将它们复合起来。最常用的复合方式是层次方式,即同层间可以通信、上一层可以调用下一层,而与再下一层不发生关系。
把用户应用程序作为最高层,把物理通信线路作为最低层,将其间的协议处理分为若干层,规定每层处理的任务,也规定每层的接口标准。
ISO模型与TCP/IP模型的对应关系如图12-2所示。
Java基础6网络编程_第5张图片

数据封装与解封:

由于用户传输的数据一般都比较大,有的可以达到MB字节,一次性发送出去十分困难,于是就需要把数据分成许多片段,再按照一定的次序发送出去。这个过程就需要对数据进行封装。
数据封装(Data Encapsulation)是指将协议数据单元(PDU)封装在一组协议头和协议尾中的过程。在OSI七层参考模型中,每层主要负责与其它机器上的对等层进行通信。该过程是在协议数据单元(PDU)中实现的,其中每层的PDU一般由本层的协议头、协议尾和数据封装构成。

1.数据发送处理过程
(1)应用层将数据交给传输层,传输层添加上TCP的控制信息(称为TCP头部),这个数据单元称为段(Segment),加入控制信息的过程称为封装。然后,将段交给网络层。
(2)网络层接收到段,再添加上IP头部,这个数据单元称为包(Packet)。然后,将包交给数据链路层。
(3) 数据链路层接收到包,再添加上MAC头部和尾部,这个数据单元称为帧(Frame)。然后,将帧交给物理层。
(4)物理层将接收到的数据转化为比特流,然后在网线中传送。

2.数据接收处理过程
(1)物理层接收到比特流,经过处理后将数据交给数据链路层。
(2)数据链路层将接收到的数据转化为数据帧,再除去MAC头部和尾部,这个除去控制信息的过程称为解封,然后将包交给网络层。
(3)网络层接收到包,再除去IP头部,然后将段交给传输层。
(4)传输层接收到段,再除去TCP头部,然后将数据交给应用层。

从以上传输过程中,可以总结出以下规则:
(1)发送方数据处理的方式是从高层到底层,逐层进行数据封装
(2)接收方数据处理的方式是从底层到高层,逐层进行数据解封装
接收方的每一层只把对该层有意义的数据拿走,或者说每一层只能处理发送方同等层的数据,然后把其余的部分传递给上一层,这就是对等层通信的概念。

数据封装与解封如图12-3和图12-4所示:
Java基础6网络编程_第6张图片
Java基础6网络编程_第7张图片

IP地址:

定位:
IP定位电脑
Port定位软件
URL定位资源

用来标识网络中的一个通信实体的地址。通信实体可以是计算机、路由器等。 比如互联网的每个服务器都要有自己的IP地址,而每个局域网的计算机要通信也要配置IP地址。路由器是连接两个或多个网络的网络设备。
目前主流使用的IP地址是IPV4,但是随着网络规模的不断扩大,IPV4面临着枯竭的危险,所以推出了IPV6。

IPV4:32位地址,并以8位为一个单位,分成四部分,以点分十进制表示,如192.168.0.1。因为8位二进制的计数范围是00000000—11111111,对应十进制的0-255,所以-4.278.4.1是错误的IPV4地址。

IPV6:128位(16个字节)写成8个16位的无符号整数,每个整数用四个十六进制位表示,每个数之间用冒号(:)分开,如:3ffe:3201:1401:1280:c8ff:fe4d:db39:1984
IPTest.java

package com.sxt.loc;

import java.net.InetAddress;
import java.net.UnknownHostException;

/**

  • IP:定位一个节点:计算机、路由、通讯设备等
  • InetAddress: 多个静态方法
  • 1、getLocalHost:本机
  • 2、getByName:根据域名DNS | IP地址 -->IP
  • 两个成员方法
  • 1、getHostAddress: 返回地址
  • 2、getHostName:返回计算机名

*/
public class IPTest {

public static void main(String[] args) throws UnknownHostException {
	//使用getLocalHost方法创建InetAddress对象  本机
	InetAddress addr = InetAddress.getLocalHost();
	System.out.println(addr.getHostAddress());  //返回:192.168.1.110
	System.out.println(addr.getHostName());  //输出计算机名
	
	//根据域名得到InetAddress对象  DNS域名解析(将IP地址和有意义的字符串之间转换)
	addr = InetAddress.getByName("www.shsxt.com"); 
	System.out.println(addr.getHostAddress());  //返回 shsxt服务器的ip地址123.56.138.186
	System.out.println(addr.getHostName());  //输出:www.shsxt.com
	
	//根据ip得到InetAddress对象
	addr = InetAddress.getByName("123.56.138.176"); 
	System.out.println(addr.getHostAddress());  //返回 shsxt的ip:123.56.138.176
	System.out.println(addr.getHostName());  //输出ip而不是域名。如果这个IP地 址不存在或DNS服务器不允许进行IP地址和域名的映射,
}

}
注意事项
1.127.0.0.1 本机地址
2.192.168.0.0–192.168.255.255为私有地址,属于非注册地址,专门为组织机构内部使用。

端口:

IP地址用来标识一台计算机,但是一台计算机上可能提供多种网络应用程序,如何来区分这些不同的程序呢?这就要用到端口。0~65535
端口是虚拟的概念,并不是说在主机上真的有若干个端口。通过端口,可以在一个主机上运行多个网络应用程序。 端口的表示是一个16位的二进制整数对应十进制的0-65535。
Oracle、MySQL、Tomcat、QQ、msn、迅雷、电驴、360等网络程序都有自己的端口。
Java基础6网络编程_第8张图片
总结
1.IP地址好比每个人的地址(门牌号),端口好比是房间号。必须同时指定IP地址和端口号才能够正确的发送数据。
2. IP地址好比为电话号码,而端口号就好比为分机号。
Java基础6网络编程_第9张图片
Java基础6网络编程_第10张图片
PortTest.java

package com.sxt.loc;

import java.net.InetSocketAddress;
/**
 * 端口
 * 1、区分软件
 * 2、2个字节 0-65535  UDP TCP
 * 3、同一个协议端口不能冲突
 * 4、定义端口越大越好
 * InetSocketAddress 
 * 1、构造器
 *   new InetSocketAddress(地址|域名,端口);
 * 2、方法
 *  getAddress​()  返回地址
 *  getPort() 返回端口
 *  getHostName()  返回
 */
public class PortTest {

	public static void main(String[] args) {
		//包含端口
		InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1",8080);
		InetSocketAddress socketAddress2 = new InetSocketAddress("localhost",9000);
		System.out.println(socketAddress.getHostName());
		System.out.println(socketAddress.getAddress());
		System.out.println(socketAddress2.getAddress());
		System.out.println(socketAddress2.getPort());
	}
}

在这里插入图片描述

URL:

Java基础6网络编程_第11张图片
在www上,每一信息资源都有统一且唯一的地址,该地址就叫URL(Uniform Resource Locator),它是www的统一资源定位符。
URL由4部分组成:协议 、存放资源的主机域名、资源文件名和端口号
如果未指定该端口号,则使用协议默认的端口。例如http 协议的默认端口为 80。 在浏览器中访问网页时,地址栏显示的地址就是URL。
在java.net包中提供了URL类,该类封装了大量复杂的涉及从远程站点获取信息的细节。
URLTest01.java

package com.sxt.loc;

import java.net.MalformedURLException;
import java.net.URL;

/**
 * URL: 统一资源定位器,互联网三大基石之一(html http),区分资源
 * 1、协议
 * 2、域名、计算机
 * 3、端口: 默认80
 * 4、请求资源
 *  http://www.baidu.com:80/index.html?uname=shsxt&age=18#a
 */
public class URLTest01 {

	public static void main(String[] args) throws MalformedURLException {
		URL url = new URL("http://www.baidu.com:80/index.html?uname=shsxt&age=18#a");
		//获取四个值
		System.out.println("协议:"+url.getProtocol());
		System.out.println("域名|ip:"+url.getHost());
		System.out.println("端口:"+url.getPort());
		System.out.println("请求资源1:"+url.getFile());
		System.out.println("请求资源2:"+url.getPath());
		
		//参数
		System.out.println("参数:"+url.getQuery());
		//锚点
		System.out.println("锚点:"+url.getRef());
	}

}

Java基础6网络编程_第12张图片
在这里插入图片描述

爬虫原理

Java基础6网络编程_第13张图片
SpiderTest01.java

package com.sxt.loc;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;

/**
 * 网络爬虫的原理
 */
public class SpiderTest01 {

	public static void main(String[] args) throws Exception {
		//获取URL
		URL url =new URL("https://www.jd.com");
		//URL url =new URL("https://www.dianping.com");
		//下载资源
		InputStream is = url.openStream();
		BufferedReader br =new BufferedReader(new InputStreamReader(is,"UTF-8"));
		String msg =null;
		while(null!=(msg=br.readLine())) {
			System.out.println(msg);
		}
		br.close();
		//分析
		//处理。。。。
	}

}

SpiderTest02.java
package com.sxt.loc;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;

/**

  • 网络爬虫的原理 +模拟浏览器
    */
    public class SpiderTest02 {

    public static void main(String[] args) throws Exception {
    //获取URL
    URL url =new URL(“https://www.dianping.com”);
    //下载资源
    HttpURLConnection conn =(HttpURLConnection) url.openConnection();
    conn.setRequestMethod(“GET”);
    conn.setRequestProperty(“User-Agent”,“Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36”);
    BufferedReader br =new BufferedReader(new InputStreamReader(conn.getInputStream(),“UTF-8”));
    String msg =null;
    while(null!=(msg=br.readLine())) {
    System.out.println(msg);
    }
    br.close();
    //分析
    //处理。。。。
    }

}
Java基础6网络编程_第14张图片

Socket:

我们开发的网络应用程序位于应用层TCP和UDP属于传输层协议,在应用层如何使用传输层的服务呢?在应用层和传输层之间,则是使用套接Socket来进行分离。
相当于码头。

套接字就像是传输层为应用层开的一个小口,应用程序通过这个小口向远程发送数据,或者接收远程发来的数据;而这个小口以内,也就是数据进入这个口之后,或者数据从这个口出来之前,是不知道也不需要知道的,也不会关心它如何传输,这属于网络其它层次工作。
Java基础6网络编程_第15张图片
Socket实际是传输层供给应用层的编程接口。Socket就是应用层与传输层之间的桥梁。使用Socket编程可以开发客户机和服务器应用程序,可以在本地网络上进行通信,也可通过Internet在全球范围内通信。
Java基础6网络编程_第16张图片

Java基础6网络编程_第17张图片

12.2TCP UDP

12.2.1 TCP协议和UDP协议的联系和区别

TCP协议和UDP协议是传输层的两种协议。Socket是传输层供给应用层的编程接口,所以Socket编程就分为TCP编程和UDP编程两类。

在网络通讯中,TCP方式就类似于拨打电话,使用该种方式进行网络通讯时,需要建立专门的虚拟连接,然后进行可靠的数据传输,如果数据发送失败,则客户端会自动重发该数据。
Java基础6网络编程_第18张图片
而UDP方式就类似于发送短信,使用这种方式进行网络通讯时,不需要建立专门的虚拟连接,传输也不是很可靠,如果发送失败则客户端无法获得。
Java基础6网络编程_第19张图片
这两种传输方式都在实际的网络编程中使用,重要的数据一般使用TCP方式进行数据传输,而大量的非核心数据则可以通过UDP方式进行传递,在一些程序中甚至结合使用这两种方式进行数据传递。

由于TCP需要建立专用的虚拟连接以及确认传输是否正确,所以使用TCP方式的速度稍微慢一些,而且传输时产生的数据量要比UDP稍微大一些。

总结
1.TCP是面向连接的,传输数据安全,稳定,效率相对较低
2.UDP是面向无连接的,传输数据不安全,效率较高

12.2.2 TCP协议

TCP(Transfer Control Protocol)是面向连接的,所谓面向连接,就是当计算机双方通信时必需经过先建立连接,然后传送数据,最后拆除连接三个过程。

TCP在建立连接时又分三步走:
第一步,是请求端(客户端)发送一个包含SYN即同步(Synchronize)标志的TCP报文,SYN同步报文会指明客户端使用的端口以及TCP连接的初始序号。

第二步,服务器在收到客户端的SYN报文后,将返回一个SYN+ACK的报文,表示客户端的请求被接受,同时TCP序号被加一,ACK即确认(Acknowledgement)。

第三步,客户端也返回一个确认报文ACK给服务器端,同样TCP序列号被加一,到此一个TCP连接完成。然后才开始通信的第二步:数据处理。
这就是所说的TCP的三次握手(Three-way Handshake)。

基本步骤

Java基础6网络编程_第20张图片
Java基础6网络编程_第21张图片
Java基础6网络编程_第22张图片
Java基础6网络编程_第23张图片
Java基础6网络编程_第24张图片
Java基础6网络编程_第25张图片
Java基础6网络编程_第26张图片
在这里插入图片描述

例1:
Server.java

package com.sxt.tcp;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 熟悉流程
 * 创建服务器
 * 1、指定端口 使用ServerSocket创建服务器
 * 2、阻塞式等待连接 accept
 * 3、操作: 输入输出流操作
 * 4、释放资源 
 */
public class Server {

	public static void main(String[] args) throws IOException {
		System.out.println("-----Server-----");
		// 1、指定端口 使用ServerSocket创建服务器
		ServerSocket server =new ServerSocket(8888);
		// 2、阻塞式等待连接 accept
		Socket  client =server.accept(); 
		System.out.println("一个客户端建立了连接");
		// 3、操作: 输入输出流操作
		DataInputStream dis =new DataInputStream(client.getInputStream());
		String data =dis.readUTF();
		System.out.println(data);
		// 4、释放资源 
		dis.close();
		client.close();
		
		server.close();
	}

}

Client.java

package com.sxt.tcp;

import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 熟悉流程
 * 创建客户端
 * 1、建立连接: 使用Socket创建客户端 +服务的地址和端口
 * 2、操作: 输入输出流操作
 * 3、释放资源 
 */
public class Client {

	public static void main(String[] args) throws UnknownHostException, IOException {
		System.out.println("-----Client-----");
		//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
		Socket client =new Socket("localhost",8888);
		//2、操作: 输入输出流操作
		DataOutputStream dos =new DataOutputStream(client.getOutputStream());
		String data ="hello";
		dos.writeUTF(data);
		dos.flush();
		//3、释放资源 
		dos.close();
		client.close();
	}

}

文件上传

Java基础6网络编程_第27张图片
例2:单向的
LoginServer.java

package com.sxt.tcp;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 模拟登录 单向
 * 创建服务器
 * 1、指定端口 使用ServerSocket创建服务器
 * 2、阻塞式等待连接 accept
 * 3、操作: 输入输出流操作
 * 4、释放资源 
 */
public class LoginServer {

	public static void main(String[] args) throws IOException {
		System.out.println("-----Server-----");
		// 1、指定端口 使用ServerSocket创建服务器
		ServerSocket server =new ServerSocket(8888);
		// 2、阻塞式等待连接 accept
		Socket  client =server.accept(); 
		System.out.println("一个客户端建立了连接");
		// 3、操作: 输入输出流操作
		DataInputStream dis =new DataInputStream(client.getInputStream());
		String datas =dis.readUTF();
		//分析
		String[] dataArray = datas.split("&");
		for(String info:dataArray) {
			String[] userInfo =info.split("=");
				if(userInfo[0].equals("uname")) {
					System.out.println("你的用户名为:"+userInfo[1]);
				}else if(userInfo[0].equals("upwd")) {
					System.out.println("你的密码为:"+userInfo[1]);
				}
				
		}
		// 4、释放资源 
		dis.close();
		client.close();
		
		server.close();
	}

}

LoginClient.java

package com.sxt.tcp;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 模拟登录 单向
 * 创建客户端
 * 1、建立连接: 使用Socket创建客户端 +服务的地址和端口
 * 2、操作: 输入输出流操作
 * 3、释放资源 
 */
public class LoginClient {

	public static void main(String[] args) throws UnknownHostException, IOException {
		System.out.println("-----Client-----");
		BufferedReader console =new BufferedReader(new InputStreamReader(System.in));
		System.out.print("请输入用户名:");
		String uname =console.readLine();
		System.out.print("请输入密码:");
		String upwd =console.readLine();
		
		//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
		Socket client =new Socket("localhost",8888);
		//2、操作: 输入输出流操作
		DataOutputStream dos =new DataOutputStream(client.getOutputStream());		
		dos.writeUTF("uname="+uname+"&"+"upwd="+upwd);
		dos.flush();
		//3、释放资源 
		dos.close();
		client.close();
	}

}

Java基础6网络编程_第28张图片
例3:双向的
LoginTwoWayServer.java

package com.sxt.tcp;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 模拟登录 双向
 * 创建服务器
 * 1、指定端口 使用ServerSocket创建服务器
 * 2、阻塞式等待连接 accept
 * 3、操作: 输入输出流操作
 * 4、释放资源 
 */
public class LoginTwoWayServer {

	public static void main(String[] args) throws IOException {
		System.out.println("-----Server-----");
		// 1、指定端口 使用ServerSocket创建服务器
		ServerSocket server =new ServerSocket(8888);
		// 2、阻塞式等待连接 accept
		Socket  client =server.accept(); 
		System.out.println("一个客户端建立了连接");
		// 3、操作: 输入输出流操作
		DataInputStream dis =new DataInputStream(client.getInputStream());
		String datas =dis.readUTF();
		String uname ="";
		String upwd ="";
		//分析
		String[] dataArray = datas.split("&");
		for(String info:dataArray) {
				String[] userInfo =info.split("=");
				if(userInfo[0].equals("uname")) {
					System.out.println("你的用户名为:"+userInfo[1]);
					uname = userInfo[1];
				}else if(userInfo[0].equals("upwd")) {
					System.out.println("你的密码为:"+userInfo[1]);
					upwd = userInfo[1];
				}				
		}
		//输出
		DataOutputStream dos =new DataOutputStream(client.getOutputStream());			
		if(uname.equals("shsxt") && upwd.equals("laopei")) { //成功
			dos.writeUTF("登录成功,欢迎回来");
		}else { //失败
			dos.writeUTF("用户名或密码错误");
		}
		dos.flush();
		// 4、释放资源 
		dis.close();
		client.close();
		
		server.close();
	}

}

LoginTwoWayClient.java

package com.sxt.tcp;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 模拟登录 双向
 * 创建客户端
 * 1、建立连接: 使用Socket创建客户端 +服务的地址和端口
 * 2、操作: 输入输出流操作
 * 3、释放资源 
 */
public class LoginTwoWayClient {

	public static void main(String[] args) throws UnknownHostException, IOException {
		System.out.println("-----Client-----");
		BufferedReader console =new BufferedReader(new InputStreamReader(System.in));
		System.out.print("请输入用户名:");
		String uname =console.readLine();
		System.out.print("请输入密码:");
		String upwd =console.readLine();
		
		//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
		Socket client =new Socket("localhost",8888);
		//2、操作: 输入输出流操作
		DataOutputStream dos =new DataOutputStream(client.getOutputStream());		
		dos.writeUTF("uname="+uname+"&"+"upwd="+upwd);
		dos.flush();
		
		DataInputStream dis =new DataInputStream(client.getInputStream());
		String result =dis.readUTF();		
		System.out.println(result);
		//3、释放资源 
		dos.close();
		client.close();
	}

}

Java基础6网络编程_第29张图片
1.只能输入一次
2.先输入才能获取
Java基础6网络编程_第30张图片

例4:文件
FileServer.java

package com.sxt.tcp;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 存储文件
 * 创建服务器
 * 1、指定端口 使用ServerSocket创建服务器
 * 2、阻塞式等待连接 accept
 * 3、操作: 输入输出流操作
 * 4、释放资源 
 */
public class FileServer {

	public static void main(String[] args) throws IOException {
		System.out.println("-----Server-----");
		// 1、指定端口 使用ServerSocket创建服务器
		ServerSocket server =new ServerSocket(8888);
		// 2、阻塞式等待连接 accept
		Socket  client =server.accept(); 
		System.out.println("一个客户端建立了连接");
		// 3、操作: 文件拷贝 存储
		InputStream is =new BufferedInputStream(client.getInputStream());
		OutputStream os =new BufferedOutputStream(new FileOutputStream("src/tcp.png"));
		byte[] flush =new byte[1024];
		int len = -1;
		while((len=is.read(flush))!=-1) {
			os.write(flush,0,len);
		}
		os.flush();
		//3、释放资源 
		os.close();
		is.close();
		
		// 4、释放资源 
		client.close();
		
		server.close();
	}

}

FileClient.java

package com.sxt.tcp;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 上传文件
 * 创建客户端
 * 1、建立连接: 使用Socket创建客户端 +服务的地址和端口
 * 2、操作: 输入输出流操作
 * 3、释放资源 
 */
public class FileClient {

	public static void main(String[] args) throws UnknownHostException, IOException {
		System.out.println("-----Client-----");
		//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
		Socket client =new Socket("localhost",8888);
		//2、操作: 拷贝 上传
		InputStream is =new BufferedInputStream(new FileInputStream("src/ndl.png"));
		OutputStream os =new BufferedOutputStream(client.getOutputStream());
		byte[] flush =new byte[1024];
		int len = -1;
		while((len=is.read(flush))!=-1) {
			os.write(flush,0,len);
		}
		os.flush();
		//3、释放资源 
		os.close();
		is.close();
		client.close();
	}

}

多用户登陆

LoginMultiServer.java

package com.sxt.tcp;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 模拟登录 多个客户端请求
 * 创建服务器
 * 1、指定端口 使用ServerSocket创建服务器
 * 2、阻塞式等待连接 accept
 * 3、操作: 输入输出流操作
 * 4、释放资源 
 */
public class LoginMultiServer {
	public static void main(String[] args) throws IOException {
		System.out.println("-----Server-----");
		// 1、指定端口 使用ServerSocket创建服务器
		ServerSocket server =new ServerSocket(8888);
		boolean isRunning =true;;
		// 2、阻塞式等待连接 accept
		while(isRunning) {
			Socket  client =server.accept(); 
			System.out.println("一个客户端建立了连接");
			new Thread(new Channel(client)).start();
		}
		server.close();
	}
	//一个channel就代表一个客户端
	static class Channel implements Runnable{
		private Socket  client;
		//输入流
		private DataInputStream dis;
		//输出流
		private DataOutputStream dos;
		public Channel(Socket  client) {
			this.client = client;
			try {
				//输入
				dis = new DataInputStream(client.getInputStream());
				//输出
				dos =new DataOutputStream(client.getOutputStream());	
			} catch (IOException e) {
				e.printStackTrace();
				release();
			}
			
		}
		
		//接收数据
		private String receive() {
			String datas ="";
			try {
				datas = dis.readUTF();
			} catch (IOException e) {
				e.printStackTrace();
			}
			return datas;
		}
		//释放资源
		private void release() {
			// 4、释放资源 
			try {
				if(null != dos) {
					dos.close();					
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				if(null != dis) {
					dis.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				if(null != client) {
					client.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		//发送数据
		private void send(String msg) {
			try {
				dos.writeUTF(msg);
				dos.flush();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		@Override
		public void run() {						
			// 3、操作: 输入输出流操作					
			String uname ="";
			String upwd ="";
			//分析
			String[] dataArray = receive().split("&");
			for(String info:dataArray) {
					String[] userInfo =info.split("=");
					if(userInfo[0].equals("uname")) {
						System.out.println("你的用户名为:"+userInfo[1]);
						uname = userInfo[1];
					}else if(userInfo[0].equals("upwd")) {
						System.out.println("你的密码为:"+userInfo[1]);
						upwd = userInfo[1];
					}				
			}					
			if(uname.equals("shsxt") && upwd.equals("laopei")) { //成功
				send("登录成功,欢迎回来");
			}else { //失败
				send("用户名或密码错误");
			}
			
			release();
			
		}
		
	}
}

LoginMultiClient.java

package com.sxt.tcp;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 模拟登录  多个客户端请求
 * 创建客户端
 * 1、建立连接: 使用Socket创建客户端 +服务的地址和端口
 * 2、操作: 输入输出流操作
 * 3、释放资源 
 */
public class LoginMultiClient {
	public static void main(String[] args) throws UnknownHostException, IOException {		
		System.out.println("-----Client-----");
		//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
		Socket client =new Socket("localhost",8888);
		//2、操作: 输入输出流操作  先请求后响应
		new Send(client).send();
		new Receive(client).receive();		
		client.close();
	}
	//发送
	static class Send{
		private Socket client;
		private DataOutputStream dos;
		private BufferedReader console ;
		private String msg;
		public Send(Socket client) {			
			console=new BufferedReader(new InputStreamReader(System.in));
			this.msg =init();
			this.client = client;
			try {
				dos=new DataOutputStream(client.getOutputStream());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		private String init() {					
			try {
				System.out.print("请输入用户名:");
				String uname =console.readLine();
				System.out.print("请输入密码:");
				String upwd =console.readLine();
				return "uname="+uname+"&"+"upwd="+upwd;
			} catch (IOException e) {
				e.printStackTrace();
			}
			return "";			
		}
		
		public void send() {
			try {
				dos.writeUTF(msg);
				dos.flush();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	//接收
	static class Receive{
		private Socket client;
		private DataInputStream dis;
		public Receive(Socket client) {
			this.client = client;
			try {
				dis=new DataInputStream(client.getInputStream());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}		
		public void receive() {
			String result;
			try {
				result = dis.readUTF();
				System.out.println(result);
			} catch (IOException e) {
				e.printStackTrace();
			}		
			
		}
	}
}

Java基础6网络编程_第31张图片

手写聊天室_基础简易版

在这里插入图片描述
Java基础6网络编程_第32张图片
群聊:聊天室就是一个转发器
私聊:报文,遍历找到它
容器上面,遍历所有容器还是找到一个

先请求后响应已经不行了,不能你说完一句我才能说。所以需要多线程。
例1:
Client.java

package com.sxt.chat01;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 在线聊天室: 客户端
 * 目标: 实现一个客户可以正常收发消息
 */
public class Client {
	public static void main(String[] args) throws UnknownHostException, IOException {
		System.out.println("-----Client-----");
		//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
		Socket client =new Socket("localhost",8888);
		//2、客户端发送消息 输出流
		BufferedReader console =new BufferedReader(new InputStreamReader(System.in));
		String msg = console.readLine();
		DataOutputStream dos =new DataOutputStream(client.getOutputStream());		
		dos.writeUTF(msg);
		dos.flush();
		//3、获取消息
		DataInputStream dis =new DataInputStream(client.getInputStream());
		msg =dis.readUTF();
		System.out.println(msg);
		
		dos.close();
		dis.close();
		client.close();
		
	}
}

Chat.java

package com.sxt.chat01;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 在线聊天室: 服务器
 * 目标: 实现一个客户可以正常收发消息
 */
public class Chat {
	public static void main(String[] args) throws IOException {
		System.out.println("-----Server-----");
		// 1、指定端口 使用ServerSocket创建服务器
		ServerSocket server =new ServerSocket(8888);
		// 2、阻塞式等待连接 accept
		Socket  client =server.accept(); 
		System.out.println("一个客户端建立了连接");
		
		//3、接收消息
		DataInputStream dis =new DataInputStream(client.getInputStream());
		String msg =dis.readUTF();
		//4、返回消息
		DataOutputStream dos =new DataOutputStream(client.getOutputStream());		
		dos.writeUTF(msg);
		//释放资源
		dos.flush();
		dos.close();
		dis.close();
		client.close();
	}
}

我发出去又给我返回来
Java基础6网络编程_第33张图片
例2:
要发多条
MultiChat.java

package com.sxt.chat01;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 在线聊天室: 服务器
 * 目标: 实现一个客户可以正常收发多条消息 
 */
public class MultiChat {
	public static void main(String[] args) throws IOException {
		System.out.println("-----Server-----");
		// 1、指定端口 使用ServerSocket创建服务器
		ServerSocket server =new ServerSocket(8888);
		// 2、阻塞式等待连接 accept
		Socket  client =server.accept(); 
		System.out.println("一个客户端建立了连接");
		
		DataInputStream dis =new DataInputStream(client.getInputStream());
		DataOutputStream dos =new DataOutputStream(client.getOutputStream());		
		boolean isRunning = true;
		while(isRunning) {
			//3、接收消息
			String msg =dis.readUTF();
			//4、返回消息
			dos.writeUTF(msg);
			//释放资源
			dos.flush();
		}
		dos.close();
		dis.close();
		client.close();
	}
}

MultiClient.java

package com.sxt.chat01;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 在线聊天室: 客户端
 * 目标: 实现一个客户可以正常收发多条消息 
 */
public class MultiClient {
	public static void main(String[] args) throws UnknownHostException, IOException {
		System.out.println("-----Client-----");
		//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
		Socket client =new Socket("localhost",8888);
		//2、客户端发送消息
		BufferedReader console =new BufferedReader(new InputStreamReader(System.in));
		DataOutputStream dos =new DataOutputStream(client.getOutputStream());		
		DataInputStream dis =new DataInputStream(client.getInputStream());
		boolean isRunning = true;
		while(isRunning) {
			String msg = console.readLine();
			dos.writeUTF(msg);
			dos.flush();
			//3、获取消息
			msg =dis.readUTF();
			System.out.println(msg);
		}
		dos.close();
		dis.close();
		client.close();
		
	}
}

Java基础6网络编程_第34张图片
只能一个人,现在想让多个客户正常的收发消息
例3:
MultiChat.java

package com.sxt.chat02;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 在线聊天室: 服务器
 * 目标: 实现多个客户可以正常收发多条消息 
 * 问题: 其他客户必须等待之前的客户退出,才能继续    排队  
 */
public class MultiChat {
	public static void main(String[] args) throws IOException {
		System.out.println("-----Server-----");
		// 1、指定端口 使用ServerSocket创建服务器
		ServerSocket server =new ServerSocket(8888);
		// 2、阻塞式等待连接 accept
		while(true) {
			Socket  client =server.accept(); 
			System.out.println("一个客户端建立了连接");
			
			DataInputStream dis =new DataInputStream(client.getInputStream());
			DataOutputStream dos =new DataOutputStream(client.getOutputStream());		
			boolean isRunning = true;
			while(isRunning) {
				//3、接收消息
				String msg =dis.readUTF();
				//4、返回消息
				dos.writeUTF(msg);
				//释放资源
				dos.flush();
			}
			dos.close();
			dis.close();
			client.close();
		}
		
		
	}
}

MultiClient.java

package com.sxt.chat02;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 在线聊天室: 客户端
 * 目标: 实现多个客户可以正常收发多条消息 
 * 问题: 其他客户必须等待之前的客户退出,才能继续 排队
 */
public class MultiClient {
	public static void main(String[] args) throws UnknownHostException, IOException {
		System.out.println("-----Client-----");
		//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
		Socket client =new Socket("localhost",8888);
		//2、客户端发送消息
		BufferedReader console =new BufferedReader(new InputStreamReader(System.in));
		DataOutputStream dos =new DataOutputStream(client.getOutputStream());		
		DataInputStream dis =new DataInputStream(client.getInputStream());
		boolean isRunning = true;
		while(isRunning) {
			String msg = console.readLine();
			dos.writeUTF(msg);
			dos.flush();
			//3、获取消息
			msg =dis.readUTF();
			System.out.println(msg);
		}
		dos.close();
		dis.close();
		client.close();
		
	}
}

有问题,一个客户占上以后,等他退出后,下一个客户才能用
例4:加上多线程
TMultiChat.java

package com.sxt.chat02;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 在线聊天室: 服务器
 * 目标: 使用多线程实现多个客户可以正常收发多条消息 
 * 问题: 
 * 1、代码不好维护
 * 2、客户端读写没有分开 必须先写后读
 */
public class TMultiChat {
	public static void main(String[] args) throws IOException {
		System.out.println("-----Server-----");
		// 1、指定端口 使用ServerSocket创建服务器
		ServerSocket server =new ServerSocket(8888);
		// 2、阻塞式等待连接 accept
		while(true) {
			Socket  client =server.accept(); 
			System.out.println("一个客户端建立了连接");
			
			new Thread(()->{
				DataInputStream dis=null;
				DataOutputStream dos=null;
				try {
					dis = new DataInputStream(client.getInputStream());
					dos =new DataOutputStream(client.getOutputStream());
				} catch (IOException e1) {
					e1.printStackTrace();
				}					
				boolean isRunning = true;
				while(isRunning) {
					//3、接收消息
					String msg;
					try {
						msg = dis.readUTF();
						//4、返回消息
						dos.writeUTF(msg);
						//释放资源
						dos.flush();
					} catch (IOException e) {
						//e.printStackTrace();
						isRunning = false; //停止线程
					}					
				}
				try {
					if(null==dos) {
						dos.close();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
				try {
					if(null==dis) {
						dis.close();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
				try {
					if(null==client) {
						client.close();
					}
				} catch (IOException e) {
					e.printStackTrace();
				}
			}).start(); 
			
			}
		
		}

}

TMultiClient.java

package com.sxt.chat02;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 在线聊天室: 客户端
 * 目标: 使用多线程实现多个客户可以正常收发多条消息 
 */
public class TMultiClient {
	public static void main(String[] args) throws UnknownHostException, IOException {
		System.out.println("-----Client-----");
		//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
		Socket client =new Socket("localhost",8888);
		//2、客户端发送消息
		BufferedReader console =new BufferedReader(new InputStreamReader(System.in));
		DataOutputStream dos =new DataOutputStream(client.getOutputStream());		
		DataInputStream dis =new DataInputStream(client.getInputStream());
		boolean isRunning = true;
		while(isRunning) {
			String msg = console.readLine();
			dos.writeUTF(msg);
			dos.flush();
			//3、获取消息
			msg =dis.readUTF();
			System.out.println(msg);
		}
		dos.close();
		dis.close();
		client.close();
		
	}
}

Java基础6网络编程_第35张图片
多线程:使得客户和客户各自是一个线程

手写聊天室_OOP封装版

解决 1、代码不好维护
2、客户端读写没有分开 必须先写后读
封装起来
读写分开
例5:
TMultiChat.java

package com.sxt.chat03;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 在线聊天室: 服务器
 * 目标: 封装使用多线程实现多个客户可以正常收发多条消息 
 * 问题: 
 * 1、代码不好维护
 * 2、客户端读写没有分开 必须先写后读
 */
public class TMultiChat {
	public static void main(String[] args) throws IOException {
		System.out.println("-----Server-----");
		// 1、指定端口 使用ServerSocket创建服务器
		ServerSocket server =new ServerSocket(8888);
		// 2、阻塞式等待连接 accept
		while(true) {
				Socket  client =server.accept(); 
				System.out.println("一个客户端建立了连接");
				new Thread(new Channel(client)).start();			
			}		
		}
		//一个客户代表一个Channel
		static class Channel implements Runnable{
			private DataInputStream dis;
			private DataOutputStream dos;
			private Socket  client;			
			private boolean isRunning;
			public Channel(Socket  client) {
				this.client = client;
				try {
					dis = new DataInputStream(client.getInputStream());
					dos =new DataOutputStream(client.getOutputStream());
					isRunning =true;
				} catch (IOException e) {
					System.out.println("---1------");
					release();					
				}			
			}
			//接收消息
			private String receive() {
				String msg ="";
				try {
					msg =dis.readUTF();
				} catch (IOException e) {
					System.out.println("---2------");//服务器端的接收
					release();
				}
				return msg;
			}
			//发送消息
			private void send(String msg) {
				try {
					dos.writeUTF(msg);
					dos.flush();
				} catch (IOException e) {
					System.out.println("---3------");//服务器端的发送
					release();
				}
			}
			//释放资源
			private void release() {
				this.isRunning = false;
				SxtUtils.close(dis,dos,client);
			}
			@Override
			public void run() {
				while(isRunning) {
					String msg = receive() ;
					if(!msg.equals("")) {
						send(msg);
					}
				}
			}
		}
}

TMultiClient.java

package com.sxt.chat03;

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 在线聊天室: 客户端
 * 目标: 封装使用多线程实现多个客户可以正常收发多条消息 
 */
public class TMultiClient {
	public static void main(String[] args) throws UnknownHostException, IOException {
		System.out.println("-----Client-----");
		//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
		Socket client =new Socket("localhost",8888);
		//2、客户端发送消息
		new Thread(new Send(client)).start();  
		new Thread(new Receive(client)).start();
	}
}

Send.java

package com.sxt.chat03;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

/**
 * 使用多线程封装:发送端
 * 1、发送消息
 * 2、从控制台获取消息
 * 3、释放资源
 * 4、重写run
 */
public class Send implements Runnable {
	private BufferedReader console ;
	private DataOutputStream dos;
	private Socket client;
	private boolean isRunning;
	public Send(Socket client) {
		this.client =client;
		console =new BufferedReader(new InputStreamReader(System.in));
		this.isRunning = true;
		try {
			dos =new DataOutputStream(client.getOutputStream());
		} catch (IOException e) {
			System.out.println("==1==");
			this.release();
		}	
	}
	@Override
	public void run() {
		while(isRunning) {
			String msg = getStrFromConsole();
			if(!msg.equals("")) {
				send(msg);
			}
		}
	}	
	//发送消息
	private void send(String msg) {
		try {
			dos.writeUTF(msg);
			dos.flush();
		} catch (IOException e) {
			System.out.println(e);
			System.out.println("===3==");//客户端的发送
			release();
		}
	}
	/**
	 * 从控制台获取消息
	 * @return
	 */
	private String getStrFromConsole() {
		try {
			return  console.readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return "";
	}
	//释放资源
	private void release() {
		this.isRunning = false;
		SxtUtils.close(dos,client);
	}

}

Receive.java

package com.sxt.chat03;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;

/**
 * 使用多线程封装:接收端
*  1、接收消息
 * 2、释放资源
 * 3、重写run
 */
public class Receive implements Runnable {
	private DataInputStream dis ;
	private Socket client;
	private boolean isRunning;
	public Receive(Socket client) {
		this.client = client;
		this.isRunning = true;
		try {
			dis =new DataInputStream(client.getInputStream());
		} catch (IOException e) {
			System.out.println("====2=====");
			release();
		}
	}
	//接收消息
	private String receive() {
		String msg ="";
		try {
			msg =dis.readUTF();
		} catch (IOException e) {
			System.out.println("====4====");//客户端的 接收
			release();
		}
		return msg;
	}
	
	@Override
	public void run() {		
		while(isRunning) {
			String msg =receive();
			if(!msg.equals("")) {
				System.out.println(msg);
			}
		}
	}
	//释放资源
	private void release() {
		this.isRunning = false;
		SxtUtils.close(dis,client);
	}

}

SxtUtils.java

package com.sxt.chat03;

import java.io.Closeable;

/**
 * 工具类
 */
public class SxtUtils {
	/**
	 * 释放资源
	 */
	public static void close(Closeable... targets ) {
		for(Closeable target:targets) {
			try {
				if(null!=target) {
					target.close();
				}
			}catch(Exception e) {
				
			}
		}
	}
}

Java基础6网络编程_第36张图片
自己和自己聊天,各自之间不受影响
封装的魅力
任何一个客户端与服务器之间自言自语

手写聊天室_群聊过渡版

客户端发送到服务器一个消息,那么服务器就发送给所有的容器客户端
Chat.java

package com.sxt.chat04;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 在线聊天室: 服务器
 * 目标: 加入容器实现群聊
 */
public class Chat {
	private static CopyOnWriteArrayList all =new CopyOnWriteArrayList();
	//一边写一边遍历
	public static void main(String[] args) throws IOException {
		System.out.println("-----Server-----");
		// 1、指定端口 使用ServerSocket创建服务器
		ServerSocket server =new ServerSocket(8888);
		// 2、阻塞式等待连接 accept
		while(true) {//多个 客户端
				Socket  client =server.accept(); 
				System.out.println("一个客户端建立了连接");
				Channel c =new Channel(client);//来一个客户端我们就丢一个到容器
				all.add(c); //管理所有的成员
				new Thread(c).start();			
			}		
		}
		//一个客户代表一个Channel
		static class Channel implements Runnable{//之前都是只有发给自己的方法,现在把补上发给其他人的方法
			private DataInputStream dis;
			private DataOutputStream dos;
			private Socket  client;			
			private boolean isRunning;
			private String name;
			public Channel(Socket  client) {
				this.client = client;
				try {
					dis = new DataInputStream(client.getInputStream());
					dos =new DataOutputStream(client.getOutputStream());
					isRunning =true;
					//获取名称
					this.name =receive();
					//欢迎你的到来
					this.send("欢迎你的到来");
					sendOthers(this.name+"来了shsxt聊天室",true);
				} catch (IOException e) {
					System.out.println("---1------");
					release();					
				}			
			}
			//接收消息
			private String receive() {
				String msg ="";
				try {
					msg =dis.readUTF();
				} catch (IOException e) {
					System.out.println("---2------");
					release();
				}
				return msg;
			}
			//发送消息
			private void send(String msg) {
				try {
					dos.writeUTF(msg);
					dos.flush();
				} catch (IOException e) {
					System.out.println("---3------");
					release();
				}
			}
			/**
			 * 群聊:获取自己的消息,发给其他人
			 * @param msg
			 */
			private void sendOthers(String msg,boolean isSys) {//调用其他人的send
				for(Channel other: all) {
					if(other==this) { //自己
						continue;
					}
					if(!isSys) {
						other.send(this.name +"对所有人说:"+msg);//群聊消息
					}else {
						other.send(msg); //系统消息
					}
				}
			}
			//释放资源
			private void release() {
				this.isRunning = false;
				SxtUtils.close(dis,dos,client);
				//退出
				all.remove(this);
				sendOthers(this.name+"离开大家庭...",true);
			}
			@Override
			public void run() {
				while(isRunning) {
					String msg = receive() ;
					if(!msg.equals("")) {
						//send(msg);
						sendOthers(msg,false);
					}
				}
			}
		}
}

Client.java

package com.sxt.chat04;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 在线聊天室: 客户端
 * 目标: 加入容器实现群聊
 */
public class Client {
	public static void main(String[] args) throws UnknownHostException, IOException {
		System.out.println("-----Client-----");
		BufferedReader br =new BufferedReader(new InputStreamReader(System.in));
		System.out.println("请输入用户名:");
		String name =br.readLine();
		//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
		Socket client =new Socket("localhost",8888);
		//2、客户端发送消息
		new Thread(new Send(client,name)).start();  
		new Thread(new Receive(client)).start();
	}
}

Send.java

package com.sxt.chat04;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

/**
 * 使用多线程封装:发送端
 * 1、发送消息
 * 2、从控制台获取消息
 * 3、释放资源
 * 4、重写run
 */
public class Send implements Runnable {
	private BufferedReader console ;
	private DataOutputStream dos;
	private Socket client;
	private boolean isRunning;
	private String name;
	public Send(Socket client,String name) {
		this.client =client;
		console =new BufferedReader(new InputStreamReader(System.in));
		this.isRunning = true;
		this.name = name;
		try {
			dos =new DataOutputStream(client.getOutputStream());
			//发送名称
			send(name);
		} catch (IOException e) {
			System.out.println("==1==");
			this.release();
		}	
	}
	@Override
	public void run() {
		while(isRunning) {
			String msg = getStrFromConsole();
			if(!msg.equals("")) {
				send(msg);
			}
		}
	}	
	//发送消息
	private void send(String msg) {
		try {
			dos.writeUTF(msg);
			dos.flush();
		} catch (IOException e) {
			System.out.println(e);
			System.out.println("===3==");
			release();
		}
	}
	/**
	 * 从控制台获取消息
	 * @return
	 */
	private String getStrFromConsole() {
		try {
			return  console.readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return "";
	}
	//释放资源
	private void release() {
		this.isRunning = false;
		SxtUtils.close(dos,client);
	}

}

Receive.java

package com.sxt.chat03;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;

/**
 * 使用多线程封装:接收端
*  1、接收消息
 * 2、释放资源
 * 3、重写run
 */
public class Receive implements Runnable {
	private DataInputStream dis ;
	private Socket client;
	private boolean isRunning;
	public Receive(Socket client) {
		this.client = client;
		this.isRunning = true;
		try {
			dis =new DataInputStream(client.getInputStream());
		} catch (IOException e) {
			System.out.println("====2=====");
			release();
		}
	}
	//接收消息
	private String receive() {
		String msg ="";
		try {
			msg =dis.readUTF();
		} catch (IOException e) {
			System.out.println("====4====");//客户端的 接收
			release();
		}
		return msg;
	}
	
	@Override
	public void run() {		
		while(isRunning) {
			String msg =receive();
			if(!msg.equals("")) {
				System.out.println(msg);
			}
		}
	}
	//释放资源
	private void release() {
		this.isRunning = false;
		SxtUtils.close(dis,client);
	}

}

SxtUtils.java

package com.sxt.chat03;

import java.io.Closeable;

/**
 * 工具类
 */
public class SxtUtils {
	/**
	 * 释放资源
	 */
	public static void close(Closeable... targets ) {
		for(Closeable target:targets) {
			try {
				if(null!=target) {
					target.close();
				}
			}catch(Exception e) {
				
			}
		}
	}
}

Java基础6网络编程_第37张图片

Java基础6网络编程_第38张图片
输入各自客户端的名字以后,才建立连接
Java基础6网络编程_第39张图片
Java基础6网络编程_第40张图片

Java基础6网络编程_第41张图片

Java基础6网络编程_第42张图片

手写聊天室_私聊终极版

Chat.java

package com.sxt.chat05;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 在线聊天室: 服务器
 * 目标: 私聊
 */
public class Chat {
	private static CopyOnWriteArrayList all =new CopyOnWriteArrayList();
	public static void main(String[] args) throws IOException {
		System.out.println("-----Server-----");
		// 1、指定端口 使用ServerSocket创建服务器
		ServerSocket server =new ServerSocket(8888);
		// 2、阻塞式等待连接 accept
		while(true) {
				Socket  client =server.accept(); 
				System.out.println("一个客户端建立了连接");
				Channel c =new Channel(client);
				all.add(c); //管理所有的成员
				new Thread(c).start();			
			}		
		}
		//一个客户代表一个Channel
		static class Channel implements Runnable{
			private DataInputStream dis;
			private DataOutputStream dos;
			private Socket  client;			
			private boolean isRunning;
			private String name;
			public Channel(Socket  client) {
				this.client = client;
				try {
					dis = new DataInputStream(client.getInputStream());
					dos =new DataOutputStream(client.getOutputStream());
					isRunning =true;
					//获取名称
					this.name =receive();
					//欢迎你的到来
					this.send("欢迎你的到来");
					sendOthers(this.name+"来了shsxt聊天室",true);
				} catch (IOException e) {
					System.out.println("---1------");
					release();					
				}			
			}
			//接收消息
			private String receive() {
				String msg ="";
				try {
					msg =dis.readUTF();
				} catch (IOException e) {
					System.out.println("---2------");
					release();
				}
				return msg;
			}
			//发送消息
			private void send(String msg) {
				try {
					dos.writeUTF(msg);
					dos.flush();
				} catch (IOException e) {
					System.out.println("---3------");
					release();
				}
			}
			/**
			 * 群聊:获取自己的消息,发给其他人
			 * 私聊: 约定数据格式: @xxx:msg
			 * @param msg
			 */
			private void sendOthers(String msg,boolean isSys) {
				boolean isPrivate = msg.startsWith("@");
				if(isPrivate) { //私聊
					int idx =msg.indexOf(":");
					//获取目标和数据
					String targetName = msg.substring(1,idx);
					msg = msg.substring(idx+1);
					for(Channel other: all) {
						if(other.name.equals(targetName)) {//目标
							other.send(this.name +"悄悄地对您说:"+msg);
							break;
						}
					}
				}else {				
					for(Channel other: all) {
						if(other==this) { //自己
							continue;
						}
						if(!isSys) {
							other.send(this.name +"对所有人说:"+msg);//群聊消息
						}else {
							other.send(msg); //系统消息
						}
					}
				}
			}
			//释放资源
			private void release() {
				this.isRunning = false;
				SxtUtils.close(dis,dos,client);
				//退出
				all.remove(this);
				sendOthers(this.name+"离开大家庭...",true);
			}
			@Override
			public void run() {
				while(isRunning) {
					String msg = receive() ;
					if(!msg.equals("")) {
						//send(msg);
						sendOthers(msg,false);
					}
				}
			}
		}
}

Client.java

package com.sxt.chat05;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 在线聊天室: 客户端
 * 目标: 私聊
 */
public class Client {
	public static void main(String[] args) throws UnknownHostException, IOException {
		System.out.println("-----Client-----");
		BufferedReader br =new BufferedReader(new InputStreamReader(System.in));
		System.out.println("请输入用户名:");
		String name =br.readLine();
		//1、建立连接: 使用Socket创建客户端 +服务的地址和端口
		Socket client =new Socket("localhost",8888);
		//2、客户端发送消息
		new Thread(new Send(client,name)).start();  
		new Thread(new Receive(client)).start();
	}
}

Receive.java

package com.sxt.chat05;

import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;

/**
 * 使用多线程封装:接收端
*  1、接收消息
 * 2、释放资源
 * 3、重写run
 */
public class Receive implements Runnable {
	private DataInputStream dis ;
	private Socket client;
	private boolean isRunning;
	public Receive(Socket client) {
		this.client = client;
		this.isRunning = true;
		try {
			dis =new DataInputStream(client.getInputStream());
		} catch (IOException e) {
			System.out.println("====2=====");
			release();
		}
	}
	//接收消息
	private String receive() {
		String msg ="";
		try {
			msg =dis.readUTF();
		} catch (IOException e) {
			System.out.println("====4====");
			release();
		}
		return msg;
	}
	
	@Override
	public void run() {		
		while(isRunning) {
			String msg =receive();
			if(!msg.equals("")) {
				System.out.println(msg);
			}
		}
	}
	//释放资源
	private void release() {
		this.isRunning = false;
		SxtUtils.close(dis,client);
	}

}

Send.java

package com.sxt.chat05;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

/**
 * 使用多线程封装:发送端
 * 1、发送消息
 * 2、从控制台获取消息
 * 3、释放资源
 * 4、重写run
 */
public class Send implements Runnable {
	private BufferedReader console ;
	private DataOutputStream dos;
	private Socket client;
	private boolean isRunning;
	private String name;
	public Send(Socket client,String name) {
		this.client =client;
		console =new BufferedReader(new InputStreamReader(System.in));
		this.isRunning = true;
		this.name = name;
		try {
			dos =new DataOutputStream(client.getOutputStream());
			//发送名称
			send(name);
		} catch (IOException e) {
			System.out.println("==1==");
			this.release();
		}	
	}
	@Override
	public void run() {
		while(isRunning) {
			String msg = getStrFromConsole();
			if(!msg.equals("")) {
				send(msg);
			}
		}
	}	
	//发送消息
	private void send(String msg) {
		try {
			dos.writeUTF(msg);
			dos.flush();
		} catch (IOException e) {
			System.out.println(e);
			System.out.println("===3==");
			release();
		}
	}
	/**
	 * 从控制台获取消息
	 * @return
	 */
	private String getStrFromConsole() {
		try {
			return  console.readLine();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return "";
	}
	//释放资源
	private void release() {
		this.isRunning = false;
		SxtUtils.close(dos,client);
	}

}

SxtUtils.java

package com.sxt.chat05;

import java.io.Closeable;

/**
 * 工具类
 * 
 */
public class SxtUtils {
	/**
	 * 释放资源
	 */
	public static void close(Closeable... targets ) {
		for(Closeable target:targets) {
			try {
				if(null!=target) {
					target.close();
				}
			}catch(Exception e) {
				
			}
		}
	}
}

Java基础6网络编程_第43张图片

Java基础6网络编程_第44张图片

12.2.3 UDP协议

基于TCP协议可以建立稳定连接的点对点的通信。这种通信方式实时、快速、安全性高,但是很占用系统的资源。

在网络传输方式上,还有另一种基于UDP协议的通信方式,称为数据报通信方式。在这种方式中,每个数据发送单元被统一封装成数据报包的方式,发送方将数据报包发送到网络中,数据报包在网络中去寻找它的目的地。

UDP基本步骤

Java基础6网络编程_第45张图片
例1:
UdpClient.java

package com.sxt.udp;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;

/**
 * 基本流程: 发送端
 * 1、使用DatagramSocket  指定端口 创建发送端
 * 2、准备数据 一定转成字节数组
 * 3、 封装成DatagramPacket 包裹,需要指定目的地
 * 4、发送包裹send​(DatagramPacket p) * 
 * 5、释放资源
 */
public class UdpClient {

	public static void main(String[] args) throws Exception {
		System.out.println("发送方启动中.....");
		 // 1、使用DatagramSocket  指定端口 创建发送端
		DatagramSocket client =new DatagramSocket(8888);
		 //2、准备数据 一定转成字节数组
		String data = "上海尚学堂";
		byte[] datas = data.getBytes();
		 //3、 封装成DatagramPacket 包裹,需要指定目的地
		DatagramPacket packet =new DatagramPacket(datas,0,datas.length,
				new InetSocketAddress("localhost",6666));
		//4、发送包裹send​(DatagramPacket p) * 
		client.send(packet);
		// 5、释放资源
		client.close();
	}

}

UdpServer.java

package com.sxt.udp;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * 基本流程: 接收端
 * Address already in use: Cannot bind  同一个协议下端口不允许冲突
 * 1、使用DatagramSocket  指定端口 创建接收端
 * 2、准备容器 封装成DatagramPacket 包裹
 * 3、阻塞式接收包裹receive​(DatagramPacket p)
 * 4、分析数据
 *    byte[]  getData​()
 *                getLength​()
 * 5、释放资源
 */
public class UdpServer {

	public static void main(String[] args) throws Exception {
		System.out.println("接收方启动中.....");
		// 1、使用DatagramSocket  指定端口 创建接收端
		DatagramSocket server =new DatagramSocket(6666);
		// 2、准备容器 封装成DatagramPacket 包裹
		byte[] container =new byte[1024*60];
		DatagramPacket packet = new DatagramPacket(container,0,container.length);
		// 3、阻塞式接收包裹receive​(DatagramPacket p)
		server.receive(packet); //阻塞式
		// 4、分析数据
		//    byte[]  getData​()
		//                getLength​()
		 byte[]  datas =packet.getData();
		 int len = packet.getLength();
		 System.out.println(new String(datas,0,len));
		 
		// 5、释放资源
		 server.close();
	}

}

UDP上传文件

例2:
UdpTypeClient.java

package com.sxt.udp;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;

/**
 * 基本类型: 发送端
 * 1、使用DatagramSocket  指定端口 创建发送端
 * 2、将基本类型  转成字节数组
 * 3、 封装成DatagramPacket 包裹,需要指定目的地
 * 4、发送包裹send​(DatagramPacket p) * 
 * 5、释放资源
 */
public class UdpTypeClient {

	public static void main(String[] args) throws Exception {
		System.out.println("发送方启动中.....");
		 // 1、使用DatagramSocket  指定端口 创建发送端
		DatagramSocket client =new DatagramSocket(8888);
		 //2、准备数据 一定转成字节数组
		//写出
		ByteArrayOutputStream baos =new ByteArrayOutputStream();
		DataOutputStream dos =new DataOutputStream(new BufferedOutputStream(baos));
		//操作数据类型 +数据
		dos.writeUTF("编码辛酸泪");
		dos.writeInt(18);
		dos.writeBoolean(false);
		dos.writeChar('a');
		dos.flush();
		byte[] datas =baos.toByteArray();	
		 //3、 封装成DatagramPacket 包裹,需要指定目的地
		DatagramPacket packet =new DatagramPacket(datas,0,datas.length,
				new InetSocketAddress("localhost",6666));
		//4、发送包裹send​(DatagramPacket p) * 
		client.send(packet);
		// 5、释放资源
		client.close();
	}

}

UdpTypeServer.java

package com.sxt.udp;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * 基本类型: 接收端
 * Address already in use: Cannot bind  同一个协议下端口不允许冲突
 * 1、使用DatagramSocket  指定端口 创建接收端
 * 2、准备容器 封装成DatagramPacket 包裹
 * 3、阻塞式接收包裹receive​(DatagramPacket p)
 * 4、分析数据    将字节数组还原为对应的类型
 *    byte[]  getData​()
 *                getLength​()
 * 5、释放资源
 */
public class UdpTypeServer {

	public static void main(String[] args) throws Exception {
		System.out.println("接收方启动中.....");
		// 1、使用DatagramSocket  指定端口 创建接收端
		DatagramSocket server =new DatagramSocket(6666);
		// 2、准备容器 封装成DatagramPacket 包裹
		byte[] container =new byte[1024*60];
		DatagramPacket packet = new DatagramPacket(container,0,container.length);
		// 3、阻塞式接收包裹receive​(DatagramPacket p)
		server.receive(packet); //阻塞式
		// 4、分析数据    将字节数组还原为对应的类型
		//    byte[]  getData​()
		//                getLength​()
		 byte[]  datas =packet.getData();
		 int len = packet.getLength();		 
		DataInputStream dis =new DataInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));
		//顺序与写出一致
		String msg = dis.readUTF(); 
		int age = dis.readInt();
		boolean flag = dis.readBoolean();
		char ch = dis.readChar();
		System.out.println(msg+"-->"+flag);
		// 5、释放资源
		 server.close();
	}

}

Java基础6网络编程_第46张图片

例3:
Employee.java

package com.sxt.udp;
//javabean 封装数据
public class Employee implements java.io.Serializable{
	private transient String name; //该数据不需要序列化
	private double salary;
	public Employee() {
	}
	public Employee(String name, double salary) {
		this.name = name;
		this.salary = salary;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public double getSalary() {
		return salary;
	}
	public void setSalary(double salary) {
		this.salary = salary;
	}
	
}

UdpObjClient.java

package com.sxt.udp;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.util.Date;

/**
 *  引用类型: 发送端
 * 1、使用DatagramSocket  指定端口 创建发送端
 * 2、将基本类型  转成字节数组
 * 3、 封装成DatagramPacket 包裹,需要指定目的地
 * 4、发送包裹send​(DatagramPacket p) * 
 * 5、释放资源
 */
public class UdpObjClient {

	public static void main(String[] args) throws Exception {
		System.out.println("发送方启动中.....");
		 // 1、使用DatagramSocket  指定端口 创建发送端
		DatagramSocket client =new DatagramSocket(8888);
		 //2、准备数据 一定转成字节数组
		//写出
		ByteArrayOutputStream baos =new ByteArrayOutputStream();
		ObjectOutputStream oos =new ObjectOutputStream(new BufferedOutputStream(baos));
		//操作数据类型 +数据
		oos.writeUTF("编码辛酸泪");
		oos.writeInt(18);
		oos.writeBoolean(false);
		oos.writeChar('a');
		//对象
		oos.writeObject("谁解其中味");
		oos.writeObject(new Date());
		Employee emp =new Employee("马云",400);
		oos.writeObject(emp);
		oos.flush();
		byte[] datas =baos.toByteArray();	
		
		 //3、 封装成DatagramPacket 包裹,需要指定目的地
		DatagramPacket packet =new DatagramPacket(datas,0,datas.length,
				new InetSocketAddress("localhost",6666));
		//4、发送包裹send​(DatagramPacket p) * 
		client.send(packet);
		// 5、释放资源
		client.close();
	}

}

UdpObjServer.java

package com.sxt.udp;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.Date;

/**
 * 引用类型: 接收端
 * Address already in use: Cannot bind  同一个协议下端口不允许冲突
 * 1、使用DatagramSocket  指定端口 创建接收端
 * 2、准备容器 封装成DatagramPacket 包裹
 * 3、阻塞式接收包裹receive​(DatagramPacket p)
 * 4、分析数据    将字节数组还原为对应的类型
 *    byte[]  getData​()
 *                getLength​()
 * 5、释放资源
 */
public class UdpObjServer {

	public static void main(String[] args) throws Exception {
		System.out.println("接收方启动中.....");
		// 1、使用DatagramSocket  指定端口 创建接收端
		DatagramSocket server =new DatagramSocket(6666);
		// 2、准备容器 封装成DatagramPacket 包裹
		byte[] container =new byte[1024*60];
		DatagramPacket packet = new DatagramPacket(container,0,container.length);
		// 3、阻塞式接收包裹receive​(DatagramPacket p)
		server.receive(packet); //阻塞式
		// 4、分析数据    将字节数组还原为对应的类型
		//    byte[]  getData​()
		//                getLength​()
		 byte[]  datas =packet.getData();
		 int len = packet.getLength();		 
		//读取 -->反序列化
		ObjectInputStream ois =new ObjectInputStream(new BufferedInputStream(new ByteArrayInputStream(datas)));
		//顺序与写出一致
		String msg = ois.readUTF(); 
		int age = ois.readInt();
		boolean flag = ois.readBoolean();
		char ch = ois.readChar();
		System.out.println(flag);
		//对象的数据还原  
		Object str = ois.readObject();
		Object date = ois.readObject();
		Object employee = ois.readObject();
		
		if(str instanceof String) {
			String strObj = (String) str;
			System.out.println(strObj);
		}
		if(date instanceof Date) {
			Date dateObj = (Date) date;
			System.out.println(dateObj);
		}
		if(employee instanceof Employee) {
			Employee empObj = (Employee) employee;
			System.out.println(empObj.getName()+"-->"+empObj.getSalary());
		}
		 
		 
		// 5、释放资源
		 server.close();
	}

}

Java基础6网络编程_第47张图片

例4:
IOUtils.java

package com.sxt.udp;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 *1、 图片读取到字节数组
 *2、 字节数组写出到文件
 *  @author 裴新
 *
 */
public class IOUtils {
	/**
	 * 1、图片读取到字节数组
	 * 1)、图片到程序  FileInputStream
	 * 2)、程序到字节数组	ByteArrayOutputStream
	 */
	public static byte[] fileToByteArray(String filePath) {
		//1、创建源与目的地
		File src = new File(filePath);
		byte[] dest =null;
		//2、选择流
		InputStream  is =null;
		ByteArrayOutputStream baos =null;
		try {
			is =new FileInputStream(src);
			baos = new ByteArrayOutputStream();
			//3、操作 (分段读取)
			byte[] flush = new byte[1024*10]; //缓冲容器
			int len = -1; //接收长度
			while((len=is.read(flush))!=-1) {
				baos.write(flush,0,len);		 //写出到字节数组中			
			}		
			baos.flush();
			return baos.toByteArray();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			//4、释放资源
			try {
				if(null!=is) {
					is.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return null;		
	}
	/**
	 * 2、字节数组写出到图片
	 * 1)、字节数组到程序 ByteArrayInputStream
	 * 2)、程序到文件 FileOutputStream
	 */
	public static void byteArrayToFile(byte[] src,String filePath) {
		//1、创建源
		File dest = new File(filePath);
		//2、选择流
		InputStream  is =null;
		OutputStream os =null;
		try {
			is =new ByteArrayInputStream(src);
			os = new FileOutputStream(dest);
			//3、操作 (分段读取)
			byte[] flush = new byte[5]; //缓冲容器
			int len = -1; //接收长度
			while((len=is.read(flush))!=-1) {
				os.write(flush,0,len);			//写出到文件	
			}		
			os.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}finally {
			//4、释放资源
			try {
				if (null != os) {
					os.close();
				} 
			} catch (Exception e) {
			}
		}
	}
}

UdpFileClient.java

package com.sxt.udp;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;

/**
 *  文件上传: 发送端
 * 1、使用DatagramSocket  指定端口 创建发送端
 * 2、将基本类型  转成字节数组
 * 3、 封装成DatagramPacket 包裹,需要指定目的地
 * 4、发送包裹send​(DatagramPacket p) * 
 * 5、释放资源
 */
public class UdpFileClient {

	public static void main(String[] args) throws Exception {
		System.out.println("发送方启动中.....");
		 // 1、使用DatagramSocket  指定端口 创建发送端
		DatagramSocket client =new DatagramSocket(8888);
		 //2、准备数据 一定转成字节数组
		byte[] datas =IOUtils.fileToByteArray("src/logo.png");		
		 //3、 封装成DatagramPacket 包裹,需要指定目的地
		DatagramPacket packet =new DatagramPacket(datas,0,datas.length,
				new InetSocketAddress("localhost",6666));
		//4、发送包裹send​(DatagramPacket p) * 
		client.send(packet);
		// 5、释放资源
		client.close();
	}

}

UdpFileServer.java

package com.sxt.udp;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.util.Date;

/**
 * 文件存储: 接收端
 * Address already in use: Cannot bind  同一个协议下端口不允许冲突
 * 1、使用DatagramSocket  指定端口 创建接收端
 * 2、准备容器 封装成DatagramPacket 包裹
 * 3、阻塞式接收包裹receive​(DatagramPacket p)
 * 4、分析数据    将字节数组还原为对应的类型
 *    byte[]  getData​()
 *                getLength​()
 * 5、释放资源
 */
public class UdpFileServer {

	public static void main(String[] args) throws Exception {
		System.out.println("接收方启动中.....");
		// 1、使用DatagramSocket  指定端口 创建接收端
		DatagramSocket server =new DatagramSocket(6666);
		// 2、准备容器 封装成DatagramPacket 包裹
		byte[] container =new byte[1024*60];
		DatagramPacket packet = new DatagramPacket(container,0,container.length);
		// 3、阻塞式接收包裹receive​(DatagramPacket p)
		server.receive(packet); //阻塞式
		// 4、分析数据    将字节数组还原为对应的类型
		//    byte[]  getData​()
		//                getLength​()
		 byte[]  datas =packet.getData();
		 int len = packet.getLength();		 
		 IOUtils.byteArrayToFile(datas, "src/copy.png");		 
		// 5、释放资源
		 server.close();
	}
}

UDP案例在线咨询

Java基础6网络编程_第48张图片
可以多次交流,加入多线程
例5:
UdpTalkClient.java

package com.sxt.udp;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;

/**
 * 多次交流: 发送端
 * 1、使用DatagramSocket  指定端口 创建发送端
 * 2、准备数据 一定转成字节数组
 * 3、 封装成DatagramPacket 包裹,需要指定目的地
 * 4、发送包裹send​(DatagramPacket p) * 
 * 5、释放资源
 */
public class UdpTalkClient {

	public static void main(String[] args) throws Exception {
		System.out.println("发送方启动中.....");
		 // 1、使用DatagramSocket  指定端口 创建发送端
		DatagramSocket client =new DatagramSocket(8888);
		 //2、准备数据 一定转成字节数组
		BufferedReader reader =new BufferedReader(new InputStreamReader(System.in));//控制台输入
		while(true) {
			String data = reader.readLine();
			byte[] datas = data.getBytes();
			 //3、 封装成DatagramPacket 包裹,需要指定目的地
			DatagramPacket packet =new DatagramPacket(datas,0,datas.length,
					new InetSocketAddress("localhost",6666));
			//4、发送包裹send​(DatagramPacket p) * 
			client.send(packet);
			if(data.equals("bye")) {
				break;
			}
		}
		// 5、释放资源
		client.close();
	}

}

UdpTalkServer.java

package com.sxt.udp;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * 多次交流: 接收端
 * Address already in use: Cannot bind  同一个协议下端口不允许冲突
 * 1、使用DatagramSocket  指定端口 创建接收端
 * 2、准备容器 封装成DatagramPacket 包裹
 * 3、阻塞式接收包裹receive​(DatagramPacket p)
 * 4、分析数据
 *    byte[]  getData​()
 *                getLength​()
 * 5、释放资源
 */
public class UdpTalkServer {

	public static void main(String[] args) throws Exception {
		System.out.println("接收方启动中.....");
		// 1、使用DatagramSocket  指定端口 创建接收端
		DatagramSocket server =new DatagramSocket(6666);
		
		while(true) {
			// 2、准备容器 封装成DatagramPacket 包裹
			byte[] container =new byte[1024*60];
			DatagramPacket packet = new DatagramPacket(container,0,container.length);
			// 3、阻塞式接收包裹receive​(DatagramPacket p)
			server.receive(packet); //阻塞式
			// 4、分析数据
			//    byte[]  getData​()
			//                getLength​()
			 byte[]  datas =packet.getData();
			 int len = packet.getLength();
			 String data=new String(datas,0,len);
			 System.out.println(data);
			 if(data.equals("bye")) {
				 break;
			 }
		}
		// 5、释放资源
		 server.close();
	}

}

例6:
加入多线程
TalkSend.java

package com.sxt.udp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketException;

/**
 * 发送端: 使用面向对象封装
 */
public class TalkSend implements Runnable {
	private DatagramSocket client ;
	private BufferedReader reader;
	private String toIP ;//对方的IP
	private int toPort ;//对方的端口
	public TalkSend(int port,String toIP,int toPort) {
		this.toIP = toIP;
		this.toPort=toPort;
		try {
			client=new DatagramSocket(port);
			reader =new BufferedReader(new InputStreamReader(System.in));
		} catch (SocketException e) {
			e.printStackTrace();
		}
	}
	@Override
	public void run() {
		while(true) {
			String data;
			try {
				data = reader.readLine();
				byte[] datas = data.getBytes();
				 //3、 封装成DatagramPacket 包裹,需要指定目的地
				DatagramPacket packet =new DatagramPacket(datas,0,datas.length,
						new InetSocketAddress(this.toIP,this.toPort));
				//4、发送包裹send​(DatagramPacket p) * 
				client.send(packet);
				if(data.equals("bye")) {
					break;
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
			
		}
		// 5、释放资源
		client.close();
	}

}

TalkReceive.java

package com.sxt.udp;

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

/**
 * 接收端: 使用面向对象封装
 */
public class TalkReceive implements Runnable {
	private DatagramSocket server ;
	private String from ;
	public TalkReceive(int port,String from) {
		this.from = from ;
		try {
			server=new DatagramSocket(port);
		} catch (SocketException e) {
			e.printStackTrace();
		}
	}
	@Override
	public void run() {
		while(true) {
			// 2、准备容器 封装成DatagramPacket 包裹
			byte[] container =new byte[1024*60];
			DatagramPacket packet = new DatagramPacket(container,0,container.length);
			// 3、阻塞式接收包裹receive​(DatagramPacket p)
			try {
				server.receive(packet);//阻塞式
				// 4、分析数据
				 byte[]  datas =packet.getData();
				 int len = packet.getLength();
				 String data=new String(datas,0,len);
				 System.out.println(from+":"+data);
				 if(data.equals("bye")) {
					 break;
				 }
			} catch (IOException e) {
				e.printStackTrace();
			} 
		}
		// 5、释放资源
		 server.close();
	}

}

双向交流
TalkStudent.java

package com.sxt.udp;
/**
 * 加入多线程,实现双向交流 模拟在线咨询
 * 
 * @author 裴新 QQ:3401997271
 *
 */
public class TalkStudent {
	public static void main(String[] args) {
		new Thread(new TalkSend(7777,"localhost",9999)).start(); //发送		
		new Thread(new TalkReceive(8888,"老师")).start(); //接收
	}
}

TalkTeacher.java

package com.sxt.udp;
/**
 * 加入多线程,实现双向交流 模拟在线咨询
 */
public class TalkTeacher {
	public static void main(String[] args) {
		new Thread(new TalkReceive(9999,"学生")).start(); //接收
		
		new Thread(new TalkSend(5555,"localhost",8888)).start(); //发送
	}
}

Java基础6网络编程_第49张图片
Java基础6网络编程_第50张图片
Java基础6网络编程_第51张图片

12.3 Java网络编程

Java为了可移植性,不允许直接调用操作系统,而是由java.net包来提供网络功能。Java虚拟机负责提供与操作系统的实际连接。下面我们来介绍几个java.net包中的常用的类。

12.3.1 InetAddress

作用:封装计算机的IP地址和DNS(没有端口信息)。
注:DNS是Domain Name System,域名系统。
特点:这个类没有构造方法。如果要得到对象,只能通过静态方法:getLocalHost()、getByName()、 getAllByName()、 getAddress()、getHostName()。

【示例12-1】使用getLocalHost方法创建InetAddress对象

import java.net.InetAddress;
import java.net.UnknownHostException;
public class Test1 {
    public static void main(String[] args) throws UnknownHostException {
        InetAddress addr = InetAddress.getLocalHost();
        //返回IP地址:192.168.1.110
        System.out.println(addr.getHostAddress()); 
        //输出计算机名:gaoqi
        System.out.println(addr.getHostName());     
    }
}

【示例12-2】根据域名得到InetAddress对象

import java.net.InetAddress;
import java.net.UnknownHostException;
public class Test2 {
    public static void main(String[] args) throws UnknownHostException {
        InetAddress addr = InetAddress.getByName("www.sxt.cn");
        // 返回 sxt服务器的IP:59.110.14.7
        System.out.println(addr.getHostAddress());
        // 输出:www.sxt.cn
        System.out.println(addr.getHostName());
    }
}

【示例12-3】根据IP得到InetAddress对象

import java.net.InetAddress;
import java.net.UnknownHostException;
public class Test3 {
    public static void main(String[] args) throws UnknownHostException {
        InetAddress addr = InetAddress.getByName("59.110.14.7");
        // 返回sxt服务器的IP:59.110.14.7
        System.out.println(addr.getHostAddress());
        /*
         * 输出ip而不是域名。如果这个IP地址不存在或DNS服务器不允许进行IP地址
         * 和域名的映射,getHostName方法就直接返回这个IP地址。
         */
        System.out.println(addr.getHostName());
    }
}

12.3.2 InetSocketAddress

作用:包含IP和端口信息,常用于Socket通信。此类实现 IP 套接字地址(IP 地址 + 端口号),不依赖任何协议。

【示例12-4】InetSocketAddress的使用

import java.net.InetSocketAddress;
public class Test4 {
    public static void main(String[] args) {
        InetSocketAddress socketAddress = new InetSocketAddress("127.0.0.1", 8080);
        InetSocketAddress socketAddress2 = new InetSocketAddress("localhost", 9000);
        System.out.println(socketAddress.getHostName());
        System.out.println(socketAddress2.getAddress());
    }
}

12.3.3 URL类

IP地址唯一标识了Internet上的计算机,而URL则标识了这些计算机上的资源。类 URL 代表一个统一资源定位符,它是指向互联网“资源”的指针。资源可以是简单的文件或目录,也可以是对更为复杂的对象的引用,例如对数据库或搜索引擎的查询。

为了方便程序员编程,JDK中提供了URL类,该类的全名是java.net.URL,有了这样一个类,就可以使用它的各种方法来对URL对象进行分割、合并等处理。

【示例12-5】URL类的使用

import java.net.MalformedURLException;
import java.net.URL;
public class Test5 {
    public static void main(String[] args) throws MalformedURLException {
        URL u = new URL("http://www.google.cn:80/webhp#aa?canhu=33");
        System.out.println("获取与此url关联的协议的默认端口:" + u.getDefaultPort());
        System.out.println("getFile:" + u.getFile()); // 端口号后面的内容
        System.out.println("主机名:" + u.getHost()); // www.google.cn
        System.out.println("路径:" + u.getPath()); // 端口号后,参数前的内容
        // 如果www.google.cn:80则返回80.否则返回-1
        System.out.println("端口:" + u.getPort()); 
        System.out.println("协议:" + u.getProtocol());
        System.out.println("参数部分:" + u.getQuery());
        System.out.println("锚点:" + u.getRef());
 
        URL u1 = new URL("http://www.abc.com/aa/");
        URL u2 = new URL(u, "2.html"); // 相对路径构建url对象
        System.out.println(u2.toString()); // http://www.abc.com/aa/2.html
    }
}

【示例12-6】最简单的网络爬虫

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
 
public class Test6 {
    public static void main(String[] args) {
        basicSpider();
    }
    //网络爬虫
    static void basicSpider() {
        URL url = null;
        InputStream is = null;
        BufferedReader br = null;
        StringBuilder sb = new StringBuilder();
        String temp = "";
        try {
            url = new URL("http://www.baidu.com");
            is = url.openStream();
            br = new BufferedReader(new InputStreamReader(is));
            /* 
             * 这样就可以将网络内容下载到本地机器。
             * 然后进行数据分析,建立索引。这也是搜索引擎的第一步。
             */
            while ((temp = br.readLine()) != null) {
                sb.append(temp);
            }
            System.out.println(sb);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

12.3.4 基于TCP协议的Socket编程和通信

在网络通讯中,第一次主动发起通讯的程序被称作客户端(Client)程序,简称客户端,而在第一次通讯中等待连接的程序被称作服务器端(Server)程序,简称服务器。一旦通讯建立,则客户端和服务器端完全一样,没有本质的区别。

“请求-响应”模式:
1.Socket类:发送TCP消息。
2.ServerSocket类:创建服务器。

套接字是一种进程间的数据交换机制。这些进程既可以在同一机器上,也可以在通过网络连接的不同机器上。换句话说,套接字起到通信端点的作用。单个套接字是一个端点,而一对套接字则构成一个双向通信信道,使非关联进程可以在本地或通过网络进行数据交换。一旦建立套接字连接,数据即可在相同或不同的系统中双向或单向发送,直到其中一个端点关闭连接。套接字与主机地址和端口地址相关联。主机地址就是客户端或服务器程序所在的主机的IP地址。端口地址是指客户端或服务器程序使用的主机的通信端口。

在客户端和服务器中,分别创建独立的Socket,并通过Socket的属性,将两个Socket进行连接,这样,客户端和服务器通过套接字所建立的连接使用输入输出流进行通信。

TCP/IP套接字是最可靠的双向流协议,使用TCP/IP可以发送任意数量的数据。

实际上,套接字只是计算机上已编号的端口。如果发送方和接收方计算机确定好端口,他们就可以通信了。

如图12-6所示为客户端与服务器端的通信关系图:
Java基础6网络编程_第52张图片
TCP/IP通信连接的简单过程:
位于A计算机上的TCP/IP软件向B计算机发送包含端口号的消息,B计算机的TCP/IP软件接收该消息,并进行检查,查看是否有它知道的程序正在该端口上接收消息。如果有,他就将该消息交给这个程序。
要使程序有效地运行,就必须有一个客户端和一个服务器。

通过Socket的编程顺序:
1.创建服务器ServerSocket,在创建时,定义ServerSocket的监听端口(在这个端口接收客户端发来的消息)。
2.ServerSocket调用accept()方法,使之处于阻塞状态。
3.创建客户端Socket,并设置服务器的IP及端口。
4.客户端发出连接请求,建立连接。
5.分别取得服务器和客户端Socket的InputStream和OutputStream。
6.利用Socket和ServerSocket进行数据传输。
7.关闭流及Socket。
【示例12-7】TCP:单向通信Socket之服务器端

import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
 
/**
 * 最简单的服务器端代码
 * @author Administrator
 */
public class BasicSocketServer {
    public static void main(String[] args) {
        Socket socket = null;
        BufferedWriter bw = null;
        try {
            // 建立服务器端套接字:指定监听的接口
            ServerSocket serverSocket = new ServerSocket(8888);
            System.out.println("服务端建立监听");
            // 监听,等待客户端请求,并愿意接收连接
            socket = serverSocket.accept();
            // 获取socket的输出流,并使用缓冲流进行包装
            bw = new BufferedWriter(new     
                                    OutputStreamWriter(socket.getOutputStream()));
            // 向客户端发送反馈信息
            bw.write("hhhh");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭流及socket连接
            if (bw != null) {
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

【示例12-8】TCP:单向通信Socket之客户端

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetAddress;
import java.net.Socket;
/**
 * 最简单的Socket客户端
 * @author Administrator
 */
public class BasicSocketClient {
    public static void main(String[] args) {
        Socket socket = null;
        BufferedReader br = null;
        try {
            /*
             * 创建Scoket对象:指定要连接的服务器的IP和端口而不是自己机器的
             * 端口。发送端口是随机的。
             */
            socket = new Socket(InetAddress.getLocalHost(), 8888);
            //获取scoket的输入流,并使用缓冲流进行包装
            br = new BufferedReader(new  
                                   InputStreamReader(socket.getInputStream()));
            //接收服务器端发送的信息
            System.out.println(br.readLine());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            // 关闭流及socket连接
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

【示例12-9】TCP:双向通信Socket之服务器端

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
 
public class Server {
    public static void main(String[] args){
        Socket socket = null;
        BufferedReader in = null;
        BufferedWriter out = null;
        BufferedReader br = null;
        try {
            //创建服务器端套接字:指定监听端口
            ServerSocket server = new ServerSocket(8888);
            //监听客户端的连接
            socket = server.accept();
            //获取socket的输入输出流接收和发送信息
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new BufferedWriter(new 
                                   OutputStreamWriter(socket.getOutputStream()));
            br = new BufferedReader(new InputStreamReader(System.in));
            while (true) {
                //接收客户端发送的信息
                String str = in.readLine();
                System.out.println("客户端说:" + str);
                String str2 = "";
                //如果客户端发送的是“end”则终止连接 
                if (str.equals("end")){
                    break;
                }
                //否则,发送反馈信息
                str2 = br.readLine(); // 读到\n为止,因此一定要输入换行符!
                out.write(str2 + "\n");
                out.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭资源
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(out != null){
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(socket != null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

【示例12-10】TCP:双向通信Socket之客户端

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
 
public class Client {
    public static void main(String[] args) {
        Socket socket = null;
        BufferedReader in = null;
        BufferedWriter out = null;
        BufferedReader wt = null;
        try {
            //创建Socket对象,指定服务器端的IP与端口
            socket = new Socket(InetAddress.getLocalHost(), 8888);
            //获取scoket的输入输出流接收和发送信息
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new BufferedWriter(new 
                                   OutputStreamWriter(socket.getOutputStream()));
            wt = new BufferedReader(new InputStreamReader(System.in));
            while (true) {
                //发送信息
                String str = wt.readLine();
                out.write(str + "\n");
                out.flush();
                //如果输入的信息为“end”则终止连接
                if (str.equals("end")) {
                    break;
                }
                //否则,接收并输出服务器端信息
                System.out.println("服务器端说:" + in.readLine());
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭资源
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (wt != null) {
                try {
                    wt.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (socket != null) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

执行结果如图12-7与图12-8所示
Java基础6网络编程_第53张图片
Java基础6网络编程_第54张图片
菜鸟雷区
运行时,要先启动服务器端,再启动客户端,才能得到正常的运行效果。
但是,上面这个程序,必须按照安排好的顺序,服务器和客户端一问一答!不够灵活!!可以使用多线程实现更加灵活的双向通讯!!
服务器端:一个线程专门发送消息,一个线程专门接收消息。
客户端:一个线程专门发送消息,一个线程专门接收消息。

【示例12-11】TCP:聊天室之服务器端

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
 
public class ChatServer {
    public static void main(String[] args) {
        ServerSocket server = null;
        Socket socket = null;
        BufferedReader in = null;
        try {
            server = new ServerSocket(8888);
            socket = server.accept();
            //创建向客户端发送消息的线程,并启动
            new ServerThread(socket).start();
            // main线程负责读取客户端发来的信息
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            while (true) {
                String str = in.readLine();
                System.out.println("客户端说:" + str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (socket != null) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
 
/**
 * 专门向客户端发送消息的线程
 * 
 * @author Administrator
 *
 */
class ServerThread extends Thread {
    Socket ss;
    BufferedWriter out;
    BufferedReader br;
 
    public ServerThread(Socket ss) {
        this.ss = ss;
        try {
            out = new BufferedWriter(new OutputStreamWriter(ss.getOutputStream()));
            br = new BufferedReader(new InputStreamReader(System.in));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    public void run() {
        try {
            while (true) {
                String str2 = br.readLine();
                out.write(str2 + "\n");
                out.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if(out != null){
                out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(br != null){
                    br.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

【示例12-12】TCP:聊天室之客户端

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
 
public class ChatClient {
    public static void main(String[] args) {
        Socket socket = null;
        BufferedReader in = null;
        try {
            socket = new Socket(InetAddress.getByName("127.0.1.1"), 8888);
            // 创建向服务器端发送信息的线程,并启动
            new ClientThread(socket).start();
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            // main线程负责接收服务器发来的信息
            while (true) {
                System.out.println("服务器说:" + in.readLine());
            }
        } catch (UnknownHostException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (socket != null) {
                    socket.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
 
/**
 * 用于向服务器发送消息
 * 
 * @author Administrator
 *
 */
class ClientThread extends Thread {
    Socket s;
    BufferedWriter out;
    BufferedReader wt;
 
    public ClientThread(Socket s) {
        this.s = s;
        try {
            out = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
            wt = new BufferedReader(new InputStreamReader(System.in));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    public void run() {
        try {
            while (true) {
                String str = wt.readLine();
                out.write(str + "\n");
                out.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (wt != null) {
                    wt.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

执行结果如图12-9与图12-10所示:
Java基础6网络编程_第55张图片
Java基础6网络编程_第56张图片

12.3.5 UDP通讯的实现

▪ DatagramSocket:用于发送或接收数据报包
当服务器要向客户端发送数据时,需要在服务器端产生一个DatagramSocket对象,在客户端产生一个DatagramSocket对象。服务器端的DatagramSocket将DatagramPacket发送到网络上,然后被客户端的DatagramSocket接收。

DatagramSocket有两种常用的构造函数。一种是无需任何参数的,常用于客户端;另一种需要指定端口,常用于服务器端。如下所示:

DatagramSocket() :构造数据报套接字并将其绑定到本地主机上任何可用的端口。
DatagramSocket(int port) :创建数据报套接字并将其绑定到本地主机上的指定端口。

常用方法:
Ø send(DatagramPacket p) :从此套接字发送数据报包。
Ø receive(DatagramPacket p) :从此套接字接收数据报包。
Ø close() :关闭此数据报套接字。

▪ DatagramPacket:数据容器(封包)的作用
此类表示数据报包。 数据报包用来实现封包的功能。
常用方法:
Ø DatagramPacket(byte[] buf, int length) :构造数据报包,用来接收长度为 length 的数据包。
Ø DatagramPacket(byte[] buf, int length, InetAddress address, int port) :构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
Ø getAddress() :获取发送或接收方计算机的IP地址,此数据报将要发往该机器或者是从该机器接收到的。
Ø getData() :获取发送或接收的数据。
Ø setData(byte[] buf) :设置发送的数据。

UDP通信编程基本步骤:
1.创建客户端的DatagramSocket,创建时,定义客户端的监听端口。
2.创建服务器端的DatagramSocket,创建时,定义服务器端的监听端口。
3.在服务器端定义DatagramPacket对象,封装待发送的数据包。
4.客户端将数据报包发送出去。
5.服务器端接收数据报包。

【示例12-13】UDP:单向通信之客户端

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
 
public class Client {
    public static void main(String[] args) throws Exception {
        byte[] b = "北京尚学堂".getBytes();
        //必须告诉数据报包要发到哪台计算机的哪个端口,发送的数据以及数据的长度
        DatagramPacket dp = new DatagramPacket(b,b.length,new 
InetSocketAddress("localhost",8999));
        //创建数据报套接字:指定发送信息的端口
        DatagramSocket ds = new DatagramSocket(9000);
        //发送数据报包
        ds.send(dp);
        //关闭资源
        ds.close();
    }
}

【示例12-14】UDP:单向通信之服务器端

import java.net.DatagramPacket;
import java.net.DatagramSocket;
 
public class Server {
    public static void main(String[] args) throws Exception {
        //创建数据报套接字:指定接收信息的端口
        DatagramSocket ds = new DatagramSocket(8999);
        byte[] b = new byte[1024];
        //创建数据报包,指定要接收的数据的缓存位置和长度
        DatagramPacket dp = new DatagramPacket(b, b.length);
        //接收客户端发送的数据报
        ds.receive(dp); // 阻塞式方法
        //dp.getLength()返回实际收到的数据的字节数
        String string = new String(dp.getData(), 0, dp.getLength());
        System.out.println(string);
        //关闭资源
        ds.close();
    }
}

执行结果如图12-11所示:
Java基础6网络编程_第57张图片
通过字节数组流ByteArrayInputStream、ByteArrayOutputStream与数据流DataInputStream、DataOutputStream联合使用可以传递基本数据类型。

【示例12-15】UDP:基本数据类型的传递之客户端

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
 
public class Client {
    public static void main(String[] args) throws Exception {
        long n = 2000L;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(bos);
        dos.writeLong(n);
        //获取字节数组流中的字节数组(我们要发送的数据)
        byte[] b = bos.toByteArray();
        //必须告诉数据报包要发到哪台计算机的哪个端口,发送的数据以及数据的长度
        DatagramPacket dp = new DatagramPacket(b,b.length,new   
                                             InetSocketAddress("localhost",8999));
        //创建数据报套接字:指定发送信息的端口
        DatagramSocket ds = new DatagramSocket(9000);
        //发送数据报包
        ds.send(dp);
        //关闭资源
        dos.close();
        bos.close();
        ds.close();
    }
}

【示例12-16】UDP:基本数据类型的传递之服务器端

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
 
public class Server {
    public static void main(String[] args) throws Exception {
        //创建数据报套接字:指定接收信息的端口
        DatagramSocket ds = new DatagramSocket(8999);
        byte[] b = new byte[1024];
        //创建数据报包,指定要接收的数据的缓存位置和长度
        DatagramPacket dp = new DatagramPacket(b, b.length);
        //接收客户端发送的数据报
        ds.receive(dp); // 阻塞式方法
        //dp.getData():获取客户端发送的数据,返回值是一个字节数组
        ByteArrayInputStream bis = new ByteArrayInputStream(dp.getData());
        DataInputStream dis = new DataInputStream(bis);
        System.out.println(dis.readLong());
        //关闭资源
        dis.close();
        bis.close();
        ds.close();
    }
}

执行结果如图12-12所示:
Java基础6网络编程_第58张图片
通过字节数组流ByteArrayInputStream、ByteArrayOutputStream与数据流ObjectInputStream、ObjectOutputStream联合使用可以传递对象。

【示例12-17】UDP:对象的传递之Person类

import java.io.Serializable;
public class Person implements Serializable{
    private static final long serialVersionUID = 1L;
    int age;
    String name;
    public Person(int age, String name) {
        super();
        this.age = age;
        this.name = name;
    }
    @Override
    public String toString() {
        return "Person [age=" + age + ", name=" + name + "]";
    }
}

【示例12-18】UDP:对象的传递之客户端

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
 
public class Client {
    public static void main(String[] args) throws Exception {
        //创建要发送的对象
        Person person = new Person(18, "高淇");
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(person);
        //获取字节数组流中的字节数组(我们要发送的数据)
        byte[] b = bos.toByteArray();
        //必须告诉数据报包要发到哪台计算机的哪个端口,发送的数据以及数据的长度
        DatagramPacket dp = new DatagramPacket(b,b.length,new 
                                             InetSocketAddress("localhost",8999));
        //创建数据报套接字:指定发送信息的端口
        DatagramSocket ds = new DatagramSocket(9000);
        //发送数据报包
        ds.send(dp);
        //关闭资源
        oos.close();
        bos.close();
        ds.close();
    }
}  

【示例12-19】UDP:对象的传递之服务器端

import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
 
public class Server {
    public static void main(String[] args) throws Exception {
        //创建数据报套接字:指定接收信息的端口
        DatagramSocket ds = new DatagramSocket(8999);
        byte[] b = new byte[1024];
        //创建数据报包,指定要接收的数据的缓存位置和长度
        DatagramPacket dp = new DatagramPacket(b, b.length);
        //接收客户端发送的数据报
        ds.receive(dp); // 阻塞式方法
        //dp.getData():获取客户端发送的数据,返回值是一个字节数组
        ByteArrayInputStream bis = new ByteArrayInputStream(dp.getData());
        ObjectInputStream ois = new ObjectInputStream(bis);
        System.out.println(ois.readObject());
        //关闭资源
        ois.close();
        bis.close();
        ds.close();
    }
}

执行结果如图12-13所示:
Java基础6网络编程_第59张图片

总结

1.端口是虚拟的概念,并不是说在主机上真的有若干个端口。
2.在www上,每一信息资源都有统一且唯一的地址,该地址就叫URL(Uniform Resource Locator),它是www的统一资源定位符。
3.TCP与UDP的区别
1)TCP是面向连接的,传输数据安全,稳定,效率相对较低。
2)UDP是面向无连接的,传输数据不安全,效率较高。
4.Socket通信是一种基于TCP协议,建立稳定连接的点对点的通信。
5.网络编程是由java.net包来提供网络功能。
1)InetAddress:封装计算机的IP地址和DNS(没有端口信息!)。
2)InetSocketAddress:包含IP和端口,常用于Socket通信。
3)URL:以使用它的各种方法来对URL对象进行分割、合并等处理。
6.基于TCP协议的Socket编程和通信
1)“请求-响应”模式:
–Socket类:发送TCP消息。
–ServerSocket类:创建服务器。
7.UDP通讯的实现
1)DatagramSocket:用于发送或接收数据报包。
2)常用方法:send()、receive()、 close()。
8.DatagramPacket:数据容器(封包)的作用
1)常用方法:构造方法、getAddrress(获取发送或接收方计算机的IP地址)、getData(获取发送或接收的数据)、setData(设置发送的数据)。

如何定位(IP\端口、URL)
TCP\UDP
Socket编程
Java基础6网络编程_第60张图片

你可能感兴趣的:(Java语言基础)