网络模型:
OSI
TCP\IP
IP地址:网络设备中的标识
端口:用于标识进程的逻辑地址,不同进程的标识。有效端口:0~65535,其中0~1024为系统使用或保留窗口。
传输协议
UDP
将数据及源和目的封装成数据包中,不需要建立连接
每个数据包的大小限制在64k内
因无连接,是不可靠协议
不需要建立连接,速度快
TCP
建立连接,形成传输数据的通道。
在连接中进行大数据量传输
通过三次握手完成连接,是可靠协议
必须建立连接,效率会稍低
关于IP、InetAddress :
public static void main(String[] args) throws UnknownHostException {
//获取本地主机ip地址对象。
InetAddress ip = InetAddress.getLocalHost();
//获取其他主机的ip地址对象
ip = InetAddress.getByName("192.168.1.111");
System.out.println(ip.getHostAddress());
System.out.println(ip.getHostName());
}
DNS
域名解析器
UDP传输与接收
Socket就是为网络服务提供的一种机制
通信的两端都有Socket
网络通信其实就是Socket间的通信。
数据在两个Socket间通过IO传输
DatagramSocket与DatagramPacket
建立发送端,接收端。
建立数据包。
调用Soclet的发送接收方法。
关闭Socket
发送端与接收端是两个独立运行的程序。
public class UDPDemo {
/*创建UDP发送端
* 思路:
* 1 建立udp的socket服务。
* 2 将要发送的数据封装到数据包中
* 4 关闭socket服务
* */
public static void main(String[] args) throws IOException {
//1 udpsocket服务,使用DatagramSocket对象
DatagramSocket ds = new DatagramSocket();
//2 将要发送的数据封装到数据包中。
String str = "udp传输演示:哥们来了!";
//使用DatagramPacket将数据封装到该对象包中
byte[] buf = str.getBytes();
/*DatagramPacket(byte[] buf, int length, InetAddress address, int port)
构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。*/
DatagramPacket dp = new DatagramPacket(buf, buf.length,InetAddress.getByName("192.168.1.100"),10000);
//3 通过udp的Socket服务奖数据包发送出去。使用send方法。
ds.send(dp);
//4 关闭资源
ds.close();
}
}
public class UDPReceDemo {
/*建立接收端的思路。
* 1 建立udp,socket服务,因为是要接受数据,必须要明确一个端口号
* 2 创建数据包,用于存储接受到的数据,方便用数据包对象的方法解析这些数据。
* 3 使用socket服务的receive方法接收的数据存储到数据包中。
* 4 通过数据包的方法解析数据包中的数据。
* 5 关闭资源。
* */
public static void main(String[] args) throws IOException {
System.out.println("接收端启动");
//1 建立udp socket服务。
DatagramSocket ds = new DatagramSocket(10000);
//2 创建数据包
byte[] buf = new byte[1024];
DatagramPacket dp = new DatagramPacket(buf, buf.length);
//3 使用接收方法将数据存储到数据包中。
ds.receive(dp);//阻塞式的。
//4 通过数据包对象的方法,解析其中的数据,比如,地址,端口,数据内容。
String ip = dp.getAddress().getHostAddress();
int port = dp.getPort();//获取端口。
String txt = new String(dp.getData(),0,dp.getLength());
System.out.println(ip+"::"+port+"::"+txt);
//5 关闭资源
ds.close();
}
}
TCP传输
public class ClientDemo {
public static void main(String[] args) throws UnknownHostException, IOException {
/*客户端->服务端
*Tcp传输,客户端建立的过程。
* 1 创建tcp客户端服务器。使用的是Socket对象。
* 建议该对象一创建就明确目的地。要连接的主机。
* 2 如果连接建立成功,说明数据传输通道已建立。
* 该通道就是socke流,是底层建立好的,既然是流,说明这里既有输入
* 又有输出,想要输入或者输出流对象,可以找Socket来获取。
* 可以通过getOutputStream(),和getIntputStream()来获取两个字节流。
* 3 使用输出流,将数据写出。
* 4 关闭资源
* */
//创建客户端
Socket socket = new Socket("192.168.1.100",10002);
//获取socket流中的输出流
OutputStream out = socket.getOutputStream();
//使用输出流将指定的数据写出去。
out.write("tcp演示:哥们又来了!".getBytes());
//读取服务端返回的数据,使用socket读取流。
InputStream in = socket.getInputStream();
byte[] bus = new byte[1024];
int len = in.read(bus);
String str = new String(bus,0,len);
System.out.println(str);
//关闭资源
socket.close();//有这个就不用关流了。
}
}
public class ServerDemo {
public static void main(String[] args) throws IOException {
/*建立tcp服务端的思路:
* 1 创建Socket服务。通过ServerSocket对象
* 2 服务端必须对外提供一个端口,否则客户端换无法连接
* 3 获取连接过来的客户端对象。
* 4 通过客户端对象获取socket流读取客户端发来的数据。。
* 并打印在控制台上
* 5 关闭资源。关客户端,关服务端。
*
* */
//1 创建服务端对象。
ServerSocket ss = new ServerSocket(10002);
//2 获取连接过来的客户端对象。
Socket s = ss.accept();
String ip = s.getInetAddress().getHostAddress();
//3 通过socket对象获取输入流,要读取客户端发来的数据
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int len = in.read(buf);
String str = new String(buf,0,len);
System.out.println(ip+"::"+str);
//反馈回客户端
OutputStream out = s.getOutputStream();
out.write("收到".getBytes());
s.close();
ss.close();
}
}
练习:
文本转换
客户端:
public class TransClient {
public static void main(String[] args) throws UnknownHostException, IOException {
/*思路:
* 客户端:
* 1 需要先有socket端点
* 2 客户端的数据源:键盘。
* 3 客户端的目的:socket
* 4 接收服务端的数据,源:socket
* 5 将数据显示打印出来,目的:控制台。
* 6 在这些流中操作的数据都是文本数据。
* 转换客户端:
* 1 创建socket客户端对象。
* 2 获取键盘录入
* 3 将录入的信息发送给socket输出流。
* */
//1 创建socket客户端对象。
Socket s = new Socket("192.168.1.112",10004);
//2 获取键盘录入
BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));
//3 socket输出流
PrintWriter out = new PrintWriter(s.getOutputStream(),true);//输出出去的时候,保持数据 的原样性不变,true 自动刷新
//socket输入流,读取服务端返回的大写数据
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
String line = null;
while((line = buf.readLine())!=null){
if("over".equals(line))
break;
out.println(line);
//读取服务端发回的一行大写数
String upperStr = bufIn.readLine();
System.out.println(upperStr);
}
s.close();//此方法会植入客户端结束标记。从而才能使服务端的readline方法不会一直等待。
}
}
服务端:
public class TransServer {
public static void main(String[] args) throws IOException {
/*转换服端
* 分析:
* 1 serversocket服务
* 2 获取socket对象
* 3 源:socket,读取客户端发过来的需要转换的数据。
* 4 目的:显示在控制台上。
* 5 将数据转成大写发给客户端。
* */
//1
ServerSocket ss = new ServerSocket(10004);
//2 获取socket对象
Socket s = ss.accept();
//获取ip
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip);
//3 获取socket读取流,并装饰。
BufferedReader bufIn = new BufferedReader(new InputStreamReader(s.getInputStream()));
//获取socket输出流,并装饰
PrintWriter out = new PrintWriter(s.getOutputStream(),true);//true 自动刷新
String line = null;
while((line = bufIn.readLine())!=null){
System.out.println(line);
out.println(line.toUpperCase());
}
s.close();
ss.close();
}
}
常见问题:
基本都是因为阻塞式方法造成的,readLine为阻塞式方法
在客户端与服务端交互时,使用下列两个方法做结束标记。(缓冲区默认大小1024*8,装满以后自动刷新一次)
void | shutdownInput() 此套接字的输入流置于“流的末尾”。 |
void | shutdownOutput() 禁用此套接字的输出流。 |
多线程上传图片:
客户端
public class UploadPicClient {
public static void main(String[] args) throws UnknownHostException, IOException {
Socket s = new Socket("192.168.1.100",10006);
FileInputStream fis = new FileInputStream("0.bmp");
OutputStream out = s.getOutputStream();
byte[] bus = new byte[1024];
int len = 0;
while((len = fis.read(bus))!=-1){
out.write(bus,0,len);
out.flush();
}
s.shutdownOutput();
InputStream ins = s.getInputStream();
byte[] buf = new byte[1024];
int len1 = ins.read(buf);
String str = new String(buf,0,len1);
System.out.println(str);
s.close();
fis.close();
}
}
服务端:
public class UploadPicServer {
public static void main(String[] args) throws IOException {
//创建tcp的socket服务端
ServerSocket ss = new ServerSocket(10006);
while(true){
Socket s = ss.accept();
new Thread(new UploadTask(s)).start();//为每个客户端都建立线程任务
}
}
}
public class UploadTask implements Runnable {
private Socket s;
public UploadTask(Socket s){
this.s = s;
}
public void run() {
int count = 0;
String ip = s.getInetAddress().getHostAddress();
System.out.println(ip+"connect...");
try{
InputStream ins = s.getInputStream();
File f = new File("e:\\abc");
if(!f.exists())
f.mkdirs();
File f1 = new File(f,ip+".bmp");
while(f1.exists()){
f1 = new File(f,ip+"("+(++count)+").bmp");
}
FileOutputStream fos = new FileOutputStream(f1);
byte[] bus = new byte[1024];
int len = 0;
while((len = ins.read(bus))!=-1){
fos.write(bus, 0, len);
}
OutputStream out = s.getOutputStream();
out.write("上传成功".getBytes());
fos.close();
s.close();
}catch(IOException e){}
}
}
http: 超文本传输协议
Ftp:文件传输协议
类 URI
表示一个统一资源标识符 (URI) 引用。类 URL
类 URL
代表一个统一资源定位符,它是指向互联网“资源”的指针。
构造方法摘要 | |
---|---|
URL(String spec) 根据 String 表示形式创建 URL 对象。 |
InputStream | openStream() 打开到此 URL 的连接并返回一个用于从该连接读入的 InputStream 。 |
openStream
public final InputStream openStream() throws IOException
-
打开到此
URL
的连接并返回一个用于从该连接读入的InputStream
。此方法是下面方法的缩写:openConnection().getInputStream()
-
openConnection
-
public URLConnection openConnection() throws IOException
-
返回一个
URLConnection
对象,它表示到URL
所引用的远程对象的连接。
-
返回一个
类 URLConnection
InputStream | getInputStream() 返回从此打开的连接读取的输入流。 |
例:
String str_url = "http://192.168.1.100:8080/myweb/1.html?name=lisi";
URL url = new URL(str_url);
InputStream in = url.openStream();
相当于:
URLConnection conn = url.openConnection();获取url对象的URL连接器对象,将连接封装成了对象:java中内置的可以解析的具体协议的对象+socket.
InputStream in = conn.getInputStream();
网络架构:
1 C/S client/server
特点:
该结构的软件,客户端和服务端都需要编写。
开发成本较高,维护较为麻烦
好处:
客户端在本地可以分担一部分运算。
2 B/S browser/server
特点:
该结构的软件只开发服务器端,不开发客户端,因为客户端直接由浏览器取代。
开发成本相对低,维护更为简单。
缺点:
所有运算都要在服务端完成。