黑马程序员_网络篇_重在理解
来源:互联网 发布:mysql数据库注释 编辑:程序博客网 时间:2024/05/11 05:30
1、网络模型:OSI参考模型和TCP/IP参考模型
图示:
一般来说开发处于传输层和网际层,应用层为:FTP和HTTP协议等,传输层为:UDP和TCP等,网际层为:IP。
通常用户操作的是应用层,而编程人员需要做的是传输层和网际层,用户在应用层操作的数据,经过逐层封包,最后到物理层发送到另一个模型中,再进行逐层解包,图示为:
2、网络通信三要素:IP地址,端口号,传输协议
A、IP地址
a、它是网络中的设备标识
b、不易记忆,可用主机名表示,两者存在映射关系
c、本机回环地址:127.0.0.1,主机名为:localhost。
IP地址:java中对应的是InetAddress类,存在于java.net包中。
InetAddress类:
(一)无构造函数,可通过getLocalHost()方法获取InetAddress对象,此方法是静态的,返回本类对象。
InetAddress i = InetAddress.getLocalHost();
(二)方法:
1)static InetAddress getByName(String host):获取指定主机的IP和主机名。(最好用ip地址去获取,主机名需要解析)
2)static InetAddress[] getAllByName(String host):在给定主机名的情况下,根据系统上配置的名称服务返回IP地址所组成的数组。返回对象不唯一时,用此方法。
3)String getHostAddress():返回IP地址字符串文本形式,以IP地址为主。
4)String getHostName():返回IP地址主机名。
(三)如何获取任意一台主机的IP地址对象:
1)功能:返回InetAddress对象
2)对于任意主机,需要指定传入主机名的参数
注意:如果IP地址和对应的主机名,这种映射关系没有在网络上,就不会解析成功,返回的还是指定的IP。
示例:
- <span style="font-size:14px;">/*InetAddress练习*/
- import java.net.*;
- class InetAddressDemo
- {
- public static void main(String[] args)
- {
- try
- {
- //练习一、获取本机信息
- //通过getLocalHost("String");获取本地主机
- InetAddress inet = InetAddress.getLocalHost();
- //如果找不到 host 的任何 IP 地址。抛出UnknownHostException异常
- //通过InetAddress对象获取本地主机IP
- sop(inet.getHostAddress());
- //通过InetAddress对象获取本地主机名
- sop(inet.getHostName());
- //练习二、指定IP获取信息
- /*
- static InetAddress getByName(String host)
- 在给定主机名的情况下确定主机的 IP 地址。
- */
- /*
- InetAddress inet1 = InetAddress.getByName("196.168.1.254");
- sop("inet1="+inet1.getHostAddress());
- sop("inet1="+inet1.getHostName());
- */
- InetAddress inet1 = InetAddress.getByName("www.baidu.com");
- sop("inet1="+inet1.getHostAddress());
- sop("inet1="+inet1.getHostName());
- //练习三、指定IP获取信息
- /*
- static InetAddress[] getAllByName(String host)
- 在给定主机名的情况下,根据系统上配置的名称服务返回其 IP 地址
- 所组成的数组。
- */
- InetAddress [] inet2 = InetAddress.getAllByName("www.baidu.com");
- for(InetAddress in:inet2){
- sop("inet2="+in.getHostAddress());
- sop("inet2="+in.getHostName());
- }
- }
- catch (UnknownHostException e)
- {
- }
- }
- public static void sop(Object obj){
- System.out.println(obj);
- }
- }</span>
B、端口号:
a、用于标识进程的逻辑地址,不用进程的标识。
b、有效端口:0 ~65535,系统使用或保留的端口是:0~ 1024。
C、传输协议:
即通信规则,包含TCP和UDP协议
UDP
是面向无连接,明确了对方的端口,无论在不在网上,只管传输,不在就会丢失数据。只求速度,应用于网络视频会议和聊天等应用程序中。
协议特点:
a、面向无连接,即将数据及源和目的封装成数据包中,不建立链接的发送
b、每个数据包的大小限制在64K之内
c、因无连接,是不可靠的协议
d、不建立连接,速度快。
TCP
是面向连接的,必须连接成功才能传输数据,应用于下载等程序上
协议特点:
a、面向连接,在建立连接后,形成传输数据的通道
b、在连接中进行大数据量的传输
c、通过三次握手完成连接,是可靠的协议
d、必须建立连接,效率稍慢
三次握手:第一次本方发送请求,第二次对方确认连接,第三次本方再次确认连接成功。
3、通信的步骤:
1)找到IP地址
2)数据要发送到对象指定应用程序,为标识这些应用程序,所以给这些网络应用程序都用数字标识,为方便称呼这个数字,叫做端口,即逻辑端口。
3)定义通信规则,称之为协议。国际组织定义了通用协议,即TCP/IP。
注意:必须要有数字标识才能将数据发送到应用程序上。
传输协议
一、Socket
1、它被称之为插座,相当于港口一样,是网络服务提供的一种机制。
2、通信两端都要有Socket,才能建立服务。
3、网络通信其实就是Socket间的通信,数据在两个Socket间通过IO传输。
二、UDP传输
1、通过类DatagramSocket,此类表示用发送和接收数据包的套接字,即Socket。
2、方法:
1)创建 UDPSocket发送服务对象:
DatagramSocket(),不指定端口。DatagramSocket(int port),指定端口。
2)发送:void send(DatagramPacket p)
3)接收:void receive(DatagramPacket p)
其中DatagramPacket:数据报包用来实现无连接包投递服务的,每条报文仅根据该包中包含的信息从一台机器路由到另一台机器中。凡是带地址(InetAddress)的都是用于发送包的。
3、步骤
1)发送数据:
a、建立UDPSocket服务,在此无需指定端口,也可以将端口加入。如果不指定的话,系统会随机分配一个端口,如第一次运行时端口为1093,那么第二次就会顺延为1094,再运行会一直顺延,因为之前的端口还没有得到释放,所以会顺延端口号值。
b、提供数据,并将数据封装到数据包中
c、通过socket服务的发送功能,将数据包发送出去
d、关闭资源
2)接收数据:
a、定义UDPSocket服务。通常会监听一个端口,其实就是给这个接收网路应用程序定义数字标识,方便于明确哪些数据过来该应用程序可以处理。
b、定义一个数据包,用来存储接收到的字节数据,因为数据包对象中有更多功能可以提取字节数据中的不同数据信息。
c、通过socket服务的receive方法接收到的数据存入已定义好的数据包中
d、通过数据包对象的特有功能,将这些不同的数据取出,打印在控制台上
e、关闭资源
在定义接收数据的方法中,仍会在DatagramSocket构造函数中传入DatagramPacket的参数,这是因为收到的数据太多,需要解析,通过将数据封装成对象,易于解析,所以需要传入参数。
注意:
1、发送端与接收端是两个独立的运行程序。
2、在发送端,要在数据包对象中明确目的地IP及端口。
3、在接收端,要指定监听的端口。
示例:练习一、接收端接收发送端键盘录入内容,并打印
- <span style="font-size:14px;"><span style="font-size:14px;">/*UDP传输
- */
- /*发送端
- 步骤:1、建立UDP的socket服务,也就是端点
- 2、将要提供的数据封装到数据包中
- 3、通过socket服务的发送功能将数据发出
- 4、关闭资源
- */
- import java.net.*;
- class DatagramSocketSend
- {
- public static void main(String[] args) throws Exception
- {
- //创建UDP的socket服务
- DatagramSocket out = new DatagramSocket();
- //要发送的数据
- byte [] data = "UDP come to Be".getBytes();
- //建立数据包,实现无线投递服务
- DatagramPacket pack =
- new DatagramPacket(data,data.length,InetAddress.getByName("192.168.1.255"),10010);
- //通过socket服务的sent方法将数据发出
- out.send(pack);
- //关闭资源
- out.close();
- }
- }
- /*接收端
- 步骤:1、创建UDP接收端服务,并指定端口号
- 2、定义一个数据包,用来存储接收到的字节数据,
- 因为数据包对象中有更多功能可以提取字节数据中的不同数据信息
- 3、通过Socket服务的receive方法将接收到的数据存入已定义好的数据包中
- 4、使用数据包的特有功能,对这些不同的数据取出,并打印到控制台
- 5、关闭资源
- */
- class DatagramSocketReceive
- {
- public static void main(String[] args) throws Exception
- {
- //创建socket服务,并指定端口号为10000,方便测试
- DatagramSocket ds = new DatagramSocket(10010);
- while(true){
- //定义一个数据包,用于存储接收到的数据
- byte [] buff = new byte[1024];
- DatagramPacket pack = new DatagramPacket(buff,buff.length);
- //通过socket服务的receive方法将接收到的数据存入数据包
- ds.receive(pack); //该方法是阻塞状态
- //获取信息
- //获取发送端IP
- System.out.println(pack.getAddress().getHostAddress());
- //获取发送端端口号
- System.out.println(pack.getPort());
- //获取发送端发送的数据的长度
- System.out.println(pack.getLength());
- //获取发送端发送的数据
- System.out.println(new String(pack.getData(),0,pack.getLength()));
- }
- }
- }</span></span>
练习二、
- <span style="font-size:14px;"><span style="font-size:14px;">/*
- 需求:把键盘输入的数据,在接收端显示
- */
- /*
- 发送端:
- 步骤:
- 1、创建UDP的socket服务
- 2、定义字符输出流,读取键盘输入的数据
- 3、定义数组保存键盘输入数据
- 4、使用socket服务的send方法将数据发出
- 5、关闭资源
- */
- import java.io.*;
- import java.net.*;
- class DatagramSystemSend
- {
- public static void main(String[] args) throws Exception
- {
- //1、
- DatagramSocket out = new DatagramSocket();
- //2、
- BufferedReader bufr =
- new BufferedReader(new InputStreamReader(System.in));
- //3、
- String line = "";
- while((line=bufr.readLine())!=null){
- if("over".equals(line)){
- line = "发送端发送完毕!!!";
- //4
- send(line,out);
- break;
- }
- else
- send(line,out);
- }
- //5
- out.close();
- bufr.close();
- }
- public static void send(String line,DatagramSocket out)throws IOException{
- byte [] data = line.getBytes();
- DatagramPacket pack =
- new DatagramPacket(data,data.length,InetAddress.getByName("192.168.1.255"),10005);
- out.send(pack);
- }
- }
- /*
- 接收端:
- 步骤:
- 1、创建UDP的socket服务
- 2、定义数据包用于保存从发送端读取到的数据
- 3、使用socket的receive方法将读取到的数据保存到数据包
- 4、使用数据包的功能取出数据,并打印在控制台
- 5、关闭资源
- */
- class DatagramSystemReceive
- {
- public static void main(String[] args) throws Exception
- {
- //1、
- DatagramSocket in = new DatagramSocket(10005);
- while(true){
- //2、
- byte [] data = new byte[1024];
- DatagramPacket pack = new DatagramPacket(data,data.length);
- //3、
- in.receive(pack);
- System.out.println(data.length);
- //4、
- //获取发送端发送数据的长度
- int length = pack.getLength();
- //获取发送端IP
- String address = pack.getAddress().getHostAddress();
- //获取发送端端口号
- int port = pack.getPort();
- //获取发送端数据
- String str = new String(pack.getData(),0,length);
- System.out.println(address+"......."+port+"。。。。。"+length);
- System.out.println(str);
- if(str.equals("发送端发送完毕!!!"))
- break;
- }
- in.close();
- }
- }</span></span>
- <span style="font-size:14px;">/*
- 编写一个聊天程序。
- 有收数据的部分,和发数据的部分。
- 这两部分需要同时执行。
- 那就需要用到多线程技术。
- 一个线程控制收,一个线程控制发。
- 因为收和发动作是不一致的,所以要定义两个run方法。
- 而且这个两个方法要封装到不同的类中。
- */
- //Udp发送线程
- import java.net.*;
- import java.io.*;
- class UdpSend implements Runnable
- {
- //定义Socket服务引用
- private DatagramSocket ds;
- UdpSend(DatagramSocket ds)
- {
- this.ds=ds;
- }
- //复写run方法
- public void run()
- {
- try
- {
- //2、确定数据,从键盘录入,并把键盘录入的数据封装成数据包
- DatagramPacket dp=null;
- BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
- String line=null;
- while((line=br.readLine())!=null)
- {
- byte[] buf=line.getBytes();
- dp=new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.255"),10000);
- //3、通过Socket服务,将已有的数据包发送出去
- ds.send(dp);
- if ("886".equals(line))
- {
- break;
- }
- }
- //4、关闭资源
- ds.close();
- }
- catch (Exception e)
- {
- throw new RuntimeException("发送数据失败");
- }
- }
- }
- //Udp接收线程
- class UdpReceive implements Runnable
- {
- //定义Socket服务引用
- private DatagramSocket ds;
- UdpReceive(DatagramSocket ds)
- {
- this.ds=ds;
- }
- //复写run方法
- public void run()
- {
- try
- {
- //一直处于接收状态
- while (true)
- {
- //2、定义数据包,用于存储数据
- byte[] buf=new byte[1024];
- DatagramPacket dp=new DatagramPacket(buf,buf.length);
- //3、通过Socket服务,将数据接收并存储进数据包中
- ds.receive(dp);
- //4、通过数据包的方法获取其中的数据
- String ip=dp.getAddress().getHostAddress();
- String data=new String(dp.getData(),0,dp.getLength());
- int port=dp.getPort();
- System.out.println("IP:"+ip+"=="+data);
- }
- //5、关闭资源
- //ds.close();
- }
- catch (Exception e)
- {
- throw new RuntimeException("接收端接收数据失败");
- }
- }
- }
- class UdpChatDemo
- {
- public static void main(String[] args)throws Exception
- {
- new Thread(new UdpSend(new DatagramSocket())).start();
- new Thread(new UdpReceive(new DatagramSocket(10000))).start();
- }
- }</span>
三、TCP传输
1、TCP分客户端和服务端。客户端对应的对象是Socket,服务端对应的对象是ServerSocket。
2、方法:
1)创建客户端对象:
Socket():创建空参数的客户端对象,一般用于服务端接收数据
Socket(String host,int port),指定要接收的IP地址和端口号
2)创建服务端对象:ServerSocket(int port):指定接收的客户端的端口
3)Socket accept():监听并接受到此套接字的连接
4)void shutdownInput():此套接字的输入流至于“流的末尾”
5)void shutdownOutput():禁用此套接字的输出流
6)InputStream getInputStream():返回此套接字的输入流,Socket对象调用
7)OutputStream getOutputStream():返回套接字的输出流,Socket对象调用
3、基本思路
客户端:
1)客户端需要明确服务器的ip地址以及端口,这样才可以去试着建立连接,如果连接失败,会出现异常。
2)连接成功,说明客户端与服务端建立了通道,那么通过IO流就可以进行数据的传输,而Socket对象已经提供了输入流和输出流对象,通过getInputStream(),getOutputStream()获取即可。
3)与服务端通讯结束后,关闭Socket。
服务端:
1)服务端需要明确它要处理的数据是从哪个端口进入的。
2)当有客户端访问时,要明确是哪个客户端,可通过accept()获取已连接的客户端对象,并通过该对象与客户端通过IO流进行数据传输。
3)当该客户端访问结束,关闭该客户端。
4、步骤
客户端:
通过查阅Socket对象的API文档,发现在该对象在建立时,就可去连接指定主机,因为TCP是面向连接的,所以在建立Socket服务时,就要有服务端存在,并连接成功,形成通路后,再通过该通道进行数据的传输。
1)创建Socket服务,并指定要连接的主机端口。通路一建立,就会产生Socket流(包括输入流和输出流),通过方法获取
2)为了发送数据,应获取Socket中的输出流,如果要接收服务端的反馈信息,还需要获取Socket的输入流
3)通过输出流的write()方法将要发送的数据写入到流中
4)关闭Socket流资源
服务端:
服务器套接字等待请求通过网络传入。它基于该请求执行某些操作,然后可能向请求者返回结果。需监听一个端口。
1)建立服务端的Socket服务,并监听一个端口。通过ServerSocet带端口参数的构造函数
2)获取连接过来的客户对象,通过ServerSocket的accept()方法,此方法是阻塞式的,如果服务端没有连接到就会一直等待
3)客户端如果发过来数据,则服务端要使用对应的客户端对象,并获取到该客户端对象的读取流读取发过来的数据,并输出到指定目的地。
4)关闭服务端(可选)。一般服务端是常开的,因为在实际应用中,随时有客户端在请求连接和服务。但这里需要定时关闭客户端对象流,避免某一个客户端长时间占用服务器端。
示例:
练习二、
- <span style="font-size:14px;">/*TCP传输*/
- /*
- 客户端:
- 1、创建Socket服务,并指定要连接的服务器的Ip和端口号
- 2、向服务端发送数据
- 3、关闭资源
- */
- import java.net.*;
- import java.io.*;
- class SocketDemo
- {
- public static void main(String[] args) throws Exception
- {
- Socket s = new Socket("127.0.0.1",10000);
- OutputStream out = s.getOutputStream();
- out.write("TCP come by me".getBytes());
- s.close();
- }
- }
- /*
- 服务端:
- 1、创建Socket服务,并指定端口号
- 2、使用accept方法得到Socket连接过来的客户端对象
- 3、通过客户端对象获取客户端发送过来的数据,并打印在控制台
- 4、关闭资源(可选)
- */
- class ServerSocketDemo
- {
- public static void main(String[] args) throws Exception
- {
- ServerSocket ss = new ServerSocket(10000);
- Socket s = ss.accept();
- InputStream in = s.getInputStream();
- byte [] data = new byte[1024];
- int len = in.read(data);
- //打印客户端发送的数据
- System.out.println(new String(data,0,len));
- //打印客户端信息IP
- System.out.println(s.getInetAddress().getHostAddress());
- }
- }
- </span>
练习三、
- <span style="font-size:14px;">/*TCP传输*/
- /*
- 客户端:
- 1、创建Socket服务,并指定要连接的服务器的Ip和端口号
- 2、向服务端发送数据
- 3、关闭资源
- */
- import java.net.*;
- import java.io.*;
- class SocketDemo
- {
- public static void main(String[] args) throws Exception
- {
- //创建socket服务,并指定要连接的服务器的IP和端口号
- Socket s = new Socket("127.0.0.1",10000);
- //通过socket服务的getOutputStream方法获取输出流
- OutputStream out = s.getOutputStream();
- //向服务端传输数据
- out.write("TCP 我来了!!!".getBytes());
- //通过socket服务的getInputStream方法获取读取流,得到服务端反馈的信息
- InputStream in = s.getInputStream();
- byte [] data = new byte[1024];
- //读取服务端反馈的信息
- int len = in.read(data);
- System.out.println(new String(data,0,len));
- s.close();
- }
- }
- /*
- 服务端:
- 1、创建Socket服务,并指定端口号
- 2、使用accept方法得到Socket连接过来的客户端对象
- 3、通过客户端对象获取客户端发送过来的数据,并打印在控制台
- 4、关闭资源(可选)
- */
- class ServerSocketDemo
- {
- public static void main(String[] args) throws Exception
- {
- //创建socket服务,并指定端口号
- ServerSocket ss = new ServerSocket(10000);
- //通过accept方法获取socket连接的客户端对象
- Socket s = ss.accept();
- //通过获取读取流得到客户端传输过来的数据
- InputStream in = s.getInputStream();
- byte [] data = new byte[1024];
- //读取客户端传输的数据
- int len = in.read(data);
- //打印客户端发送的数据
- System.out.println(new String(data,0,len));
- //打印客户端信息IP
- System.out.println(s.getInetAddress().getHostAddress());
- //向客户端反馈信息
- OutputStream out = s.getOutputStream();
- out.write("已收到!!!".getBytes());
- }
- }
- </span>
练习四、
- <span style="font-size:14px;">/*
- 练习
- 需求:建立一个文本转换服务器
- 客户端给服务端发送文本,服务端会将文本转成大写再返回给客户端。
- 而且客户端可以不断的进行文本转换。当客户端输入over时,转换结束。
- 分析:
- 客户端:
- 既然是操作设备上的数据,那么就可以使用io技术,并按照io的操作规律来思考。
- 源:键盘录入
- 目的:网络设备,网络输出流。
- 而且操作的是文本数据。可以选择字符流。
- 步骤:
- 1、建立服务
- 2、获取键盘录入
- 3、将数据发给服务端
- 4、获取服务端返回的大写数据
- 5、结束,管资源。
- 都是文本数据,可以使用字符流进行操作,同时提高效率,加入缓冲。
- 此练习出现的问题:
- 现象:客户端和服务端都在莫名的等待。
- 原因:因为客户端和服务端都有阻塞式方法。这些方法没有读到结束标记。那么就一直等。而导致两端都在等待。
- 解决:需要用到刷新和换行的方式将写入和读取的数据从流中刷新到内存中
- 方式一:可用高效缓冲区类的newLine()换行作为结束标记,并用flush()进行刷新。
- 方式二:可用PrintWriter(s.getOutputStrean(),true)创建输出流对象,true作用是刷新,通过打印方法println()换行,“ln”表示换行。
- */
- import java.io.*;
- import java.net.*;
- class TransSocket
- {
- public static void main(String[] args) throws Exception
- {
- //创建Socket服务,并指定要连接的主机的IP和端口号
- Socket s = new Socket("127.0.0.1",10002);
- //创建读取流对象,用于读取键盘录入,这里使用字符读取流
- BufferedReader bufr =
- new BufferedReader(new InputStreamReader(System.in));
- //定义目的,将数据写入到Socket输出流。发给服务端
- // BufferedWriter bufw =
- // new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
- //------------使用PrintWriter替代BufferedWriter--------
- PrintWriter print =
- new PrintWriter(new BufferedWriter(
- new OutputStreamWriter(s.getOutputStream())),true);
- //定义读取流,读取服务端反馈的信息
- BufferedReader readerServer =
- new BufferedReader(new InputStreamReader(s.getInputStream()));
- //读取键盘录入
- String data = null;
- while((data=bufr.readLine())!=null){
- //如果输入over转换结束
- if("over".equals(data))
- break;
- //-----使用打印流的特有方法替代缓冲------
- // bufw.write(data);
- // bufw.newLine();
- // bufw.flush();
- print.println(data);
- //每发送一次,获取一次服务端反馈回来的信息
- System.out.println("服务端反馈信息:"+readerServer.readLine());
- }
- s.close();
- bufr.close();
- }
- }
- class TransServer
- {
- public static void main(String [] args) throws Exception
- {
- //创建ServerSocket服务,并指定端口号
- ServerSocket ss = new ServerSocket(10002);
- //通过accept方法获取客户端对象
- Socket s = ss.accept();
- //显示客户端IP信息
- System.out.println(s.getInetAddress().getHostAddress());
- //获取客户端传输的数据
- BufferedReader bufr =
- new BufferedReader(new InputStreamReader(s.getInputStream()));
- //将客户端发送的文本转换为大写,反馈给客户端
- // BufferedWriter bufw =
- // new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
- //--------------使用打印流 替换 BufferedWriter--------
- PrintWriter print =
- new PrintWriter(new BufferedWriter(
- new OutputStreamWriter(s.getOutputStream())),true);
- String line = null;
- while((line=bufr.readLine())!=null){
- // bufw.write(line.toUpperCase());
- // bufw.newLine();
- // bufw.flush();
- //--------------使用打印流的特有方法 替换 BufferedWriter的方法--------
- print.println(line.toUpperCase());
- }
- }
- }
- </span>
- <span style="font-size:14px;">/*
- 需求:向服务器上传一个文件,服务返回一条信息
- 1、客户端:
- 源:硬盘上的文件;目的:网络设备,即网络输出流。
- 若操作的是文本数据,可选字符流,并加入高效缓冲区。若是媒体文件,用字节流。
- 2、服务端:
- 源:socket读取流;目的:socket输出流。
- 3、出现的问题:
- 现象:
- a、文件已经上传成功了,但是没有得到服务端的反馈信息。
- b、即使得到反馈信息,但得到的是null,而不是“上传成功”的信息
- 原因:
- a、因为客户端将数据发送完毕后,服务端仍然在等待着读取数据,并没有收到结束标记,就会一直等待读取。
- b、上个问题解决后,收到的不是指定信息而是null,是因为服务端写入数据后,需要刷新,才能将信息反馈给客服端。
- 解决:
- 方法一:定义结束标记,先将结束标记发送给服务端,让服务端接收到结束标记,然后再发送上传的数据。但是这样定义可能会发生定义的标记和文件中的数据重复,而导致提前结束。
- 方法二:定义时间戳,由于时间是唯一的,在发送数据前,先获取时间,发送完后在结尾处写上相同的时间戳,在服务端,接收数据前先接收一个时间戳,然后在循环中判断时间戳以结束标记。
- 方法三:通过socket方法中的shutdownOutput(),关闭输入流资源,从而结束传输流,以给定结束标记。通常用这个方法。
- */
- import java.io.*;
- import java.net.*;
- class TextSocket
- {
- public static void main(String[] args) throws Exception
- {
- //创建socket服务
- Socket s = new Socket("127.0.0.1",10003);
- //创建文件对象
- File file = new File("D:\\file.txt");
- //判断文件是否存在
- if(file.exists()){
- //创建本地读取文件流
- BufferedReader bufr =
- new BufferedReader(new FileReader(file));
- //定义目的,将数据写入到socket流,并传输个服务端
- PrintWriter print =
- new PrintWriter(new BufferedWriter(
- new OutputStreamWriter(s.getOutputStream())),true);
- //定义读取流,读取服务端反馈信息
- BufferedReader readerSer =
- new BufferedReader(new InputStreamReader(s.getInputStream()));
- String data = null;
- while((data=bufr.readLine())!=null){
- //向服务端输出信息
- print.println(data);
- }
- //使用socket服务的shutdownOutput方法,标识客户端数据传输已结束
- s.shutdownOutput();
- //打印服务端反馈信息
- System.out.println(readerSer.readLine());
- bufr.close();
- s.close();
- }
- else
- {
- System.out.println("该文件不存在");
- s.close();
- }
- }
- }
- class TextServer
- {
- public static void main(String [] args)throws Exception
- {
- //创建服务
- ServerSocket ss = new ServerSocket(10003);
- //获取客户端对象
- Socket s = ss.accept();
- if(s!=null){
- //打印客户端IP
- System.out.println("IP="+s.getInetAddress().getHostAddress());
- //创建流对象,冲socket流中读取客户端传输的数据
- BufferedReader bufr =
- new BufferedReader(new InputStreamReader(s.getInputStream()));
- //创建输出流,用于给客户端反馈信息
- PrintWriter printSocket =
- new PrintWriter(new BufferedWriter(
- new OutputStreamWriter(s.getOutputStream())),true);
- //创建输出流,将客户端传输的数据打印到本地硬盘
- PrintWriter printText =
- new PrintWriter(new BufferedWriter(
- new FileWriter("d:\\server.txt")),true);
- //读取客户端传输的数据
- String data = null;
- while((data=bufr.readLine())!=null){
- printText.println(data);
- }
- printSocket.println("上传成功!");
- printText.close();
- bufr.close();
- ss.close();
- }
- else{
- System.out.println("没有客户端访问!!!");
- ss.close();
- }
- }
- }</span>
- 黑马程序员_网络篇_重在理解
- 黑马程序员_网络编程理解
- 黑马程序员_网络
- 黑马程序员_网络篇之二_加强篇
- 黑马程序员_字符串理解
- 黑马程序员_异常理解
- 黑马程序员_网络编程
- 黑马程序员_网络编程
- 黑马程序员_网络编程
- 黑马程序员_网络编程
- 黑马程序员_网络编程
- 黑马程序员_网络编程
- 黑马程序员_网络编程
- 黑马程序员_网络编程
- 黑马程序员_网络编程
- 黑马程序员_网络编程
- 黑马程序员_网络编程
- 黑马程序员_网络编程
- dvi转ps和pdf
- 二分图的最大匹配问题
- POJ-2318
- ZeptoLab Code Rush 2015 A. King of Thieves
- 人社卡送检测试项
- 黑马程序员_网络篇_重在理解
- Cocos2d-x_Box2D刚体自定义形状
- 中国天气城市代码编号
- 【Android开发】高级组件-自动完成文本框
- 解决异常:使用genymotion调试程序出现INSTALL_FAILED_CPU_ABI_INCOMPATIBLE
- 23种设计模式(State模式)
- 并查集 总结
- hdu5186 zhx's submissions
- 黑马程序员_网络篇之二_加强篇