侧边栏壁纸
博主头像
coydone博主等级

记录学习,分享生活的个人站点

  • 累计撰写 306 篇文章
  • 累计创建 51 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

网络编程

coydone
2021-04-03 / 0 评论 / 0 点赞 / 309 阅读 / 17,941 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-05-01,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

网络概述

网络的概念和分类

简单来说,网络就是连接在一起共享数据和资源的一组计算机。

分布在不同地理区域的计算机与专门的外部设备通过通信线路互连在一起,形成一个规模大、功能强的网络系统,从而使从多的计算机可以方便地互相传递信息、共享信息资源。

计算机网络旨在实现数据通信。数据可以有多种形式,如文本、图片或声音。进行数据通信的两台计算机可以相距很近(如同一间办公室),也可以在地理位置上相隔很远(如不同的国家)。

按照地理覆盖范围,计算机网络可以划分为局域网、城域网和广域网。

局域网(LAN):局限在小的地理区域或单独的建筑物内,被用于连接公司办公室、实验室或工厂里的个人计算机和工作站。

城域网(MAN):覆盖城市或城镇内的广大地理区域,是在一个城市范围内所建立的计算机通信网。

广域网(WAN):是在一个更广泛的地理范围内所建立的计算机通信网,其范围可以超越城市和国家以至全球,因而对通信的要求及复杂性都比较高。

网络分层模型

国际标准化组织(International Standard Organization,ISO)于1984年颁布了开放系统互连(Open System Interconnection,OSI)参考模型。OSI参考模型是一个开放式体系结构,它规定将网络分为七层,每一层在网络信息传递中都发挥不同的作用。

分层 功能
应用层 网络服务和最终用户的接口
表示层 数据的表示、安全和压缩
会话层 建立、管理和终止会话
传输层 定义传输数据的协议端口号,流量控制和差错恢复
网络层 进行逻辑地址寻址,实现不同网络之间的路径选择
数据链路层 建立逻辑连接,进行硬件地址寻址,差错校验等功能
物理层 建立、维护、断开物理连接

另外一个著名的模型是TCP/IP模型。TCP/IP是传输控制协议/网络互联协议(Transmission Control Protocol/Internet Protocol)的简称。早期的TCP/IP模型是四层结构。在后来的使用中,借鉴OSI七层参考模型,将网络接口层划分为物理层和数据链路层,形成新的五层结构。

软件结构

C/S结构 :全称为Client/Server结构,是指客户端和服务器结构。常见程序有QQ、迅雷等软件。

B/S结构 :全称为Browser/Server结构,是指浏览器和服务器结构。常见浏览器有谷歌、火狐等。

两种架构各有优势,但是无论哪种架构,都离不开网络的支持。网络编程,就是在一定的协议下,实现两台计算机的通信的程序。

网络通信协议

网络通信协议:通过计算机网络可以使多台计算机实现连接,位于同一个网络中的计算机在进行连接和通信时需要遵守一定的规则,这就好比在道路中行驶的汽车一定要遵守交通规则一样。在计算机网络中,这些连接和通信的规则被称为网络通信协议,它对数据的传输格式、传输速率、传输步骤等做了统一规定,通信双方必须同时遵守才能完成数据交换。

TCP/IP协议: 传输控制协议/因特网互联协议( Transmission Control Protocol/Internet Protocol),是Internet最基本、最广泛的协议。它定义了计算机如何连入因特网,以及数据如何在它们之间传输的标准。它的内部包含一系列的用于处理数据通信的协议,并采用了4层的分层模型,每一层都呼叫它的下一层所提供的协议来完成自己的需求。

上图中,TCP/IP协议中的四层分别是应用层、传输层、网络层和链路层,每层分别负责不同的通信功能。

  • 链路层:链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如针对光纤、网线提供的驱动。

  • 网络层:网络层是整个TCP/IP协议的核心,它主要用于将传输的数据进行分组,将分组数据发送到目标计算机或者网络。

  • 运输层:主要使网络程序进行通信,在进行网络通信时,可以采用TCP协议,也可以采用UDP协议。

  • 应用层:主要负责应用程序的协议,例如HTTP协议、FTP协议等。

协议分类

通信的协议还是比较复杂的,java.net 包中包含的类和接口,它们提供低层次的通信细节。我们可以直接使用这些类和接口,来专注于网络程序开发,而不用考虑通信的细节。java.net 包中提供了两种常见的网络协议的支持。

UDP

UDP:用户数据报协议(User Datagram Protocol)。UDP是无连接通信协议,即在数据传输时,数据的发送端和接收端不建立逻辑连接。简单来说,当一台计算机向另外一台计算机发送数据时,发送端不会确认接收端是否存在,就会发出数据,同样接收端在收到数据时,也不会向发送端反馈是否收到数据。

由于使用UDP协议消耗资源小,通信效率高,所以通常都会用于音频、视频和普通数据的传输例如视频会议都使用UDP协议,因为这种情况即使偶尔丢失一两个数据包,也不会对接收结果产生太大影响。

但是在使用UDP协议传送数据时,由于UDP的面向无连接性,不能保证数据的完整性,因此在传输重要数据时不建议使用UDP协议。

特点:数据被限制在64kb以内,超出这个范围就不能发送了。

数据报(Datagram):网络传输的基本单位。

TCP

TCP:传输控制协议 (Transmission Control Protocol)。TCP协议是面向连接的通信协议,即传输数据之前,在发送端和接收端建立逻辑连接,然后再传输数据,它提供了两台计算机之间可靠无差错的数据传输。

在TCP连接中必须要明确客户端与服务器端,由客户端向服务端发出连接请求,每次连接的创建都需要经过“三次握手”。

三次握手:TCP协议中,在发送数据的准备阶段,客户端与服务器之间的三次交互,以保证连接的可靠。

  • 第一次握手,客户端向服务器端发出连接请求,等待服务器确认。

  • 第二次握手,服务器端向客户端回送一个响应,通知客户端收到了连接请求。

  • 第三次握手,客户端再次向服务器端发送确认信息,确认连接。整个交互过程如下图所示。

完成三次握手,连接建立后,客户端和服务器就可以开始进行数据传输了。由于这种面向连接的特性,TCP协议可以保证传输数据的安全,所以应用十分广泛,例如下载文件、浏览网页等。

网络编程三要素

网络的三要素为:协议、IP地址、端口号。

协议:计算机网络通信必须遵守的规则,已经介绍过了,不再赘述。

IP地址:指互联网协议地址(Internet Protocol Address),俗称IP。IP地址用来给一个网络中的计算机设备做唯一的编号。假如我们把“个人电脑”比作“一台电话”的话,那么“IP地址”就相当于“电话号码”。

IP地址分类

IPv4:是一个32位的二进制数,通常被分为4个字节,如:00001010000000000000000000000001。

由于二进制形式表示的IP地址非常不便记忆和处理,因此通常会将IP地址写成十进制的形式,每个字节用一个十进制数字(0-255)表示,数字间用符号“.”分开,如 “192.168.1.100”。例如192.168.65.100 。其中a、b、c、d都是0~255之间的十进制整数,那么最多可以表示42亿个。

IPv6:随着计算机网络规模的不断扩大,对IP地址的需求也越来越多,IPV4这种用4个字节表示的IP地址面临枯竭,因此IPv6 便应运而生了,IPv6使用16个字节表示IP地址,采用128位地址长度,每16个字节一组,分成8组十六进制数,表示成ABCD:EF01:2345:6789:ABCD:EF01:2345:6789它所拥有的地址容量约是IPv4的8×1028倍,达到2128个(算上全零的),这样就解决了网络地址资源数量不够的问题。

常用命令

  • 查看本机IP地址,在控制台(win+R输入cmd)输入:
ipconfig
  • 检查网络是否连通,在控制台输入:
ping 空格 IP地址
ping 220.181.57.216

特殊的IP地址

  • 本机IP地址:127.0.0.1localhost

端口号

通过IP地址可以连接到指定计算机,但如果想访问目标计算机中的某个应用程序,还需要指定端口号。在计算机中,不同的应用程序是通过端口号区分的。

如果说IP地址可以唯一标识网络中的设备,那么端口号就可以唯一标识设备中的进程(应用程序)了。

**端口号:用两个字节表示的整数,它的取值范围是065535**。其中,01023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败。

利用协议+IP地址+端口号 三元组合,就可以标识网络中的进程了,那么进程间的通信就可以利用这个标识与其它进程进行交互。

接下来通过一个图例来描述IP地址和端口号的作用,如下图所示。

从上图中可以清楚地看到,位于网络中一台计算机可以通过IP地址去访问另一台计算机,并通过端口号访问目标计算机中的某个应用程序。

常见的端口号:

  • 80端口:网络端口

  • 数据库:mysql:3306,oracle:1521

  • tomcat服务器:8080

Socket简介

Socket概述

Java最初是作为网络编程语言出现的,它对网络的高度支持,使得客户端和服务器流畅的沟通变为现实。而在网络编程中,使用最多的就是Socket,每一个实用的网络程序都少不了它的参与。

在计算机网络编程技术中,两个进程,或者说两台计算机可以通过一个网络通信连接实现数据的交换,这种通信链路的端点就被称为“套接字”(英文名称也就是Socket),Socket是网络驱动层提供给应用程序的接口或者说是一种机制。

Socket通信原理

信息如何在网络中寻址传递,应用程序并不用关心,只负责准备发送数据和接收数据即可。对于编程人员来说,无需了解Socket底层机制是如何传送数据的,而是直接将数据提交给Socket,Socket会根据应用程序提供的相关信息,通过一系列计算,绑定IP及信息数据,将数据交给驱动程序向网络上发送出去。

数据发送过程:

Socket的底层机制非常复杂,Java平台提供了一些虽然简单但是相当强大的类,可以更简单有效地使用Socket开发通信程序而无须了解底层机制。

java.net包

java.net包提供了若干支持基于套接字的客户端/服务器通信的类。

java.net包中常用的类有Socket、ServerSocket、DatagramPacket、DatagramSocket、InetAddress、URL、URLConnection和URLEncoder等。

为了监听客户端的连接请求,可以使用ServerSocket类。Socket类实现用于网络上进程间通信的套接字。DatagramSocket类使用UDP协议实现客户端和服务器套接字。DatagramPacket 类使用 DatagramSocket类的对象封装设置和收到的数据报。InetAddress类表示Internet地址。在创建数据报报文和Socket对象时,可以使用InetAddress类。

InetAddress

JDK中提供了一个InetAdderss类,位于java.net包下该类用于封装一个IP地址,并提供了一系列与IP地址相关的方法,下面列出了InetAddress类的一些常用方法。

static InetAddress getByName(String host)   在给定主机名的情况下确定主机的IP地址
static InetAddress getLocalHost()   返回本地主机
String getHostName() 获取此IP地址的主机名
String getHostAddress() 返回IP地址字符串(以文本表现形式)    

上面列举了InetAddress的四个常用方法。其中,前两个方法用于获得该类的实例对象,第一个方法用于获得表示指定主机的InetAddress对象,第二个方法用于获得表示本地的InetAddress对象。通过InetAddress对象便可获取指定主机名,IP地址等。通过一个案例来演示InetAddress的常用方法,如下所示。

public static void main(String[] args) throws Exception {
	InetAddress local = InetAddress.getLocalHost();
    InetAddress remote = InetAddress.getByName("www.baidu.com");//百度的IP地址
	System.out.println("本机的IP地址:" + local.getHostAddress());//本机的IP地址:192.168.1.107
	System.out.println("百度的IP地址:" + remote.getHostAddress());//百度的IP地址:182.61.200.7
	System.out.println("百度的主机名为:" + remote.getHostName());//百度的主机名为:www.baidu.com
}

UDP通信

DatagramPacket

前面介绍了UDP是一种面向无连接的协议,因此,在通信时发送端和接收端不用建立连接。UDP通信的过程就像是货运公司在两个码头间发送货物一样。在码头发送和接收货物时都需要使用集装箱来装载货物,UDP通信也是一样,发送和接收的数据也需要使用“集装箱”进行打包,为此JDK中提供了一个DatagramPacket类,该类的实例对象就相当于一个集装箱,用于封装UDP通信中发送或者接收的数据。

想要创建一个DatagramPacket对象,首先需要了解一下它的构造方法。在创建发送端和接收端的DatagramPacket对象时,使用的构造方法有所不同,接收端的构造方法只需要接收一个字节数组来存放接收到的数据,而发送端的构造方法不但要接收存放了发送数据的字节数组,还需要指定发送端IP地址和端口号。

DatagramPacket的构造方法

//构造DatagramPacket,用来接收长度为length的数据包
DatagramPacket(byte[] buf,int length)

使用该构造方法在创建DatagramPacket对象时,指定了封装数据的字节数组和数据的大小,没有指定IP地址和端口号。很明显,这样的对象只能用于接收端,不能用于发送端。因为发送端一定要明确指出数据的目的地(ip地址和端口号),而接收端不需要明确知道数据的来源,只需要接收到数据即可。

//构造数据报包,用来将长度为length的包发送到指定主机上的指定端口号
DatagramPacket(byte[] buf,int length,InetAddress address,int port)

数据的字节数组和数据的大小,还指定了数据包的目标IP地址(addr)和端口号(port)。该对象通常用于发送端,因为在发送数据时必须指定接收端的IP地址和端口号,就好像发送货物的集装箱上面必须标明接收人的地址一样。

DatagramPacket类中的常用方法

InetAddress getAddress()//返回某台机器的IP地址,此数据报将要发往该机器或者从该机器接收到的
int getPort()//返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的
byte[] getData()//返回数据缓冲区
int getLength()//返回将要发送或接收到的数据的长度    

DatagramSocket

DatagramPacket数据包的作用就如同是“集装箱”,可以将发送端或者接收端的数据封装起来。然而运输货物只有“集装箱”是不够的,还需要有码头。在程序中需要实现通信只有DatagramPacket数据包也同样不行,为此JDK中提供的一个DatagramSocket类。DatagramSocket类的作用就类似于码头,使用这个类的实例对象就可以发送和接收DatagramPacket数据包,发送数据的过程如下图所示。

在创建发送端和接收端的DatagramSocket对象时,使用的构造方法也有所不同。

DatagramSocket类中常用的构造方法

DatagramSocket()//构造数据报套接字并将其绑定到本地主机上任何可用的端口

该构造方法用于创建发送端的DatagramSocket对象,在创建DatagramSocket对象时,并没有指定端口号,此时,系统会分配一个没有被其它网络程序所使用的端口号。

DatagramSocket(int port)//创建数据报套接字并将其绑定到主机上的指定端口

该构造方法既可用于创建接收端的DatagramSocket对象,又可以创建发送端的DatagramSocket对象,在创建接收端的DatagramSocket对象时,必须要指定一个端口号,这样就可以监听指定的端口。

DatagramSocket类中的常用方法

void receive(DatagramPacket p)//从此套接字接收数据报包
void send(DatagramPacket p)//从此套接字发送数据报包    

UDP网络程序

下图为UDP发送端与接收端交互图解:

要实现UDP通信需要创建一个发送端程序和一个接收端程序,很明显,在通信时只有接收端程序先运行,才能避免因发送端发送的数据无法接收,而造成数据丢失。

UDP完成数据的发送:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/*
* 发送端
 * 1,创建DatagramSocket对象
 * 2,创建DatagramPacket对象,并封装数据
 * 3,发送数据
 * 4,释放流资源
 */
public class UDPSend {
	public static void main(String[] args) throws IOException {
		//1,创建DatagramSocket对象
		DatagramSocket sendSocket = new DatagramSocket();
		//2,创建DatagramPacket对象,并封装数据
		//public DatagramPacket(byte[] buf, int length, InetAddress address,  int port)
		//构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
		byte[] buffer = "hello,UDP".getBytes();
		DatagramPacket dp = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("127.0.0.1"), 12306);
		//3,发送数据
		//public void send(DatagramPacket p) 从此套接字发送数据报包
		sendSocket.send(dp);
		//4,释放流资源
		sendSocket.close();
	}
}

UDP完成数据的接收:

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/*
 * UDP接收端
 * 
 * 1,创建DatagramSocket对象
 * 2,创建DatagramPacket对象
 * 3,接收数据存储到DatagramPacket对象中
 * 4,获取DatagramPacket对象的内容
 * 5,释放流资源
 */
public class UDPReceive {
    public static void main(String[] args) throws IOException {
        //1,创建DatagramSocket对象,并指定端口号
        DatagramSocket receiveSocket = new DatagramSocket(12306);
        //2,创建DatagramPacket对象, 创建一个空的仓库
        byte[] buffer = new byte[1024];
        DatagramPacket dp = new DatagramPacket(buffer, 1024);
        //3,接收数据存储到DatagramPacket对象中
        receiveSocket.receive(dp);
        //4,获取DatagramPacket对象的内容
        //谁发来的数据  getAddress()
        InetAddress ipAddress = dp.getAddress();
        String ip = ipAddress.getHostAddress();//获取到了IP地址
        //发来了什么数据  getData()
        byte[] data = dp.getData();
        //发来了多少数据 getLenth()
        int length = dp.getLength();
        //显示收到的数据
        String dataStr = new String(data,0,length);
        System.out.println("IP地址:"+ip+ "数据是"+ dataStr);
        //5,释放流资源
        receiveSocket.close();
    }
}

TCP通信

TCP通信同UDP通信一样,都能实现两台计算机之间的通信,通信的两端都需要创建socket对象。

区别在于,UDP中只有发送端和接收端,不区分客户端与服务器端,计算机之间可以任意地发送数据。

而TCP通信是严格区分客户端与服务器端的,在通信时,必须先由客户端去连接服务器端才能实现通信,服务器端不可以主动连接客户端,并且服务器端程序需要事先启动,等待客户端的连接

在JDK中提供了两个类用于实现TCP程序,一个是ServerSocket类,用于表示服务器端,一个是Socket类,用于表示客户端。

通信时,首先创建代表服务器端的ServerSocket对象,该对象相当于开启一个服务,并等待客户端的连接,然后创建代表客户端的Socket对象向服务器端发出连接请求,服务器端响应请求,两者建立连接开始通信。

ServerSocket

在开发TCP程序时,首先需要创建服务器端程序。JDK的java.net包中提供了一个ServerSocket类,该类的实例对象可以实现一个服务器段的程序。

ServerSocket的构造方法

ServerSocket(int port)//创建绑定到特定端口的服务器套接字

使用该构造方法在创建ServerSocket对象时,就可以将其绑定到一个指定的端口号上(参数port就是端口号)。

ServerSocket的常用方法

Socket accept()//侦听并接受到此套接字的连接
InetAddress getInetAddress()//返回此服务器套接字的本地地址    

ServerSocket对象负责监听某台计算机的某个端口号,在创建ServerSocket对象后,需要继续调用该对象的accept()方法,接收来自客户端的请求。当执行了accept()方法之后,服务器端程序会发生阻塞,直到客户端发出连接请求,accept()方法才会返回一个Scoket对象用于和客户端实现通信,程序才能继续向下执行。

Socket

ServerSocket对象可以实现服务端程序,但只实现服务器端程序还不能完成通信,此时还需要一个客户端程序与之交互,为此JDK提供了一个Socket类,用于实现TCP客户端程序。

Socket的常用构造方法

//创建一个流套接字并将其连接到指定主机上的指定端口号
Socket(String host,int port)

使用该构造方法在创建Socket对象时,会根据参数去连接在指定地址和端口上运行的服务器程序,其中参数host接收的是一个字符串类型的IP地址。

//创建一个流套接字并将其连接到指定IP地址的指定端口号
Socket(InterAddress address,int port)

该方法在使用上与第二个构造方法类似,参数address用于接收一个InetAddress类型的对象,该对象用于封装一个IP地址。

在以上Socket的构造方法中,最常用的是第一个构造方法。

Socket的常用方法

方法声明 功能描述
int getPort() 该方法返回一个int类型对象,该对象是Socket对象与服务器端连接的端口号
InetAddress getLocalAddress() 该方法用于获取Socket对象绑定的本地IP地址,并将IP地址封装成InetAddress类型的对象返回
void close() 该方法用于关闭Socket连接,结束本次通信。在关闭socket之前,应将与socket相关的所有的输入/输出流全部关闭,这是因为一个良好的程序应该在执行完毕时释放所有的资源
InputStream getInputStream() 该方法返回一个InputStream类型的输入流对象,如果该对象是由服务器端的Socket返回,就用于读取客户端发送的数据,反之,用于读取服务器端发送的数据
OutputStream getOutputStream() 该方法返回一个OutputStream类型的输出流对象,如果该对象是由服务器端的Socket返回,就用于向客户端发送数据,反之,用于向服务器端发送数据

在Socket类的常用方法中,getInputStream()和getOutStream()方法分别用于获取输入流和输出流。当客户端和服务端建立连接后,数据是以IO流的形式进行交互的,从而实现通信。

通过一张图来描述服务器端和客户端的数据传输,如下图所示。

简单的TCP网络程序

TCP通信的案例

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/*
  TCP通信的客户端:向服务器发送连接请求,给服务器发送数据,读取服务器回写的数据
  表示客户端的类:
      java.net.Socket:此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
      套接字:包含了IP地址和端口号的网络单位

  构造方法:
      Socket(String host, int port) 创建一个流套接字并将其连接到指定主机上的指定端口号。
      参数:
          String host:服务器主机的名称/服务器的IP地址
          int port:服务器的端口号

  成员方法:
      OutputStream getOutputStream() 返回此套接字的输出流。
      InputStream getInputStream() 返回此套接字的输入流。
      void close() 关闭此套接字。

  实现步骤:
      1.创建一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号
      2.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
      3.使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
      4.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
      5.使用网络字节输入流InputStream对象中的方法read,读取服务器回写的数据
      6.释放资源(Socket)
  注意:
      1.客户端和服务器端进行交互,必须使用Socket中提供的网络流,不能使用自己创建的流对象
      2.当我们创建客户端对象Socket的时候,就会去请求服务器和服务器经过3次握手建立连接通路
        这时如果服务器没有启动,那么就会抛出异常ConnectException: Connection refused: connect
        如果服务器已经启动,那么就可以进行交互了
 */
public class TCPClient {
    public static void main(String[] args) throws IOException {
        //1.创建一个客户端对象Socket,构造方法绑定服务器的IP地址和端口号
        Socket socket = new Socket("127.0.0.1", 8888);
        //2.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
        OutputStream os = socket.getOutputStream();
        //3.使用网络字节输出流OutputStream对象中的方法write,给服务器发送数据
        os.write("你好服务器".getBytes());

        //4.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
        InputStream is = socket.getInputStream();

        //5.使用网络字节输入流InputStream对象中的方法read,读取服务器回写的数据
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);
        System.out.println(new String(bytes, 0, len));

        //6.释放资源(Socket)
        socket.close();
    }
}
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/*
 TCP通信的服务器端:接收客户端的请求,读取客户端发送的数据,给客户端回写数据
 表示服务器的类:
    java.net.ServerSocket:此类实现服务器套接字。

 构造方法:
    ServerSocket(int port) 创建绑定到特定端口的服务器套接字。

 服务器端必须明确一件事情,必须的知道是哪个客户端请求的服务器
 所以可以使用accept方法获取到请求的客户端对象Socket
 成员方法:
     Socket accept() 侦听并接受到此套接字的连接。

 服务器的实现步骤:
    1.创建服务器ServerSocket对象和系统要指定的端口号
    2.使用ServerSocket对象中的方法accept,获取到请求的客户端对象Socket
    3.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
    4.使用网络字节输入流InputStream对象中的方法read,读取客户端发送的数据
    5.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
    6.使用网络字节输出流OutputStream对象中的方法write,给客户端回写数据
    7.释放资源(Socket,ServerSocket)
*/
public class TCPServer {
    public static void main(String[] args) throws IOException {
        //1.创建服务器ServerSocket对象和系统要指定的端口号
        ServerSocket server = new ServerSocket(8888);
        //2.使用ServerSocket对象中的方法accept,获取到请求的客户端对象Socket
        Socket socket = server.accept();
        //3.使用Socket对象中的方法getInputStream()获取网络字节输入流InputStream对象
        InputStream is = socket.getInputStream();
        //4.使用网络字节输入流InputStream对象中的方法read,读取客户端发送的数据
        byte[] bytes = new byte[1024];
        int len = is.read(bytes);
        System.out.println(new String(bytes,0,len));
        //5.使用Socket对象中的方法getOutputStream()获取网络字节输出流OutputStream对象
        OutputStream os = socket.getOutputStream();
        //6.使用网络字节输出流OutputStream对象中的方法write,给客户端回写数据
        os.write("收到谢谢".getBytes());
        //7.释放资源(Socket,ServerSocket)
        socket.close();
        server.close();
    }
}

文件上传案例

客户端:读取本地文件,上传到服务器,读取服务器回写的数据。

服务器端:读取客户端上传的文件,保存到服务器的硬盘,给客户端回写"上传成功"。

 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.Socket;
 /*
     文件上传案例的客户端:读取本地文件,上传到服务器,读取服务器回写的数据

     明确:
         数据源:c:\\1.jpg
         目的地:服务器

     实现步骤:
         1.创建一个本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
         2.创建一个客户端Socket对象,构造方法中绑定服务器的IP地址和端口号
         3.使用Socket中的方法getOutputStream,获取网络字节输出流OutputStream对象
         4.使用本地字节输入流FileInputStream对象中的方法read,读取本地文件
         5.使用网络字节输出流OutputStream对象中的方法write,把读取到的文件上传到服务器
         6.使用Socket中的方法getInputStream,获取网络字节输入流InputStream对象
         7.使用网络字节输入流InputStream对象中的方法read读取服务回写的数据
         8.释放资源(FileInputStream,Socket)
  */
 public class TCPClient {
     public static void main(String[] args) throws IOException {
         //1.创建一个本地字节输入流FileInputStream对象,构造方法中绑定要读取的数据源
         FileInputStream fis = new FileInputStream("c:\\1.jpg");
         //2.创建一个客户端Socket对象,构造方法中绑定服务器的IP地址和端口号
         Socket socket = new Socket("127.0.0.1",8888);
         //3.使用Socket中的方法getOutputStream,获取网络字节输出流OutputStream对象
         OutputStream os = socket.getOutputStream();
         //4.使用本地字节输入流FileInputStream对象中的方法read,读取本地文件
         int len = 0;
         byte[] bytes = new byte[1024];
         while((len = fis.read(bytes))!=-1){
             //5.使用网络字节输出流OutputStream对象中的方法write,把读取到的文件上传到服务器
             os.write(bytes,0,len);
         }

         /*
             解决:上传完文件,给服务器写一个结束标记
             void shutdownOutput() 禁用此套接字的输出流。
             对于 TCP 套接字,任何以前写入的数据都将被发送,并且后跟 TCP 的正常连接终止序列。
          */
         socket.shutdownOutput();

         //6.使用Socket中的方法getInputStream,获取网络字节输入流InputStream对象
         InputStream is = socket.getInputStream();

         //7.使用网络字节输入流InputStream对象中的方法read读取服务回写的数据
         while((len = is.read(bytes))!=-1){
             System.out.println(new String(bytes,0,len));
         }

         //8.释放资源(FileInputStream,Socket)
         fis.close();
         socket.close();
     }
 }
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Random;

/*
文件上传案例服务器端:读取客户端上传的文件,保存到服务器的硬盘,给客户端回写"上传成功"
明确:
   数据源:客户端上传的文件
   目的地:服务器的硬盘 d:\\upload\\1.jpg
实现步骤:
  1.创建一个服务器ServerSocket对象,和系统要指定的端口号
  2.使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象
  3.使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
  4.判断d:\\upload文件夹是否存在,不存在则创建
  5.创建一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
  6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件
  7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
  8.使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象
  9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功"
  10.释放资源(FileOutputStream,Socket,ServerSocket)
*/
public class TCPServer {
    public static void main(String[] args) throws IOException {
        //1.创建一个服务器ServerSocket对象,和系统要指定的端口号
        ServerSocket server = new ServerSocket(8888);
        //2.使用ServerSocket对象中的方法accept,获取到请求的客户端Socket对象

        /*
            让服务器一直处于监听状态(死循环accept方法)
            有一个客户端上传文件,就保存一个文件
         */
        while(true){
            Socket socket = server.accept();

            /*
                使用多线程技术,提高程序的效率
                有一个客户端上传文件,就开启一个线程,完成文件的上传
             */
            new Thread(new Runnable() {
                //完成文件的上传
                @Override
                public void run() {
                   try {
                       //3.使用Socket对象中的方法getInputStream,获取到网络字节输入流InputStream对象
                       InputStream is = socket.getInputStream();
                       //4.判断d:\\upload文件夹是否存在,不存在则创建
                       File file =  new File("d:\\upload");
                       if(!file.exists()){
                           file.mkdirs();
                       }

                    /*
                       自定义一个文件的命名规则:防止同名的文件被覆盖
                       规则:域名+毫秒值+随机数
                     */
                       String fileName = "coydone"+System.currentTimeMillis()+new Random().nextInt(999999)+".jpg";

                       //5.创建一个本地字节输出流FileOutputStream对象,构造方法中绑定要输出的目的地
                       //FileOutputStream fos = new FileOutputStream(file+"\\1.jpg");
                       FileOutputStream fos = new FileOutputStream(file+"\\"+fileName);
                       //6.使用网络字节输入流InputStream对象中的方法read,读取客户端上传的文件

                       int len =0;
                       byte[] bytes = new byte[1024];
                       while((len = is.read(bytes))!=-1){
                           //7.使用本地字节输出流FileOutputStream对象中的方法write,把读取到的文件保存到服务器的硬盘上
                           fos.write(bytes,0,len);
                       }

                       //8.使用Socket对象中的方法getOutputStream,获取到网络字节输出流OutputStream对象
                       //9.使用网络字节输出流OutputStream对象中的方法write,给客户端回写"上传成功"
                       socket.getOutputStream().write("上传成功".getBytes());
                       //10.释放资源(FileOutputStream,Socket,ServerSocket)
                       fos.close();
                       socket.close();
                   }catch (IOException e){
                       System.out.println(e);
                   }
                }
            }).start();
        }
        //服务器不用关闭
        //server.close();
    }
}
0

评论区