java 自学日志 【十六】---网络编程
2013-05-13 15:17
393 查看
------android培训,java培训,期待与您交流------
网络编程:
网络编程是为了实现不同主机间的数据通信,要实现通讯,则要首先知道对方的IP和端口号,同时还要明确双方使用的通信规则(传输协议)。
IP地址,端口号和传输协议就是网络通讯的三要素:
①IP地址:是主机在网络上的地址,在网上的唯一标识,127.0.0.1是本地回环地址,可测试网卡是否损坏;
②端口号:数据要发送到对方指定的应用程序上,为了标识这些应用程序,用数字对其标识,这些数字就是端 口,或被称为逻辑端口;它并无物理实体与之对应;有效端口号从0~65535,其中0~1024是系统保留的 端口号;
一些常见的端口:浏览器:80 ; tomcat服务器:8080; mysql:3306;
③传输协议:要完成传输,还要遵守相同的通信协议,国际通用协议是 TCP/IP协议,既能用于局域网,也可以 用于广域网,除了TCP/IP,还有 UDP,FTP,HTTP等协议;
网络模型:主要有两个网络模型,一是OSI模型,一是TCP/IP模型;
OSI模型:从顶到底层 分别是: 应用层,表示层,会话层,传输层,网络层,数据链路层,物理层,分七层;
TCP/IP模型:为了简化,TCP/IP将OSI的应用层,表示层,会话层合并为应用层,数据链路层和物理层合并成 主机至网络层,所以它由
应用层,传输层,网际层,主机至网络层组成;
FTP,HTTP都在应用层,TCP,UDP在传输层,IP在网际层;
数据封包:数据从应用层开始,每经一层,都加入该层的标识信息,直到物理层,这个过程叫数据封包,之后 变成二进制数据从物理设备上输出;
数据拆包:去掉每层的标识信息,获取数据的过程就是拆包过程;
网络编程现阶段主要是在网际层,传输层,而javaweb开发主要是在应用层;
java中对各个层都创建了对象与之对应,以方便我们开发使用;
IP地址:是数字,由四段组成,如192.168.1.1,使用时不便于记忆,所以有与之对应的主机名,主机名与IP地址相对应;IP常用的方法如下:都在java。net包中;
InetAddress: getLocalHost();可获取主机名和本地地址;
getHostAddress():主机地址的字符串表现形式;
getHostName():获取主机名称;
如果网络获取主机名称,可能出现解析不成功的情况,那么IP地址就是主机名,getByName("")可通过给定主机名,获取主机的IP地址;
演示:
TCP/IP和UDP协议:两者都在传输层;
两者的区别:
①UDP:(User Datagram Protocol),用户数据报协议,是一种面向无连接的不可靠协议,它将数据源和目的封装成数据报包,在不需要建立连接的情况下就可实现传输,每个数据报包大小限制在64K内,特点是传输速度快;存在数据丢失的可能,主要应用于即时数据的传输,如QQ聊天程序,对话机,网络视频会议等;
②TCP:(Transmission Control Protocol),传输控制协议,它是面向连接的可靠的传输协议,通过三次握手的形式完成数据传输通道的连接,传输大量的数据,特点是速度相对较慢,适合对数据完整性要求高的程序,如下载应用程序;
Socket:(插座,套接字):
网络编程其实就是socket编程,它是为网络服务的一种机制,每个应用程序都有一个socket通信端点,可以把socket想象为每个应用程序的插座,通信的两端都有socket,数据在两个socket之间通过IO传输;
UDP传输:UDP两端是发送端和接受端;
DatagramSocekt:UDP传输专用的程序通信断电,既能发送也能接受;
DatagramPacket: 数据报包,封装了UDP传输的数据报包,里面可以设置发送目的地的IP地址,端口和内容;
广播地址:每个网段尾数为255的IP 地址,如192.168.1.255就是一个广播地址;
UDP代码演示:
利用UDP,编写一个聊天程序
有接受数据的部分,和发数据的部分。
这两部分需要同时执行。那就需要用到多线程技术。
一个线程控制收,一个线程控制发。
因为收和发动作是不一致的,所以要定义两个run方法,
而且run方法要封装到不同的类中。
TCP传输:对应的是客户端和服务端
Socket:TCP传输客户端通信端点;
ServerSocket: TCP传输服务端端点;serversocket(port,backlog)这个构造函数中的backlog用于指定客户端的最大连接数;
因为TCP需要建立连接,所以socket客户端一建立就要指定服务端的IP和端口,而在服务端建立时,要设置监听的端口,TCP连接成功后,在客户端和服务端就会产生网络流,客户端提供对网络流进行读写的输入流和输出流对象,服务端操作网络流时,先获取客户端的socket对象,然后利用该socket的字节输入输出流进行读写操作,客户端和服务端进行多次交互时,注意阻塞式方法对程序的影响;
TCP代码演示一:
演示TCP传输。
1.tcp分客户端和服务端。
2.客户端对应的对象是Socket。
服务端对应的对象是SeverSocket
演示代码二:
演示tcp的传输的的客户端和服务端的互访。
需求:客户端给服务端发送数据,服务端收到数据后,给客户端反馈信息。
演示代码三:
需求:建立一个文本转换服务器。
客户端给服务端发送文本,服务端会将文本转成大写再返回给客户端。
而且,客户端可以不断的进行文本转换。当客户端输入over,转换就结束。
分析:
客户端:
既然是操作设备上的数据,那么就可以使用io技术,并按照IO的操作规律来思考。
源:键盘录入
目的:网络设备,也就是网络输出流,
而且操作的是文本数据,可以选择字符流。
步骤:
1,建立服务
2.获取键盘录入
3,将数据发给服务端。
4.获取服务端返回的大写数据。
5.结束,关闭资源。
都是文本数据,可以使用字符流进行操作。同时提高效率,要加入缓冲。
TCP上传文件(使用IO字符流)
把文件从客户端传到服务器端上。
容易出现的问题:没有客户端上传完后,文件结束标记,服务器无法停止连接Socket连接。
TCP传输的两个问题:
①客户端与服务端交互时同时阻塞,当客户端和服务端有多个阻塞方法时,如果使用缓冲区读取每行,没有刷新和换行,会导致同时阻塞;
②定义结束标记:定义结束标记的方法:定义“over“这样的字符标记,或者盖时间标记,或者使用socket的shutdownOutput和shutdownInput方法(建议使用)。
代码演示五:
需求:上传图片
客户端:
1.服务端点
2.读取客户端已有的图片数据
3.通过socket的输出流将数据发给服务端。
4.读取服务端反馈信息。
5.关闭。
TCP客户端并发访问:客户端并发访问服务器时,把服务端的处理代码封装在Runnable实现子类的run方法中,并把服务器获得的socket对象传递给该实现子类的构造函数,服务端通过while循环启动多个线程,对每个客户端请求进行并发处理,这也是一个服务器的基本原理;
客户端通过键盘录入用户名:
服务端对这个用户名进行校验。
如果该用户存在,在服务器端显示***,已登录。
并在客户端显示:***,欢迎光临。
如果该用户不存在,在服务器端显示 ***,尝试登陆
并在客户端显示 ***,该用户不存在。
最多就登陆三次。
URL:代表一个统一资源定位符,是指向互联网资源的指针,资源可以是简单的文本或目录,也可以是更为复杂对象的引用,如对数据库或搜索引擎的查询;
常用方法:
①int getDefaultPort(): 获取与此URL关联协议的默认端口;
②String getFile(): 获取此URL的文件名;
③String getHost(): 获取此URL的主机名;
④String getPath():或取此URL的路径;
⑤int getPort(): 获取此URL的端口号;
⑥ String getProtocol(): 获取此URL的协议名称;
⑦String getQuery(): 获取此URL的查询部分;
代码演示:
代码演示:
自定义图形化界面浏览器
域名解析:用IP地址登陆网站,数字不好记忆,习惯用主机名,如www.baidu.com,从主机名到获得该主机名对应的IP地址的过程,就是域名解析,它一般是DNS服务器完成的;
域名解析步骤大致分为2步:
①查找本地的IP地址和主机名映射表,如果存在映射,则使用本机映射,如:127.0.0.1和localhost的对应关系就在这个映射表中,通过对映射文件进行配置可以实现一些功能,如:将常用网站配置在表中,这样速度快些,还可以屏蔽过滤一些垃圾网站;
②如果第一步没有找到,就会去设置的DNS服务器查找,找到对应的IP地址,获取地址后,再返回给本机IP地址,本机再通过IP地址链接上想要访问的网站。
网络编程:
网络编程是为了实现不同主机间的数据通信,要实现通讯,则要首先知道对方的IP和端口号,同时还要明确双方使用的通信规则(传输协议)。
IP地址,端口号和传输协议就是网络通讯的三要素:
①IP地址:是主机在网络上的地址,在网上的唯一标识,127.0.0.1是本地回环地址,可测试网卡是否损坏;
②端口号:数据要发送到对方指定的应用程序上,为了标识这些应用程序,用数字对其标识,这些数字就是端 口,或被称为逻辑端口;它并无物理实体与之对应;有效端口号从0~65535,其中0~1024是系统保留的 端口号;
一些常见的端口:浏览器:80 ; tomcat服务器:8080; mysql:3306;
③传输协议:要完成传输,还要遵守相同的通信协议,国际通用协议是 TCP/IP协议,既能用于局域网,也可以 用于广域网,除了TCP/IP,还有 UDP,FTP,HTTP等协议;
网络模型:主要有两个网络模型,一是OSI模型,一是TCP/IP模型;
OSI模型:从顶到底层 分别是: 应用层,表示层,会话层,传输层,网络层,数据链路层,物理层,分七层;
TCP/IP模型:为了简化,TCP/IP将OSI的应用层,表示层,会话层合并为应用层,数据链路层和物理层合并成 主机至网络层,所以它由
应用层,传输层,网际层,主机至网络层组成;
FTP,HTTP都在应用层,TCP,UDP在传输层,IP在网际层;
数据封包:数据从应用层开始,每经一层,都加入该层的标识信息,直到物理层,这个过程叫数据封包,之后 变成二进制数据从物理设备上输出;
数据拆包:去掉每层的标识信息,获取数据的过程就是拆包过程;
网络编程现阶段主要是在网际层,传输层,而javaweb开发主要是在应用层;
java中对各个层都创建了对象与之对应,以方便我们开发使用;
IP地址:是数字,由四段组成,如192.168.1.1,使用时不便于记忆,所以有与之对应的主机名,主机名与IP地址相对应;IP常用的方法如下:都在java。net包中;
InetAddress: getLocalHost();可获取主机名和本地地址;
getHostAddress():主机地址的字符串表现形式;
getHostName():获取主机名称;
如果网络获取主机名称,可能出现解析不成功的情况,那么IP地址就是主机名,getByName("")可通过给定主机名,获取主机的IP地址;
演示:
import java.net.*; class IPDemo { public static void main(String[] args) throws Exception { InetAddress i = InetAddress.getLocalHost(); System.out.println(i.toString()); System.out.println("address:"+i.getHostAddress()); System.out.println("name:"+i.getHostName()); InetAddress [] ia = InetAddress.getAllByName("www.renren.com"); for(InetAddress s : ia) { System.out.println(s.toString()); } //System.out.println("address:"+ia.getHostAddress()); //System.out.println("name:"+ia.getHostName()); } }
TCP/IP和UDP协议:两者都在传输层;
两者的区别:
①UDP:(User Datagram Protocol),用户数据报协议,是一种面向无连接的不可靠协议,它将数据源和目的封装成数据报包,在不需要建立连接的情况下就可实现传输,每个数据报包大小限制在64K内,特点是传输速度快;存在数据丢失的可能,主要应用于即时数据的传输,如QQ聊天程序,对话机,网络视频会议等;
②TCP:(Transmission Control Protocol),传输控制协议,它是面向连接的可靠的传输协议,通过三次握手的形式完成数据传输通道的连接,传输大量的数据,特点是速度相对较慢,适合对数据完整性要求高的程序,如下载应用程序;
Socket:(插座,套接字):
网络编程其实就是socket编程,它是为网络服务的一种机制,每个应用程序都有一个socket通信端点,可以把socket想象为每个应用程序的插座,通信的两端都有socket,数据在两个socket之间通过IO传输;
UDP传输:UDP两端是发送端和接受端;
DatagramSocekt:UDP传输专用的程序通信断电,既能发送也能接受;
DatagramPacket: 数据报包,封装了UDP传输的数据报包,里面可以设置发送目的地的IP地址,端口和内容;
广播地址:每个网段尾数为255的IP 地址,如192.168.1.255就是一个广播地址;
UDP代码演示:
/* 需求:通过Udp传输方式,将一段文字数据发送出去。 定义一个udp发送端。 思路: 1。建立UDPsocket服务。 2.提供数据,并将数据封装到数据包中。 3.通过socket服务的发送功能,将数据包发出去。 4.关闭资源。 */ import java.net.*; class UDPSend { public static void main(String[] args) throws Exception { //1.创建UDP服务,通过DatagramSocket对象。 DatagramSocket ds = new DatagramSocket(6666); //2.确定数据,并封装成数据包。DatagramPacket(byte[] buf, int length, InetAddress address, int port) byte[] buf = "传输数据成功".getBytes(); DatagramPacket dp = new DatagramPacket(buf,buf.length,InetAddress.getByName("192.168.1.102"),10000); //3.通过socket服务,将已有的数据包发送出去,通过send方法。 ds.send(dp); //4.关闭资源。它使用了底层的资源,所以要关闭资源。 ds.close(); } } /* 需求; 定义一个应用程序,用于接收并处理数据。 定义udp接受端 思路: 1.定义socket服务,通常会监听一个端口,其实就是给这个接受网络应用程序定义一个数字标示。 方便与明确哪些数据过来该应用程序可以处理。 2.定义一个数据包,因为要存储要接受到的字节数据, 因为数据包对象中有更多功能可以提取字节数据中的不同数据信息。 3.通过socket服务的receive方法将收到的数据存入已定义的数据包中。 4.通过数据包对象的特有功能,将这些不同的数据取出,打印在控制台上。 5.关闭资源。 */ class UDPReceive { public static void main(String[] args) throws Exception { //1.创建udpsocket,建立端点。这里必须定义监听的端口,其实就是给网络应用程序定义数字标识。 DatagramSocket ds = new DatagramSocket(10000); //2.定义数据包,用于存储数据。 byte[] buf = new byte[1024]; DatagramPacket dp = new DatagramPacket(buf,buf.length); //3.通过服务的receive方法,将收到的数据存入到数据包中。 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+"--"+data+"--"+port); ds.close(); } }UDP代码演示2:
import java.net.*; import java.io.*; //键盘录入UDP传输 class UDPSend2 { public static void main(String[] args) throws Exception { DatagramSocket ds = new DatagramSocket(); BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in)); String line = null; while((line=bufr.readLine())!=null) { if("886".equals(line)) break; byte [] b = line.getBytes(); //192.168.1.255是这个网段的广播地址,用它就能实现局域网群聊。 DatagramPacket dp = new DatagramPacket(b,b.length,InetAddress.getByName("192.168.1.255"),10001); ds.send(dp); } ds.close(); } } class UDPRece2 { public static void main(String[] args) throws Exception { DatagramSocket ds = new DatagramSocket(10001); while(true) { byte[] buf = new byte[1024]; DatagramPacket dp = new DatagramPacket(buf,buf.length); ds.receive(dp); String ip =dp.getAddress().getHostAddress(); String data = new String(dp.getData(),0,dp.getLength()); System.out.println(ip+"说:"+data); } } }
利用UDP,编写一个聊天程序
有接受数据的部分,和发数据的部分。
这两部分需要同时执行。那就需要用到多线程技术。
一个线程控制收,一个线程控制发。
因为收和发动作是不一致的,所以要定义两个run方法,
而且run方法要封装到不同的类中。
import java.net.*; import java.io.*; class Send implements Runnable { private DatagramSocket ds; // public Send(DatagramSocket ds) { this.ds = ds; } public void run() { try { BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); String line = null; while((line = bufr.readLine())!=null) { if("886".equals(line)) break; byte[] buf =line.getBytes(); //把发送地址,设为广播地址, InetAddress i = InetAddress.getByName("192.168.1.255"); DatagramPacket dp= new DatagramPacket(buf,0,buf.length,i,10020); ds.send(dp); } ds.close(); } catch (Exception e) { throw new RuntimeException("发送端失败"); } } } class Rece implements Runnable { private DatagramSocket ds; public Rece(DatagramSocket ds) { this.ds = ds; } public void run() { try { while(true) { byte [] buf = new byte[1024]; DatagramPacket dp = new DatagramPacket(buf,buf.length); ds.receive(dp); String ip = dp.getAddress().getHostAddress(); String data = new String(dp.getData(),0,dp.getLength()); System.out.println("ip:"+ip+" data:"+data); } } catch (Exception e) { throw new RuntimeException("接受端失败"); } } } class ChatDemo { public static void main(String[] args) throws Exception { DatagramSocket sendSoket = new DatagramSocket(); DatagramSocket receSoket = new DatagramSocket(10020); new Thread(new Send(sendSoket)).start(); new Thread(new Rece(receSoket)).start(); } }
TCP传输:对应的是客户端和服务端
Socket:TCP传输客户端通信端点;
ServerSocket: TCP传输服务端端点;serversocket(port,backlog)这个构造函数中的backlog用于指定客户端的最大连接数;
因为TCP需要建立连接,所以socket客户端一建立就要指定服务端的IP和端口,而在服务端建立时,要设置监听的端口,TCP连接成功后,在客户端和服务端就会产生网络流,客户端提供对网络流进行读写的输入流和输出流对象,服务端操作网络流时,先获取客户端的socket对象,然后利用该socket的字节输入输出流进行读写操作,客户端和服务端进行多次交互时,注意阻塞式方法对程序的影响;
TCP代码演示一:
演示TCP传输。
1.tcp分客户端和服务端。
2.客户端对应的对象是Socket。
服务端对应的对象是SeverSocket
/* 客户端: 通过查阅Socket对象,发现在该对象建立时,就可以连接指定的主机。 因为TCP是面向连接的,所以在建立Socket服务时,就要有服务端存在, 并连接成功,形成通路后,在该通道进行数据的传输。 需求:给服务段发送一个文本数据。 步骤: 1.创建Socket服务,并指定要连接的主机和端口。 2.为了发送数据,应该获取Socket流中的输出流。 3.获取输出流后写入数据。 4.socket关闭客户端Socket服务,也就关闭了网络流资源。 */ import java.net.*; import java.io.*; class TCPClient { public static void main(String[] args) throws Exception { Socket s = new Socket("192.168.1.102",10086); OutputStream out = s.getOutputStream(); out.write("i am coming!".getBytes()); s.close(); } } /* 难在服务端上。 需求:定义端点,接受数据,并打印在控制台上。 服务端: 1.建立服务端的Socket服务,通过SeverSocket();并监听一个端口。 2. 获取连接过了的客户端对象。 通过SeverSocket的accept方法。没有就会等,所以这个方法是阻塞式的。 3.客户端如果发过了数据,那么服务端要使用对应的客户端对象,并获取到该客户端对象的读取流读取发过了的数据。并打印在控制台上。 4.关闭服务。(可选操作,服务端一般是一直开着的)。 */ class TCPServer { public static void main(String[] args) throws Exception { ServerSocket ss = new ServerSocket(10086); Socket s = ss.accept(); String ip = s.getInetAddress().getHostAddress(); System.out.println(ip+"......connect"); InputStream in = s.getInputStream(); byte[] buf = new byte[1024]; int len = in.read(buf); System.out.println(new String(buf,0,len)); s.close(); } }
演示代码二:
演示tcp的传输的的客户端和服务端的互访。
需求:客户端给服务端发送数据,服务端收到数据后,给客户端反馈信息。
/* 客户端: 1.建立socket服务,指定要连接的主机和端口 1.获取Socket流中的输出流,将数据写入到该流中,通过网络发送给服务端。 3.获取socket流中的输入流,将服务端反馈的数据获取到,并打印, 4.关闭客户端资源。 */ import java.io.*; import java.net.*; class TCPC2 { public static void main(String[] args) throws Exception { Socket s = new Socket("192.168.1.102",10010); OutputStream out = s.getOutputStream(); out.write("收到了吗?".getBytes()); InputStream in = s.getInputStream(); byte[] buf = new byte[1024]; int len = in.read(buf); System.out.println(new String(buf,0,len)); s.close(); } } class TCPS2 { public static void main(String[] args) throws Exception { ServerSocket ss = new ServerSocket(10010); Socket s = ss.accept(); String ip = s.getInetAddress().getHostAddress(); System.out.println(ip+"....connect"); InputStream in = s.getInputStream(); byte[] buf = new byte[1024]; int len = in.read(buf); System.out.println(new String(buf,0,len)); OutputStream out = s.getOutputStream(); out.write("收到了".getBytes()); s.close(); } }
演示代码三:
需求:建立一个文本转换服务器。
客户端给服务端发送文本,服务端会将文本转成大写再返回给客户端。
而且,客户端可以不断的进行文本转换。当客户端输入over,转换就结束。
分析:
客户端:
既然是操作设备上的数据,那么就可以使用io技术,并按照IO的操作规律来思考。
源:键盘录入
目的:网络设备,也就是网络输出流,
而且操作的是文本数据,可以选择字符流。
步骤:
1,建立服务
2.获取键盘录入
3,将数据发给服务端。
4.获取服务端返回的大写数据。
5.结束,关闭资源。
都是文本数据,可以使用字符流进行操作。同时提高效率,要加入缓冲。
import java.io.*; import java.net.*; class TCPC3 { public static void main(String[] args) throws Exception { Socket s = new Socket("192.168.1.102",10088); //定义读取键盘数据的流对象 BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in)); //定义目的,将数据写入socket输出流,发给服务器 BufferedWriter bufout = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); //PrintWriter out = new PrintWriter(s.getOutputStream(),true); //定义一个socket读取流,读取服务端返回的信息。 BufferedReader bufin = new BufferedReader(new InputStreamReader(s.getInputStream())); String line = null; while((line=bufr.readLine())!=null) { if("over".equals(line)) break; bufout.write(line); bufout.newLine(); bufout.flush(); //out.println(line); String str = bufin.readLine(); System.out.println("Server:"+str); } bufr.close(); s.close(); } } class TCPS3 { public static void main(String[] args) throws Exception { ServerSocket ss = new ServerSocket(10088); Socket s = ss.accept(); String ip = s.getInetAddress().getHostAddress(); System.out.println(ip+".....connect"); //读取socket读取流中的数据 BufferedReader bufin = new BufferedReader(new InputStreamReader(s.getInputStream())); //目的,socket输出流,将大写数据写入到socket输出流,并发送给客户端。 BufferedWriter bufout = new BufferedWriter(new OutputStreamWriter(s.getOutputStream())); //PrintWriter out = new PrintWriter(s.getOutputStream(),true); String line = null; while ((line=bufin.readLine())!=null) { System.out.println(line); bufout.write(line.toUpperCase()); bufout.newLine(); bufout.flush(); //out.println(line.toUppCase()); } s.close(); ss.close(); } }代码演示四:
TCP上传文件(使用IO字符流)
把文件从客户端传到服务器端上。
容易出现的问题:没有客户端上传完后,文件结束标记,服务器无法停止连接Socket连接。
import java.io.*; import java.net.*; class TCPC4 { public static void main(String[] args) throws Exception { Socket s = new Socket("192.168.1.102",11000); BufferedReader bufr = new BufferedReader(new FileReader("IPDemo.java")); PrintWriter out = new PrintWriter(s.getOutputStream(),true); String line= null; while ((line=bufr.readLine())!=null) { out.println(line); } s.shutdownOutput(); BufferedReader bufin= new BufferedReader(new InputStreamReader(s.getInputStream())); String str = bufin.readLine(); System.out.println(str); bufr.close(); s.close(); } } class TCPS4 { public static void main(String[] args) throws Exception { ServerSocket ss = new ServerSocket(11000); Socket s = ss.accept(); String ip = s.getInetAddress().getHostAddress(); System.out.println(ip+"....connect"); BufferedReader bufin = new BufferedReader(new InputStreamReader(s.getInputStream())); PrintWriter out = new PrintWriter(new FileWriter("copy.txt"),true); String line = null; while ((line=bufin.readLine())!=null) { out.println(line); } PrintWriter pw = new PrintWriter(s.getOutputStream(),true); pw.println("上传成功!"); out.close(); s.close(); ss.close(); } }
TCP传输的两个问题:
①客户端与服务端交互时同时阻塞,当客户端和服务端有多个阻塞方法时,如果使用缓冲区读取每行,没有刷新和换行,会导致同时阻塞;
②定义结束标记:定义结束标记的方法:定义“over“这样的字符标记,或者盖时间标记,或者使用socket的shutdownOutput和shutdownInput方法(建议使用)。
代码演示五:
需求:上传图片
客户端:
1.服务端点
2.读取客户端已有的图片数据
3.通过socket的输出流将数据发给服务端。
4.读取服务端反馈信息。
5.关闭。
import java.net.*; import java.io.*; class PICC { public static void main(String[] args) throws Exception { Socket s =new Socket("192.168.1.102",10090); FileInputStream fis = new FileInputStream("L.jpg"); OutputStream out = s.getOutputStream(); byte[] buf = new byte[1024]; int len = 0; while((len=fis.read(buf))!=-1) { out.write(buf,0,len); } s.shutdownOutput(); InputStream fin= s.getInputStream(); byte[] bufin = new byte[1024]; int in = fin.read(bufin); System.out.println(new String(bufin,0,in)); fis.close(); s.close(); } } class PICS { public static void main(String[] args) throws Exception { ServerSocket ss = new ServerSocket(10090); Socket s = ss.accept(); String ip = s.getInetAddress().getHostAddress(); System.out.println(ip+"....connected"); InputStream in = s.getInputStream(); FileOutputStream fout = new FileOutputStream("copy.jpg"); byte[] buf = new byte[1024]; int len= 0; while((len=in.read(buf))!=-1) { fout.write(buf,0,len); } OutputStream out= s.getOutputStream(); out.write("上传成功".getBytes()); fout.close(); s.close(); ss.close(); } }
TCP客户端并发访问:客户端并发访问服务器时,把服务端的处理代码封装在Runnable实现子类的run方法中,并把服务器获得的socket对象传递给该实现子类的构造函数,服务端通过while循环启动多个线程,对每个客户端请求进行并发处理,这也是一个服务器的基本原理;
import java.net.*; import java.io.*; class PICC1 { public static void main(String[] args) throws Exception { if (args.length!=1) { System.out.println("请选择要上传的图片"); return ; } File file= new File(args[0]); if(!(file.exists()&&file.isFile())) { System.out.println("图片不存在或不是图片文件"); return ; } if(!file.getName().endsWith(".jpg")) { System.out.println("图片格式错误,请选择jpg格式图片"); return ; } if (file.length()>1024*1024*3) { System.out.println("文件过大,上传失败"); return ; } Socket s =new Socket("192.168.1.102",10090); FileInputStream fis = new FileInputStream(file); OutputStream out = s.getOutputStream(); byte[] buf = new byte[1024]; int len = 0; while((len=fis.read(buf))!=-1) { out.write(buf,0,len); } s.shutdownOutput(); InputStream fin= s.getInputStream(); byte[] bufin = new byte[1024]; int in = fin.read(bufin); System.out.println(new String(bufin,0,in)); fis.close(); s.close(); } } /* 服务端: 这个服务端有个局限性,当A客户端连接上以后,被服务端获取到,服务端执行具体流程。 这时B客户端连接,只有等待。因为服务端还没有处理完A客户端的请求。还没有循环回来 执行下次accept方法。所以,暂时获取不到B客户端对象。那么为了可以让多个客户端 同时并发访问服务端。那么服务端最好就是将每个客户端封装到一个单独的线程中去。这 样就可以同时处理多个客户端请求。如何定义线程?只要明确了每一个客户端要在服务端 执行的代码即可。将该代码存入run方法中。 */ class PICThread implements Runnable { private Socket s; PICThread(Socket s) { this.s= s; } public void run() { String ip = s.getInetAddress().getHostAddress(); int c = 0; try { System.out.println(ip+"...已连接"); InputStream in = s.getInputStream(); File file= new File(ip+".jpg"); while(file.exists()) file= new File(ip+"("+(c++)+")"+".jpg"); FileOutputStream fout = new FileOutputStream(file); byte[] buf = new byte[1024]; int len= 0; while((len=in.read(buf))!=-1) { fout.write(buf,0,len); } OutputStream out= s.getOutputStream(); out.write("上传成功".getBytes()); fout.close(); s.close(); } catch (Exception e) { throw new RuntimeException(ip+"上传失败"); } } } class PICS1 { public static void main(String[] args) throws Exception { ServerSocket ss = new ServerSocket(10090); while(true) { Socket s = ss.accept(); new Thread(new PICThread(s)).start(); } } }练习:
客户端通过键盘录入用户名:
服务端对这个用户名进行校验。
如果该用户存在,在服务器端显示***,已登录。
并在客户端显示:***,欢迎光临。
如果该用户不存在,在服务器端显示 ***,尝试登陆
并在客户端显示 ***,该用户不存在。
最多就登陆三次。
import java.io.*; import java.net.*; class LOGINC { public static void main(String[] args) throws Exception { Socket s = new Socket("192.168.1.102",11000); BufferedReader bfr= new BufferedReader(new InputStreamReader(System.in)); PrintWriter out = new PrintWriter(s.getOutputStream(),true); BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream())); for(int x= 0;x<3;x++) { String line= bfr.readLine(); if(line==null) break; out.println(line); String info = in.readLine(); System.out.println("info:"+info); if(info.contains("欢迎")) break; } bfr.close(); s.close(); } } class LOGINT implements Runnable { private Socket s; LOGINT(Socket s) { this.s= s; } public void run() { String ip = s.getInetAddress().getHostAddress(); System.out.println(ip+"......已连接"); try { for (int x=0;x<3 ;x++ ) { BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream())); String name = in.readLine(); if (name==null) break; BufferedReader bufr =new BufferedReader(new FileReader("log.txt")); PrintWriter out = new PrintWriter(s.getOutputStream(),true); boolean flag = false; String line=null; while((line=bufr.readLine())!=null) { if(line.equals(name)) { flag = true; break; } } if(flag) { System.out.println(name+"已登录"); out.println(name+"欢迎光临"); break; } else { System.out.println(name+"...尝试登陆"); out.println(name+"..用户名不存在"); } } s.close(); } catch (Exception e) { throw new RuntimeException(ip+"...登陆失败"); } } } class LOGINS { public static void main(String[] args) throws Exception { ServerSocket ss = new ServerSocket(11000); while (true) { Socket s = ss.accept(); new Thread(new LOGINT(s)).start(); } } }
URL:代表一个统一资源定位符,是指向互联网资源的指针,资源可以是简单的文本或目录,也可以是更为复杂对象的引用,如对数据库或搜索引擎的查询;
常用方法:
①int getDefaultPort(): 获取与此URL关联协议的默认端口;
②String getFile(): 获取此URL的文件名;
③String getHost(): 获取此URL的主机名;
④String getPath():或取此URL的路径;
⑤int getPort(): 获取此URL的端口号;
⑥ String getProtocol(): 获取此URL的协议名称;
⑦String getQuery(): 获取此URL的查询部分;
代码演示:
import java.net.*; class URLDemo { public static void main(String[] args) throws Exception { //URL url=new URL("http://192.168.1.13:11000/myweb/demo.html"); URL url=new URL("http://192.168.1.13:11000/myweb/demo.html?name=haha&age=30"); System.out.println("getProtocol():"+url.getProtocol()); //http System.out.println("getHost():"+url.getHost());//192.168.1.13 System.out.println("getDefaultPort():"+url.getDefaultPort());//80,如果关联的协议没有默认的端口,则值为-1; System.out.println("getPort():"+url.getPort()); // 11000,如果没有设置则为-1; System.out.println("getPath():"+url.getPath());// /myweb/demo.html System.out.println("getFile():"+url.getFile());///myweb/demo.html?name=haha&age=30 System.out.println("getQuery():"+url.getQuery());//name=haha&age=30 /* int port = url.getPort(); if(port==-1) port =80; getPort() = -1 */ } }URLConnection:作用类似于socket,其实内部封装了socket,所以可以获取网络输入输出流,通过URL和URLConnection可以方便的对应用层网络数据进行读取和操作;
代码演示:
import java.net.*; import java.io.*; class URLConnectionDemo { public static void main(String[] args) throws Exception { URL url=new URL("http://192.168.1.13:8080/myweb/demo.html"); URLConnection conn = url.openConnection(); System.out.println(conn+"-------"); InputStream in = conn.getInputStream(); byte [] buf =new byte[1024]; int len = in.read(buf); System.out.println(new String(buf,0,len)); } }
自定义图形化界面浏览器
import java.awt.*; import java.awt.event.*; import java.io.*; import java.net.*; class MyIEByGUI { private Frame f; private TextField tf; private Button but; private TextArea ta; private Dialog d; private Label lab; private Button okBut; MyIEByGUI() { init(); } public void init() { f = new Frame("My IE"); f.setBounds(300,100,500,400); f.setLayout(new FlowLayout()); tf = new TextField(40); but = new Button("转到"); ta = new TextArea(20,50); d = new Dialog(f,"提示信息-self",true); d.setBounds(400,200,240,200); d.setLayout(new FlowLayout()); lab =new Label(); okBut=new Button("确定"); d.add(lab); d.add(okBut); f.add(tf); f.add(but); f.add(ta); myEvent(); f.setVisible(true); } public void myEvent() { but.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { showWebPage(); } catch (Exception ex) { } } }); tf.addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e)//用keyTyped事件不成功 { if(e.getKeyCode()==KeyEvent.VK_ENTER) { try { showWebPage(); } catch (Exception ex) { } } } }); okBut.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { d.setVisible(false); } }); d.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { d.setVisible(false); } }); f.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { System.exit(0); } }); } private void showWebPage() throws Exception { ta.setText(""); //http://192.168.1.13:8080/myweb/demo.html String url =tf.getText(); int index1 = url.indexOf("//")+2; int index2 = url.indexOf("/",index1); String str =url.substring(index1,index2); String[] arr = str.split(":"); String host = arr[0]; int port = Integer.parseInt(arr[1]); String dir = url.substring(index2); //ta.setText(url); ta.setText(port+","+null); //建立Socket客户端 Socket s = new Socket(host,port); //发送请求消息头 PrintWriter out = new PrintWriter(s.getOutputStream(),true);//别忘了加true out.println("GET "+dir+" HTTP/1.1"); out.println("Accept: */*"); out.println("Accept-Language: zh-cn"); out.println("Host: 192.168.1.13:11000"); out.println("Connection: Keep-Closed"); out.println();//记得一定要加空行 out.println(); BufferedReader bufr = new BufferedReader(new InputStreamReader(s.getInputStream())); String line = null; while((line=bufr.readLine())!=null) { ta.append(line+"\r\n"); } s.close(); } public static void main(String[] args) { new MyIEByGUI(); } } private void showWebPage() throws Exception { //使用URL和URLConnection对showWebPage改写。 //很明显,这两个封装的应用层对象使用起来更方便简洁 ta.setText(""); String urlPath =tf.getText(); URL url=new URL(urlPath); URLConnection conn = url.openConnection(); //System.out.println(conn); InputStream in = conn.getInputStream(); byte [] buf =new byte[1024]; int len = in.read(buf); ta.setText(new String(buf,0,len)); }
域名解析:用IP地址登陆网站,数字不好记忆,习惯用主机名,如www.baidu.com,从主机名到获得该主机名对应的IP地址的过程,就是域名解析,它一般是DNS服务器完成的;
域名解析步骤大致分为2步:
①查找本地的IP地址和主机名映射表,如果存在映射,则使用本机映射,如:127.0.0.1和localhost的对应关系就在这个映射表中,通过对映射文件进行配置可以实现一些功能,如:将常用网站配置在表中,这样速度快些,还可以屏蔽过滤一些垃圾网站;
②如果第一步没有找到,就会去设置的DNS服务器查找,找到对应的IP地址,获取地址后,再返回给本机IP地址,本机再通过IP地址链接上想要访问的网站。
相关文章推荐
- 黑马程序员 Java自学总结十六 网络编程
- Java学习日志(23-1-网络编程-概述)
- Java学习日志(23-3-网络编程-TCP)
- Java学习日志(23-2-网络编程-UDP传输)
- java网络编程自学(三)-TCP/IP+NIO
- 黑马程序员java自学总结之--网络编程
- Java学习日志(24-1-网络编程-自定义服务端与客户端)
- java自学笔记之网络编程
- 重新学习java网络编程
- JAVA进阶-网络编程
- java网络编程,通过TCP,Socket实现多对一的局域网聊天室
- Java的网络编程(TCP/IP)
- java基础16_网络编程
- Java 网络编程
- JAVA学习第五十九课 — 网络编程概述
- Java网络编程基础 ServerSocket类使用
- Java 网络IO编程总结(BIO、NIO、AIO均含完整实例代码)
- Java网络编程---回显聊天
- JAVA 网络编程 Socket 详细说明,实现客户端和服务端相互推送消息
- Java 网络编程 字符流的发送与接收 自定义数据边界