网络编程+Socket+UDP+TCP+三次握手四次挥手

一.网络编程

1.网络编程概述


	A:网络模型概述
		计算机网络之间以何种规则进行通信,就是网络模型研究问题。
		网络模型一般是指
			OSI(Open System Interconnection开放系统互连)七层参考模型
			TCP/IP四层参考模型
				主机至网络层(物理层 , 数据链路层) , 网际层 , 传输层 , 应用层(应用层 , 表示层 , 会话层)
	
	网络模型7层概述:
		1.物理层:主要定义物理设备标准,如网线的接口类型、光纤的接口类型、各种传输介质的传输速率等。
				   它的主要作用是传输比特流(就是由1、0转化为电流强弱来进行传输,到达目的地后在转化为1、0)。
				   这一层的数据叫做比特。 	
		2. 数据链路层:主要将从物理层接收的数据进行MAC地址(网卡的地址)的封装与解封装。常把这一层的数据叫做帧。在这一层工作的设备是交换机,数据通过交换机来传输。 
		3. 网络层:主要将从下层接收到的数据进行IP地址(例192.168.0.1)的封装与解封装。在这一层工作的设备是路由器,常把这一层的数据叫做数据包。 
		4. 传输层:定义了一些传输数据的协议和端口号(WWW端口80等),如:TCP(传输控制协议,传输效率低,可靠性强,用于传输可靠性要求高,数据量大的数据),UDP(用户数据报协议,与TCP特性恰恰相反,用于传输可靠性要求不高,数据量小的数据,如QQ聊天数据就是通过这种方式传输的)。
				  主要是将从下层接收的数据进行分段和传输,到达目的地址后再进行重组。常常把这一层数据叫做段。 
		5.会话层:通过传输层(端口号:传输端口与接收端口)建立数据传输的通路。
				   主要在你的系统之间发起会话或者接受会话请求(设备之间需要互相认识可以是IP也可以是MAC或者是主机名) 
		6.表示层:主要是进行对接收的数据进行解释、加密与解密、压缩与解压缩等(也就是把计算机能够识别的东西转换成人能够能识别的东西(如图片、声音等)。 
		7.应用层: 主要是一些终端的应用,比如说FTP(各种文件下载),WEB(IE浏览),QQ之类的(可以把它理解成我们在电脑屏幕上可以看到的东西.就是终端应用)。
	

2.五层模型

五层模型:应用层,运输层,网络层,数据链路层,物理层

1.物理层又被称作实体层,它就是把电脑连接起来的物理手段。
它主要规定了网络的一些电气特性,作用是负责传送0和1的电信号。

2.数据链路层:单纯的0和1没有任何意义,规定了解读方式,确定0和1的分组方式,
多少个电信号算一组?每个信号位有何意义?
	(1)以太网协议:
		以太网规定,一组电信号构成一个数据包,叫做"帧"(Frame)。
		每一帧分成两个部分:标头(Head)和数据(Data)。"标头"包含数据包的一些说		   
		明项,比如发送者、接受者、数据类型等等;"数据"则是数据包的具体内容。
	    "标头"的长度,固定为18字节。"数据"的长度,最短为46字节,最长为1500字节。
	     因此,整个"帧"最短为64字节,最长为1518字节。如果数据很长,
	     就必须分割成多个帧进行发送。

在这里插入图片描述

	(2)MAC地址:
		上面提到,以太网数据包的"标头",包含了发送者和接受者的信息。
		那么,发送者和接受者是如何标识呢?以太网规定,连入网络的所有设备,
		都必须具有"网卡"接口。数据包必须是从一块网卡,传送到另一块网卡。
		网卡的地址,就是数据包的发送地址和接收地址,这叫做MAC地址。
		每块网卡出厂的时候,都有一个全世界独一无二的MAC地址,
		长度是48个二进制位,通常用12个十六进制数表示。
		前6个十六进制数是厂商编号,后6个是该厂商的网卡流水号。
		有了MAC地址,就可以定位网卡和数据包的路径了。

在这里插入图片描述
在这里插入图片描述

	(3)广播:
		定义地址只是第一步,后面还有更多的步骤。
		首先,一块网卡怎么会知道另一块网卡的MAC地址?
		回答是有一种ARP协议,可以解决这个问题。这个留到后面介绍,这里只需要知道,以太网数据包必须知道接收方的MAC地址,然后才能发送。

		其次,就算有了MAC地址,系统怎样才能把数据包准确送到接收方?
		回答是以太网采用了一种很"原始"的方式,它不是把数据包准确送到接收方,而是		
		向本网络内所有计算机发送,让每台计算机自己判断,是否为接收方。
		
		
3.网络层:
		它的作用是引进一套新的地址,使得我们能够区分不同的计算机是否属于
		同一个子网络。这套地址就叫做"网络地址",简称"网址"。
	(1)IP地址:
		于是,"网络层"出现以后,每台计算机有了两种地址,一种是MAC地址,
		另一种是网络地址。两种地址之间没有任何联系,MAC地址是绑定在网卡上的,
		网络地址则是管理员分配的,它们只是随机组合在一起。
		网络地址帮助我们确定计算机所在的子网络,MAC地址则将数据包送到该子网络中
		的目标网卡。因此,从逻辑上可以推断,必定是先处理网络地址,
		然后再处理MAC地址。
	(2)IP协议:
			规定网络地址的协议,叫做IP协议。它所定义的地址,就被称为IP地址。
			目前,广泛采用的是IP协议第四版,简称IPv4。这个版本规定,
			网络地址由32个二进制位组成。
			
			习惯上,我们用分成四段的十进制数表示IP地址,从0.0.0.0一直到255.255.255.255。

		互联网上的每一台计算机,都会分配到一个IP地址。这个地址分成两个部分,前一			
		部分代表网络,后一部分代表主机。比如,IP地址172.16.254.1,这是一个32位的
		地址,假定它的网络部分是前24位(172.16.254),那么主机部分就是后8位(最
		后的那个1)。处于同一个子网络的电脑,它们IP地址的网络部分必定是相同的,
		也就是说172.16.254.2应该与172.16.254.1处在同一个子网络。
		但是,问题在于单单从IP地址,我们无法判断网络部分。还是以172.16.254.1为		
		例,它的网络部分,到底是前24位,还是前16位,甚至前28位,从IP地址上是看不
		出来的。
		那么,怎样才能从IP地址,判断两台计算机是否属于同一个子网络呢?这就要用到
		另一个参数"子网掩码"(subnet mask)。
		所谓"子网掩码",就是表示子网络特征的一个参数。它在形式上等同于IP地址,也				
		是一个32位二进制数字,它的网络部分全部为1,主机部分全部为0。比如,IP地址
		172.16.254.1,如果已知网络部分是前24位,主机部分是后8位,那么子网络掩码
		就是11111111.11111111.11111111.00000000,写成十进制就是255.255.255.0。
		知道"子网掩码",我们就能判断,任意两个IP地址是否处在同一个子网络。方法是
		将两个IP地址与子网掩码分别进行AND运算(两个数位都为1,运算结果为1,否则
		为0),然后比较结果是否相同,如果是的话,就表明它们在同一个子网络中,否
		则就不是。
		比如,已知IP地址172.16.254.1和172.16.254.233的子网掩码都是255.255.255.0,
		请问它们是否在同一个子网络?两者与子网掩码分别进行AND运算,结果都是
		172.16.254.0,因此它们在同一个子网络。
		总结一下,IP协议的作用主要有两个,一个是为每一台计算机分配IP地址,另一个	
		是确定哪些地址在同一个子网络。
		
	(3)IP数据包:
		根据IP协议发送的数据,就叫做IP数据包。不难想象,其中必定包括IP地址信息。
		但是前面说过,以太网数据包只包含MAC地址,并没有IP地址的栏位。
		那么是否需	要修改数据定义,再添加一个栏位呢?
		回答是不需要,我们可以把IP数据包直接放进以太网数据包的"数据"部分,
		因此完全不用修改以太网的规格。这就是互联网分层结构的好处:
		上层的变动完全不涉及下层的结构。
		具体来说,IP数据包也分为"标头"和"数据"两个部分。

在这里插入图片描述

		"标头"部分主要包括版本、长度、IP地址等信息,
		"数据"部分则是IP数据包的具体内容。它放进以太网数据包后,以太网数据包就变成了下面这样。	

在这里插入图片描述

	(4)ARP协议:
		因为IP数据包是放在以太网数据包里发送的,所以我们必须同时知道两个地址,
		一个是对方的MAC地址,另一个是对方的IP地址。通常情况下,
		对方的IP地址是已知的,但是我们不知道它的MAC地址。
		所以,我们需要一种机制,能够从IP地址得到MAC地址。

		这里又可以分成两种情况。第一种情况,如果两台主机不在同一个子网络,
		那么事实上没有办法得到对方的MAC地址,只能把数据包传送到两个子网络连接处	
		的"网关"(gateway),让网关去处理。

		第二种情况,如果两台主机在同一个子网络,那么我们可以用ARP协议,
		得到对方的MAC地址。ARP协议也是发出一个数据包(包含在以太网数据包中),
		其中包含它所要查询主机的IP地址,在对方的MAC地址这一栏,填的是
		FF:FF:FF:FF:FF:FF,表示这是一个"广播"地址。
		它所在子网络的每一台主机,都会收到这个数据包,从中取出IP地址,
		与自身的IP地址进行比较。如果两者相同,都做出回复,
		向对方报告自己的MAC地址,否则就丢弃这个包。

		总之,有了ARP协议之后,我们就可以得到同一个子网络内的主机MAC地址,
		可以把数据包发送到任意一台主机之上了。
		

4.运输层:
	(1)端口:
	有了MAC地址和IP地址,我们已经可以在互联网上任意两台主机上建立通信。

		接下来的问题是,同一台主机上有许多程序都需要用到网络,比如,你一边浏览网页,一边与朋友在线聊天。
		当一个数据包从互联网上发来的时候,你怎么知道,它是表示网页的内容,还是表示在线聊天的内容?

		也就是说,我们还需要一个参数,表示这个数据包到底供哪个程序(进程)使用。这个参数就叫做"端口"(port),
		它其实是每一个使用网卡的程序的编号。每个数据包都发到主机的特定端口,所以不同的程序就能取到自己所需要的数据。

		"端口"是0到65535之间的一个整数,正好16个二进制位。0到1023的端口被系统占用,用户只能选用大于1023的端口。
		不管是浏览网页还是在线聊天,应用程序会随机选用一个端口,
		然后与服务器的相应端口联系。

		"传输层"的功能,就是建立"端口到端口"的通信。
		相比之下,"网络层"的功能是建立"主机到主机"的通信。
		只要确定主机和端口,我们就能实现程序之间的交流。
		因此,Unix系统就把主机+端口,叫做"套接字"(socket)。有了它,就可以进行网络应用程序开发了。
	(2)UDP协议:
		现在,我们必须在数据包中加入端口信息,这就需要新的协议。
		最简单的实现叫做UDP协议,它的格式几乎就是在数据前面,加上端口号。
		UDP数据包,也是由"标头"和"数据"两部分组成。
		"标头"部分主要定义了发出端口和接收端口,"数据"部分就是具体的内容。
		然后,把整个UDP数据包放入IP数据包的"数据"部分,
		而前面说过,IP数据包又是放在以太网数据包之中的.
		UDP数据包非常简单,"标头"部分一共只有8个字节,总长度不超过65,535字节,正好放进一个IP数据包。

在这里插入图片描述
在这里插入图片描述

	(3)TCP协议:
		UDP协议的优点是比较简单,容易实现,但是缺点是可靠性较差,一旦数据包发出,无法知道对方是否收到。

		为了解决这个问题,提高网络可靠性,TCP协议就诞生了。
		这个协议非常复杂,但可以近似认为,它就是有确认机制的UDP协议,每发出一个	
		数据包都要求确认。如果有一个数据包遗失,就收不到确认,
		发出方就知道有必要重发这个数据包了。
		因此,TCP协议能够确保数据不会遗失。
		它的缺点是过程复杂、实现困难、消耗较多的资源。
		TCP数据包和UDP数据包一样,都是内嵌在IP数据包的"数据"部分。
		TCP数据包没有长度限制,理论上可以无限长,但是为了保证网络的效率,
		通常TCP数据包的长度不会超过IP数据包的长度,以确保单个TCP数据包不必再分割

		
5.应用层:
	"应用层"的作用,就是规定应用程序的数据格式。
		即各种应用程序的使用,面向用户

3.网络编程三要素

(1)概述

	A:IP地址:InetAddress:	网络中设备的标识,不易记忆,可用主机名
	B:端口号:	用于标识进程的逻辑地址,不同进程的标识   
	C:传输协议:	通讯的规则常见协议:TCP,UDP

(2)IP概述

A:IP概述:所谓IP地址就是给每个连接在Internet上的主机分配的一个32bit地址。 
	B:IP地址的组成
		IP地址 = 网络地址+主机地址  
		A类IP地址:第一段号码为网络地址,剩下的三段号码为本地计算机的号码
		B类IP地址:前二段号码为网络地址,剩下的二段号码为本地计算机的号码
		C类IP地址:前三段号码为网络地址,剩下的一段号码为本地计算机的号码
	C:IP地址分类
		A类	1.0.0.1---127.255.255.254
			(1)10.X.X.X是私有地址(私有地址就是在互联网上不使用,而被用在局域网络中的地址)							
			(2)127.X.X.X是保留地址,用做循环测试用的。
		B类	128.0.0.1---191.255.255.254	   172.16.0.0---172.31.255.255是私有地址。
		C类	192.0.0.1---223.255.255.254	   192.168.x.x是私有地址
		D类	224.0.0.1---239.255.255.254 	
		E类	240.0.0.1---247.255.255.254	  
	D:特殊地址
		127.0.0.1 回环地址,可用于测试本机的网络是否有问题. ping 127.0.0.1   
		DOS命令 ipconfig:查看本机IP地址
		xxx.xxx.xxx.255 广播地址

(3)InetAddress类的概述和使用

//获取主机名的 DOS 命令 :hostname
​	A:InetAddress类的概述
​		为了方便我们对IP地址的获取和操作,java提供了一个类InetAddress 供我们使用
​		此类表示互联网协议 (IP) 地址。 
​	B:InetAddress类的常见功能
​		public static InetAddress getByName(String host)
​		public String getHostAddress()//获取IP
​		public String getHostName()//获取主机名
​		getLocalHost();
package org.westos.demo;

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

public class MyTest {
    public static void main(String[] args) throws Exception {
        //针对IP Java提供了一个类用来描述InetAddress
      /*  static InetAddress getByName (String host)
        在给定主机名的情况下确定主机的 IP 地址。
		 返回的是一个包装的对象
*/
	  //返回的是: 主机名/ip
        InetAddress inetAddress = InetAddress.getByName("DESKTOP-FQNCGA7");
        System.out.println(inetAddress);

        //获取IP
        String ip = inetAddress.getHostAddress();
        System.out.println(ip);

        //获取主机名
        String hostName = inetAddress.getHostName();
        System.out.println(hostName);
        System.out.println("===============");

        //传入IP地址
        InetAddress inetAddress1 = InetAddress.getByName("192.168.5.2");

        //获取ip
        String ip1 = inetAddress1.getHostAddress();
        System.out.println(ip1);

        //获取主机名
        String hostName1 = inetAddress1.getHostName();
        System.out.println(hostName1);
    }
}

(4)端口号和传输协议

A:端口
		物理端口 网卡口
		逻辑端口 我们指的就是逻辑端口
			a:每个网络程序都会有一个逻辑端口
			b:用于标识进程的逻辑地址,不同进程的标识
			c:有效端口:0~65535(两个字节),其中0~1023系统使用或保留端口。
	B:协议
		UDP 发短信
			将数据源和目的封装成数据包中,不需要建立连接;
			每个数据报的大小在限制在64k;
			因无连接,是不可靠协议;
			不需要建立连接,速度快
			
		TCP  打电话 视频
			建立连接,形成传输数据的通道;
			在连接中进行大数据量传输;
			需要连接所以是可靠协议;
			必须建立连接,效率会稍低

二.Socket

1.概述

A:Socket套接字概述:
		网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。
	B:Socket原理机制:
		通信的两端都有Socket。
		网络通信其实就是Socket间的通信。
		数据在两个Socket间通过IO传输。
		
 Socket=IP+端端口 也叫做套接字编程
        要求通信两端都要有这个Socket
        针对不同协议的Socket Java给我们提供了相应的Socket

2.UDP协议

(1)概述

  UDP协议的特点:通信两端不需要建立连接,发送的数据大小有限制,
         因为不需要建立连接,效率高一些,但是属于不可靠协议,有肯会发生数据丢失问题

        针对不同协议的Socket Java给我们提供了相应的Socket
        UDP协议的Socket
         DatagramSocket 此类表示用来发送和接收数据报包的套接字。

        UDP协议的Socket 客户端和服务端用的都是一个类DatagramSocket

(2)UDP协议发送数据

UDP协议发送数据(DatagramSocket)
	- 步骤:
	- a: 创建UDP通讯客户端对象(DatagramSocket)
	- b: 创建数据报包 public DatagramPacket(byte[] buf, int length, InetAddress address,int port)
	- c: 发送 数据
	- d: 释放资源
package org.westos.demo;

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

//客户端:发送数据
public class UDPClient {
    public static void main(String[] args) throws Exception{
        //1.创建UDP的Socket
        //DatagramSocket() 构造数据报套接字并将其绑定到本地主机上任何可用的端口。
        DatagramSocket ds = new DatagramSocket();

        //2.发送数据(封装到数据报包里面),只需要知道对方的ip和端口就可以
        //类 DatagramPacket 此类表示数据报包。封装了数据
     /*   DatagramPacket( byte[] buf, int length, InetAddress address,int port)
        构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。*/


       String string="今晚速驰电竞见,干个通宵";
        byte[] bytes = string.getBytes();
        //数据报包里面封装:你要发送的数据,数据的长度,对方的IP ,对方的端口
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length,
                InetAddress.getByName("DESKTOP-FQNCGA7"),9999);

        //发送数据报包
        ds.send(dp);
        //释放资源
        ds.close();
    }
}

(3)UDP协议接收数据

UDP协议接收数据
	- 步骤:// public DatagramSocket(int port)
	- a: 创建UDP通讯协议服务器端对象(DatagramSocket) 注意要用有参数构造 指定端口号
	- b: 创建数据报包,作用用来接收数据  // public DatagramPacket(byte[] buf, int length)
	- c: 接收数据  receive(dp) ;
	- d: 解析数据报包,拿出数据  dp.getData() ;  dp.getLength() ;
	- e: 释放资源

package org.westos.demo;

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

public class UDPServer {
    public static void main(String[] args) throws Exception{
        //创建UDP的Socket的服务端
      /*  构造方法
      DatagramSocket( int port)
        创建数据报套接字并将其绑定到本地主机上的指定端口。*/
        DatagramSocket ds = new DatagramSocket(9999);

             /*  DatagramPacket( byte[] buf, int length)
        构造 DatagramPacket,用来接收长度为 length 的数据包。*/
        byte[] bytes = new byte[1024];
        DatagramPacket dp = new DatagramPacket(bytes,bytes.length);
        System.out.println("服务器已经开启,等待接收数据.....");
        //等待接收数据,是一个阻塞方法,
       /* 从此套接字接收数据报包。当此方法返回时,DatagramPacket 的缓冲区填充了接收的数据。
        数据报包也包含发送方的 IP 地址和发送方机器上的端口号。
        此方法在接收到数据报前一直阻塞*/

        //接收发送的数据,他需要一个空的数据报包,用来装发过来的数据,获取数据报包
        ds.receive(dp);

        System.out.println("下面的代码......");
        //从数据报包里取出数据
        byte[] data = dp.getData();
        System.out.println(data);
        //从数据报包中获取数据的实际长度
        int length = dp.getLength();
        System.out.println(length);

        //数据取出是一个字节数组,转换为字符串
        String s = new String(data, 0, length);
        System.out.println(s);

        //通过发送过来的数据可以获知发送者的IP
        InetAddress address = dp.getAddress();
        String ip = address.getHostAddress();
        System.out.println("IP为"+ip+"的老铁发了一句:"+s+":给你");

        //获取发送数据Socket的端口号
        int port = dp.getPort();
        System.out.println(port);

        //服务端一般不选择关闭
        //ds.close();
    }
}

(4)客户端键盘录入

客户端键盘录入,不断地给服务端发送消息
package org.westos.demo3;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class MyClient {
    public static void main(String[] args) throws Exception{
        //客户端键盘录入,不断地给服务端发送消息

        //创建客户端Socket对象,无参,表示端口号是任意可用端口
        DatagramSocket datagramSocket = new DatagramSocket();

        String string="已开好机,今晚必上白银";

        //键盘不断录入:
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while (true){

            System.out.println("请输入你要发送的数据");
            String line = reader.readLine();

            byte[] bytes = line.getBytes();


            //创建一个数据报包,参数是发送的数据,发送数据的长度,接收方的ip,接收方的端口号
            DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length,
                    InetAddress.getByName("DESKTOP-FQNCGA7"),9999);

            datagramSocket.send(datagramPacket);

            datagramSocket.close();
        }



    }
}
------------------

package org.westos.demo3;

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

public class MyServer {
    public static void main(String[] args) throws Exception {
        //创建一个Socket对象,参数是端口号,指定
        DatagramSocket datagramSocket = new DatagramSocket(9999);

        byte[] bytes = new byte[1024];
        //封装数据,传一个空数组用来装数据
        DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length);
        //不断接收发送过来的数据
        while (true) {
            datagramSocket.receive(datagramPacket);

            byte[] data = datagramPacket.getData();
            String s = new String(data);
            System.out.println(s);
        }
    }
}

(5)多线程实现客户端键盘录入

开启两个子线程,一个线程是客户端,一个线程是服务端
package org.westos.demo4;

public class MyTest {
    public static void main(String[] args) {
        //多线程实现键盘录入,不断地给服务端发送消息
        //开启两个子线程,一个线程是客户端,一个线程是服务端
        new UDPClientThread().start();
        new UDPServerThread().start();
    }
}
-----------
package org.westos.demo4;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

public class UDPServerThread extends Thread{
    @Override
    public void run() {
        try {
            DatagramSocket datagramSocket = new DatagramSocket(9999);

            while (true) {

                DatagramPacket datagramPacket = new DatagramPacket(new byte[1024], 1024);

                datagramSocket.receive(datagramPacket);

                byte[] data = datagramPacket.getData();
                String s = new String(data);
                //定义一个结束标记
                if ("886".equals(s)){
                    break;
                }

                String ip = datagramPacket.getAddress().getHostAddress();

                System.out.println("ip为"+ip+"的老铁发消息:"+s);


            }
            datagramSocket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
--------------------
package org.westos.demo4;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

public class UDPClientThread extends Thread{
    @Override
    public void run() {
        try {
           //客户端键盘录入,不断地给服务端发送消息
            DatagramSocket datagramSocket = new DatagramSocket();

            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            while (true) {
                System.out.println("请输入要发送的消息");
                String line = reader.readLine();

                byte[] bytes = line.getBytes();

                DatagramPacket datagramPacket = new DatagramPacket(bytes,bytes.length,
                        InetAddress.getByName("DESKTOP-FQNCGA7"),9999);

                datagramSocket.send(datagramPacket);

                datagramSocket.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}


(6)多线程实现聊天室

在这里插入图片描述

package org.westos.demo5;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

public class A {
    public static void main(String[] args) {

        Thread th=new Thread(new Runnable() {
            @Override
            public void run() {
                //接收B的消息
                try {
                    DatagramSocket datagramSocket = new DatagramSocket(9999);

                    DatagramPacket datagramPacket = new DatagramPacket(new byte[1024],1024);
                    //接收数据
                    System.out.println("A 服务器已经开启,等待接收数据.....");
                    while (true) {
                        datagramSocket.receive(datagramPacket);

                        byte[] data = datagramPacket.getData();
                        String s = new String(data);

                        String ip = datagramPacket.getAddress().getHostAddress();

                        int port = datagramPacket.getPort();

                        System.out.println("ip为" + ip + "端口:" + port + "的老铁B发来消息" + s);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        th.start();

        faSong();
    }

    public static void faSong(){
        try {
            //客户端发送数据给B
            DatagramSocket datagramSocket = new DatagramSocket();

            BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
            while (true) {
                System.out.println("请输入要发送的信息");
                String line = reader.readLine();

                byte[] bytes = line.getBytes();

                if ("886".equals(line)) {
                    break;
                }
                DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length,
                        InetAddress.getByName("DESKTOP-FQNCGA7"), 8888);

                datagramSocket.send(datagramPacket);


            }
            //datagramSocket.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
-----------------
package org.westos.demo5;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.*;

public class B {
    public static void main(String[] args) throws IOException {

        Thread th=new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    DatagramSocket datagramSocket = new DatagramSocket(8888);

                    DatagramPacket datagramPacket = new DatagramPacket(new byte[1024],1024);
                    //接收数据
                    System.out.println("B 服务器已经开启,等待接收数据.....");
                    while (true) {
                        datagramSocket.receive(datagramPacket);

                        byte[] data = datagramPacket.getData();
                        String s = new String(data);

                        String ip = datagramPacket.getAddress().getHostAddress();
                        int port = datagramPacket.getPort();
                        System.out.println("ip为" + ip + "端口:" + port + "的老铁B发来消息" + s);
                    }

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        th.start();
        faSong();

    }

    public static void faSong() throws IOException {
        //客户端发送数据
        DatagramSocket datagramSocket = new DatagramSocket();

        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while (true) {
            System.out.println("请输入要发送的信息");
            String line = reader.readLine();

            byte[] bytes = line.getBytes();

            if ("886".equals(line)) {
                break;
            }
            DatagramPacket datagramPacket = new DatagramPacket(bytes, bytes.length,
                    InetAddress.getByName("DESKTOP-FQNCGA7"), 9999);

            datagramSocket.send(datagramPacket);


        }
        //datagramSocket.close();


    }
}

3.TCP协议

(1)概述

TCP:通信两端,需要建立连接,属于可靠协议,传输数据大小无限制。
两端用的类不一样
	客户端用Socket
	服务端用ServerSocket类

(2)TCP协议发送数据

TCP协议发送数据(Scoket)
	
	* 步骤:
	- a: 创建TCP通讯协议客户端对象(Socket)   // public Socket(String host, int port)
	- b: 获取输出流对象
	- c: 写数据
	- d: 释放资源
package org.westos.demo;

import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;

public class TCPClient {
    public static void main(String[] args) throws Exception{
        //TCP客户端发送数据
        //采用Socket类
        // Socket 此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点

        //构造方法
        /*Socket(InetAddress address, int port)
        创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
        Socket(String host, int port)
        创建一个流套接字并将其连接到指定主机上的指定端口号。*/
        //创建客户端的Socket 并指定服务端的IP和端口
        Socket socket = new Socket(InetAddress.getByName("DESKTOP-FQNCGA7"),9999);
        //Socket socket1 = new Socket("DESKTOP-FQNCGA7", 8888);

        //TCP通过流来传输数据
        //获取通道中的输出流
        OutputStream out = socket.getOutputStream();

        out.write("今晚速驰战个痛快".getBytes());
        socket.close();

    }
}

(3)TCP协议接收数据

TCP协议接收数据(ServerSocket)
	* 步骤:
	- a: 创建TCP通讯协议服务器端对象(ServerSocket)
	- b: 监听客户端
	- c: 获取输入流对象
	- d: 读取数据
	- e: 释放资源
package org.westos.demo;

import java.io.InputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {
    public static void main(String[] args) throws Exception{
        //TCP服务端,接收数据
        //采用ServerSocket类
        //构造方法:
        //ServerSocket( int port)
        //创建绑定到特定端口的服务器套接字。并暴露端口号
        ServerSocket serverSocket = new ServerSocket(9999);


        System.out.println("服务端已开启,等待接收数据");
        //侦听,此方法是阻塞方法
        //accept() 侦听并接受到此套接字的连接。此方法在连接传入之前一直阻塞。
        Socket socket = serverSocket.accept();

        //获取通道中的输入流
        InputStream in = socket.getInputStream();
        byte[] bytes = new byte[1024];

        int len = in.read(bytes);

        String s = new String(bytes,0,len);

        InetAddress inetAddress = socket.getInetAddress();
        String ip = inetAddress.getHostAddress();

        System.out.println("ip为:"+ip+"的老铁发来消息:"+s);

        //serverSocket.close();
    }
}

(4)服务器给客户端一个反馈

客户端发送数据,服务器接受数据并给出反馈
package org.westos.demo;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class MyClient2 {
    public static void main(String[] args) throws Exception{
        //客户端发送数据,服务器接受数据并给出反馈
        Socket socket = new Socket("DESKTOP-FQNCGA7",9999);

        OutputStream out = socket.getOutputStream();
        //发送数据
        out.write("我爱Java".getBytes());

        //读取服务端的反馈
        InputStream in = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len = in.read(bytes);//阻塞方法,读取不到反馈会阻塞
        String s = new String(bytes, 0, len);
        System.out.println("反馈是"+s);

    }
}
---------------------------
package org.westos.demo;

import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServer2 {
    public static void main(String[] args) throws Exception{
        //客户端发送数据,服务器接受数据并给出反馈
        ServerSocket ss = new ServerSocket(9999);

        System.out.println("服务端已开启,等待接收数据");
        Socket socket = ss.accept();
        System.out.println("下面的代码");

        InputStream in = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len = in.read(bytes);
        String s = new String(bytes, 0, len);

        String ip = socket.getInetAddress().getHostAddress();

        System.out.println("ip为"+ip+"的老铁给你发来消息:"+s);

        //给客户端一个反馈
        OutputStream out = socket.getOutputStream();
        out.write(":这是服务端给你的反馈".getBytes());

        ss.close();
    }
}

(5)客户端键盘录入服务器控制台输出

package org.westos.demo2;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {
    public static void main(String[] args) throws Exception{
        //客户端键盘录入服务器控制台输出
        ServerSocket ss = new ServerSocket(9999);

        System.out.println("服务端已开启");
        //接收来自客户端的连接
        Socket socket = ss.accept();
        InputStream in = socket.getInputStream();

        BufferedReader reader = new BufferedReader(new InputStreamReader(in));

        /*while (true){

            byte[] bytes = new byte[1024];

            int len = in.read(bytes);
            String s = new String(bytes, 0, len);

            String ip = socket.getInetAddress().getHostAddress();
            System.out.println("ip为"+ip+"的老铁发来消息:"+s);
        }*/
        /*while (true){
            String line = reader.readLine();
            System.out.println(line);

        }*/
        String line=null;
        while ((line=reader.readLine())!=null){
            System.out.println(line);
        }
    }
}
----------------
package org.westos.demo2;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {
    public static void main(String[] args) throws Exception{
        //客户端键盘录入服务器控制台输出
        ServerSocket ss = new ServerSocket(9999);

        System.out.println("服务端已开启");
        //接收来自客户端的连接
        Socket socket = ss.accept();
        InputStream in = socket.getInputStream();

        BufferedReader reader = new BufferedReader(new InputStreamReader(in));

        /*while (true){

            byte[] bytes = new byte[1024];

            int len = in.read(bytes);
            String s = new String(bytes, 0, len);

            String ip = socket.getInetAddress().getHostAddress();
            System.out.println("ip为"+ip+"的老铁发来消息:"+s);
        }*/
        /*while (true){
            String line = reader.readLine();
            System.out.println(line);

        }*/
        String line=null;
        while ((line=reader.readLine())!=null){
            System.out.println(line);
        }
    }
}

(6)客户端键盘录入服务器写到文本文件

package org.westos.demo3;

import java.io.*;
import java.net.Socket;

public class TCPClient {
    public static void main(String[] args) throws Exception{
        //客户端键盘录入服务器写到文本文件
        Socket socket = new Socket("DESKTOP-FQNCGA7", 9999);

        //获取通道中的流
        OutputStream out = socket.getOutputStream();
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));

        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        while (true){
            //从键盘获取要发送的数据
            System.out.println("请输入数据");
            String line = reader.readLine();

            writer.write(line);
            writer.newLine();
            writer.close();
            //定义结束标记,否则循环下不去
            if ("再见".equals(line)){
                break;
            }
        }
        //socket.close();
    }
}
-------------------
package org.westos.demo3;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {
    public static void main(String[] args) throws Exception{
        //客户端键盘录入服务器写到文本文件
        ServerSocket ss = new ServerSocket(9999);
        System.out.println("服务端已开启,......");

        Socket socket = ss.accept();
        InputStream in = socket.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));

        BufferedWriter writer = new BufferedWriter(new FileWriter("a.txt"));
        String line=null;

        while ((line=reader.readLine())!=null){
            //System.out.println(line);
            writer.write(line);
            writer.newLine();
            writer.flush();
        }

    }
}

(7)客户端读取文本文件服务器控制台输出

package org.westos.demo4;

import java.io.*;
import java.net.Socket;

public class MyClient {
    public static void main(String[] args) throws Exception{
        //客户端读取文本文件服务器控制台输出
        BufferedReader reader = new BufferedReader(new FileReader("MyClient.java"));

        Socket socket = new Socket("DESKTOP-FQNCGA7", 9999);
        OutputStream out = socket.getOutputStream();
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));

        String line=null;
        while ((line=reader.readLine())!=null){
            //System.out.println(line);

            writer.write(line);
            writer.newLine();
            writer.flush();
        }
        socket.close();
        writer.close();
    }
}
-------------------
package org.westos.demo4;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServer {
    public static void main(String[] args) throws Exception{
        //客户端读取文本文件服务器控制台输出
        ServerSocket ss = new ServerSocket(9999);
        System.out.println("服务器已开启...");
        Socket socket = ss.accept();
        InputStream in = socket.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));

        String line=null;
        while ((line=reader.readLine())!=null){
            System.out.println(line);
        }
        reader.close();
        ss.close();
    }
}

(8)TCP协议上传文本文件

TCP协议上传文本文件(客户端读取文本文件发送数据到服务器端,服务器端读取数据存储到文本文件中)
package org.westos.demo5;

import java.io.*;
import java.net.Socket;

public class MyClient {
    public static void main(String[] args) throws Exception{
        //TCP协议上传文本文件(客户端读取文本文件发送数据到服务器端,服务器端读取数据存储到文本文件中)

        BufferedReader reader = new BufferedReader(new FileReader("MyClient.java"));

        Socket socket = new Socket("DESKTOP-FQNCGA7",9999);
        OutputStream out = socket.getOutputStream();
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));

        String line=null;
        while ((line=reader.readLine())!=null){
            System.out.println(line);
            writer.write(line);
            writer.newLine();
            writer.flush();
        }
        writer.close();
        socket.close();
    }
}
-----------
package org.westos.demo5;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServer {
    public static void main(String[] args) throws Exception{
        //TCP协议上传文本文件(客户端读取文本文件发送数据到服务器端,服务器端读取数据存储到文本文件中)

        ServerSocket ss = new ServerSocket(9999);
        Socket socket = ss.accept();

        InputStream in = socket.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));

        BufferedWriter writer = new BufferedWriter(new FileWriter("b.txt"));

        String line=null;
        while ((line=reader.readLine())!=null){
            System.out.println(line);
            writer.write(line);
            writer.newLine();
            writer.flush();
        }

    }
}

4.TCP上传文本文件并给出反馈

TCP上传文本文件并给出反馈
	B:TCP传输容易出现的问题
		客户端连接上服务端,两端都在等待,没有任何数据传输。
		
		通过例程分析:
			因为read方法或者readLine方法是阻塞式。
		
		解决办法:
			1.自定义结束标记
		解决办法2:使用shutdownInput,shutdownOutput方法。
			//客户端上传文件结束后 直接结束流对象
		// public void shutdownOutput()
		    sk.shutdownOutput() ;
package org.westos.demo6;

import java.io.*;
import java.net.Socket;

public class TCPClient {
    public static void main(String[] args) throws Exception {
        //TCP上传文本文件并给出反馈
        BufferedReader reader = new BufferedReader(new FileReader("MyClient.java"));

        Socket socket = new Socket("DESKTOP-FQNCGA7", 9999);
        OutputStream out = socket.getOutputStream();
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));

        String line = null;
        while ((line = reader.readLine()) != null) {
           // System.out.println(line);
            writer.write(line);
            writer.newLine();
            writer.flush();
        }
        System.out.println("客户端能下来");
        /*
        * 此时出现一个问题,客户端能下来,但是服务端不显示上传成功
        * 原因是:客户端在等待服务端给他反馈,服务端在等待客户端啥时候可以传递完文件
        * 客户端在读取文件时,可以以null为结尾结束while循环
        但是服务端在通道中读取数据时,不能以null为条件来结束循环,
        * 导致readLine()方法会一直被阻塞
        *
        * 解决方式:
        * 1.自定义结束标记,存在缺陷
        * */

        //给服务端手写一个标记,告诉服务端,客户端上传chengg
        //1,自定义结束标记,存在缺陷,如果文件中有over标记,不能完整上传
        writer.write("over");
        writer.newLine();
        writer.flush();


        //2.客户端上次完文件,关闭输出流,则服务器不会在等待

        //void shutdownInput ()
        //此套接字的输入流置于“流的末尾”。
        //void shutdownOutput ()
        //禁用此套接字的输出流。
         socket.shutdownOutput();

        InputStream in = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len = in.read(bytes);//读取服务端的反馈,读取不到,就阻塞在这里
        String s = new String(bytes, 0, len);
        System.out.println(s);

    }
}
---------------------------
package org.westos.demo6;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {
    public static void main(String[] args) throws Exception{
        //TCP上传文本文件并给出反馈
        ServerSocket serverSocket = new ServerSocket(9999);
        System.out.println("服务器已开启...");
        Socket socket = serverSocket.accept();

        InputStream in = socket.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(in));

        BufferedWriter writer = new BufferedWriter(new FileWriter("c.java"));

        String line=null;
        while ((line=reader.readLine())!=null){
            //System.out.println(line);
            writer.write(line);
            writer.newLine();
            writer.flush();
           /* if ("over".equals(line)){
                break;
            }*/
        }

        //上传成功,服务器给出反馈
        OutputStream out = socket.getOutputStream();
        out.write("上传成功".getBytes());
        System.out.println("下面的代码");

        writer.close();
        serverSocket.close();
    }
}

5.侦听多个客户端

侦听多个客户端:多个客户端上传文件
package org.westos.demo7;

import java.io.*;
import java.net.Socket;
import java.nio.Buffer;

public class TCPClient {
    public static void main(String[] args) throws Exception{
        //侦听多个客户端:多个客户端上传文件
        Socket socket = new Socket("DESKTOP-FQNCGA7", 9999);

        OutputStream out = socket.getOutputStream();
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out));

        BufferedReader reader = new BufferedReader(new FileReader("MyClient.java"));

        String line=null;
        while ((line=reader.readLine())!=null){
            writer.write(line);
            writer.newLine();
            writer.flush();
        }
        socket.shutdownOutput();

        writer.close();
        socket.close();
    }
}
------------------------
package org.westos.demo7;

import java.net.ServerSocket;
import java.net.Socket;

public class TCPServer {
    public static void main(String[] args) throws Exception{
        //让一个服务端。连接多个客户端。
        ServerSocket ss = new ServerSocket(9999);
        System.out.println("服务器已开启...");
        int i=1;
        while (true){
            //循环侦听
            Socket socket = ss.accept();
            System.out.println((i++)+"个客户端连接成功");
            //为每一个连接上来的客户端,单独开启一个线程来处理
            //把每个客户端socket 传入到线程里面去
            new UploadFileThread(socket).start();
        }


    }
}
----------------------------
package org.westos.demo7;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;

public class UploadFileThread extends Thread{
    private Socket socket;
    public UploadFileThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        try {
            InputStream in = socket.getInputStream();
            OutputStream out = socket.getOutputStream();

            BufferedReader reader = new BufferedReader(new InputStreamReader(in));

            BufferedWriter writer = new BufferedWriter(new FileWriter(System.currentTimeMillis() + ".java"));

            String line=null;

            while ((line=reader.readLine())!=null){
                //System.out.println(line);
                writer.write(line);
                writer.newLine();
                writer.flush();
            }
            //如果上传成功,服务端给客户端一个反馈
            out.write("客户端你好,上传的文件我已经收到".getBytes());

            socket.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

6.上传图片文件

package org.westos.demo8;

import java.io.*;
import java.net.Socket;

public class MyClient {
    public static void main(String[] args) throws Exception{
        Socket socket = new Socket("DESKTOP-FQNCGA7",9999);

        OutputStream out = socket.getOutputStream();

        byte[] bytes1 = new byte[1024];

        FileInputStream in1 = new FileInputStream(new File("zpc.jpg"));
        int len1 = -1;
        while ((len1=in1.read(bytes1))!=-1){
            out.write(bytes1,0,len1);
            out.flush();
        }
        socket.shutdownOutput();

        //读取服务端的反馈
        InputStream in = socket.getInputStream();
        byte[] bytes = new byte[1024];
        int len = in.read(bytes); //读取服务端的反馈,读取不到,就阻塞在这里
        String s = new String(bytes, 0, len);
        System.out.println(s);


    }
}
-----------
package org.westos.demo8;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

public class MyServer {
    public static void main(String[] args) throws IOException {
        //多个客户端复制图片
        ServerSocket serverSocket = new ServerSocket(9999);

        int i=1;
        while (true) {
            Socket socket = serverSocket.accept();

            System.out.println((i++)+"个客户端连接成功");
            new UploadFileThread(socket).start();
        }
    }
}
-----------
package org.westos.demo8;

import java.io.*;
import java.net.Socket;

public class UploadFileThread extends Thread{
    private Socket socket;

    public UploadFileThread(Socket socket) {
        this.socket = socket;
    }

    @Override
    public void run() {
        //复制一个图片
        try {
            InputStream in = socket.getInputStream();
            OutputStream out = socket.getOutputStream();

            int i=1;
            FileOutputStream outputStream = new FileOutputStream(new File((i++)+".jpg"));

            byte[] bytes = new byte[1024];

            int len=0;
            while ((len=in.read(bytes))!=-1){
                outputStream.write(bytes,0,len);
                outputStream.flush();
            }
            //上传成功给客户端一个反馈
            out.write("上传成功".getBytes());
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

三.三次握手四次挥手

三次握手:
第一次握手:客户端发送syn包(syn=x)到服务器,并进入SYN_SEND状态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(syn=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。

四次挥手:
与建立连接的“三次握手”类似,断开一个TCP连接则需要“四次握手”。
第一次挥手:主动关闭方发送一个FIN,用来关闭主动方到被动关闭方的数据传送,也就是主动关闭方告诉被动关闭方:我已经不 会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,主动关闭方依然会重发这些数据),但是,此时主动关闭方还可 以接受数据。
第二次挥手:被动关闭方收到FIN包后,发送一个ACK给对方,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。
第三次挥手:被动关闭方发送一个FIN,用来关闭被动关闭方到主动关闭方的数据传送,也就是告诉主动关闭方,我的数据也发送完了,不会再给你发数据了。
第四次挥手:主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,至此,完成四次挥手。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值