1、两台主机通信的必备条件:ip地址、端口号、协议
2、tcp/ip协议(传输层)族两台主机通信必须遵守
tcp:Transmission Control Protocol:传输控制协议
ip:Internet Protocol:互联网协议
应用层协议:HTTP超文件传输协议、FTP:文件传输协议、SMTP:简单邮件传送协议、Telnet:远程登录服务
端口:用来区分一台主机上的不同的应用程序
端口号的范围:0~65535,其中0~1023为系统所保留,建议使用1023之后的端口号。
ip地址和端口号组成Socket,Socket是网络上运行的程序之间进行双向通信链路的终结点,是tcp和UDP的基础
常用端口号:
http:80 ftp:21 telnet:23
3、java中的网络支持
针对网络通信的不同层次,java提供的网络功能有四大类:
InetAddress:用于表示网络上的硬件资源
URL:统一资源定位符,通过URL可以直接读取或写入网络上的数据
Sockets:使用tcp协议实现网络通信的Socket相关类
Datagram :使用UDP协议,将数据保存在数据报中,通过网络进行通信
4、InetAddress类
表示互联网协议(IP)地址
package com.imooc.socket;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
/**
* InetAddress类
* @author Administrator
*
*/
public class Test01 {
public static void main(String[] args) throws UnknownHostException {
//获取本机的InetAddress实例
InetAddress localHost = InetAddress.getLocalHost();
System.out.println("计算机名:"+localHost.getHostName());
System.out.println("计算机Ip:"+localHost.getHostAddress());
//获取字节数组形式的ip地址,输出:“字节数组形式的ip地址:[10, 1, 17, -89]”
byte[] bytes = localHost.getAddress();
System.out.println("字节数组形式的ip地址:"+Arrays.toString(bytes));
//输出“计算机名/ip地址”
System.out.println(localHost);
/**
* 通过计算机名或ip地址获取InetAddress实例
*/
//通过主机名获取InetAddress实例
//InetAddress localHost2 = InetAddress.getByName("SC-201701251309");
//通过ip地址获取InetAddress实例
InetAddress localHost2 = InetAddress.getByName("10.1.17.167");
System.out.println("计算机名:"+localHost.getHostName());
System.out.println("计算机Ip:"+localHost.getHostAddress());
}
}
5、URL类
1)url(uniform resource locator):统一资源定位符,表示Internet上某一资源的地址
2)url由两部分组成:协议名和资源名。中间用冒号隔开
3)在java.net包中,提供了URL类来表示URL
4)URL中常用方法
package com.imooc.socket;
import java.net.MalformedURLException;
import java.net.URL;
public class Test02 {
public static void main(String[] args) {
try {
//创建URL实例
URL imooc = new URL("http://www.imooc.com");
//根据已知的url imooc来创建另一个url
URL url = new URL(imooc,"/index.html?username=tom#test");
//?后面表示参数,#后面表示锚点
System.out.println("协议:"+url.getProtocol());
System.out.println("主机:"+url.getHost());
//如果未指定端口号,则使用协议默认的端口好这里是80,getPort方法返回-1
System.out.println("端口:"+url.getPort());
System.out.println("文件路径:"+url.getPath());
System.out.println("文件名:"+url.getFile());
System.out.println("相对路径:"+url.getRef());
System.out.println("查询字符串:"+url.getQuery());
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
5)使用URL读取网页的内容
通过URL对象的openStream()方法可以得到指定资源的输入流
通过输入流 可以读取,访问网络上的数据
package com.imooc.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
/**
* 使用URL读取网页的数据
* @author Administrator
*
*/
public class Test03 {
public static void main(String[] args) {
try {
//创建一个URL实例
URL url = new URL("http://www.baidu.com");
//通过url的openstream方法获取url对象所表示的资源的字节输入流
InputStream is = url.openStream();
//将字节流转换为字符流
InputStreamReader isr = new InputStreamReader(is,"utf-8");
//为字符输入流添加缓冲
BufferedReader br = new BufferedReader(isr);
//读取数据
String data = br.readLine();
while(data!=null){//循环读取数据
System.out.println(data);
data = br.readLine();
}
br.close();
isr.close();
is.close();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
6、Socket通信–基于TCP协议
1)tcp协议:以字节流的方式发送数据,面向连接,可靠的,有序的
2)基于tcp协议实现网络通信的类
客户端的Socket类
服务器端的ServerSocket类
3)Socket通信模型
实现步骤
①创建ServerSocket和Socket
②打开连接到Socket的输入\输出流
③按照协议对Socket进行读\写操作
④关闭输入、输出流、关闭Socket
服务器端代码:
package com.imooc.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 基于tcp协议的socket通信,实现用户登录
* 服务器端
* @author Administrator
*
*/
public class Server {
public static void main(String[] args) {
try {
//1、创建一个服务器端Socket,即serverSocket,并指定绑定端口,并监听此端口
ServerSocket serverSocket = new ServerSocket(8888);
//2、调用accept方法,等待客户端的连接
System.out.println("*****服务端即将启动,等待客户端的连接*****");
Socket socket = serverSocket.accept();//处于阻塞状态,等待
//3.获取输入流,并读取客户端信息
InputStream is = socket.getInputStream();//字节输入流
InputStreamReader isr = new InputStreamReader(is);//将字节流转换为字符流
BufferedReader br = new BufferedReader(isr);//为输入流添加缓冲
String info = null;
while((info=br.readLine())!=null){
System.out.println("服务器端:客户端说:"+info);
}
socket.shutdownInput();
//4、获取输出流,响应客户端请求
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);//包装成打印流
pw.write("欢迎您");
pw.flush();
//socket.shutdownOutput();
//5、关闭资源
pw.close();
os.close();
br.close();
isr.close();
is.close();
socket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端的代码:
package com.imooc.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 客户端,基于tcp协议的socket通信
* @author Administrator
*
*/
public class Client {
public static void main(String[] args) {
try {
//1、创建一个Socket对象,指定服务器地址和端口
Socket socket = new Socket("localhost",8888);
//2、获取输出流,向服务器端发送数据
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);//将输出流包装成打印流
pw.write("用户名:admin;密码:123");
pw.flush();
socket.shutdownOutput();//关闭输出流
//3、获取输入流,用来获取服务端的响应信息
InputStream is = socket.getInputStream();//字节流
InputStreamReader isr = new InputStreamReader(is);//转换成字符流
BufferedReader br = new BufferedReader(isr);//为输入流添加缓存
String info = null;
while((info=br.readLine())!=null){
System.out.println("我是客户端,服务端说:"+info);
}
//4、关闭资源
br.close();
isr.close();
is.close();
pw.close();
os.close();
socket.close();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
4)使用多线程实现多客户端的通信
服务器端代码:
package com.imooc.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 基于tcp协议的socket通信,实现用户登录
* 服务器端
* @author Administrator
*
*/
public class Server {
public static void main(String[] args) {
try {
//1、创建一个服务器端Socket,即serverSocket,并指定绑定端口,并监听此端口
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = null;
System.out.println("*****服务端即将启动,等待客户端的连接*****");
int count = 0;
while(true){
//2、调用accept方法,等待客户端的连接
socket = serverSocket.accept();//处于阻塞状态,等待
//创建一个新的线程
ServerThread serverThread = new ServerThread(socket);
//启动线程
serverThread.start();
count++;
System.out.println("当前客户端的数量:"+count);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
服务端线程类:
package com.imooc.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
public class ServerThread extends Thread {
Socket socket = null;
InputStream is = null;
InputStreamReader isr = null;
BufferedReader br = null;
OutputStream os = null;
PrintWriter pw = null;
public ServerThread(Socket socket){
this.socket = socket;
}
public void run(){
try {
//3.获取输入流,并读取客户端信息
is = socket.getInputStream();//字节输入流
isr = new InputStreamReader(is);//将字节流转换为字符流
br = new BufferedReader(isr);//为输入流添加缓冲
String info = null;
while((info=br.readLine())!=null){
System.out.println("服务器端:客户端说:"+info);
}
socket.shutdownInput();
//4、获取输出流,响应客户端请求
os = socket.getOutputStream();
pw = new PrintWriter(os);//包装成打印流
pw.write("欢迎您");
pw.flush();
//socket.shutdownOutput();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//5、关闭资源
if(pw!=null)
pw.close();
if(os!=null)
os.close();
if(br!=null)
br.close();
if(isr!=null)
isr.close();
if(is!=null)
is.close();
if(socket!=null)
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
客户端代码:
package com.imooc.socket;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
/**
* 客户端,基于tcp协议的socket通信
* @author Administrator
*
*/
public class Client {
public static void main(String[] args) {
try {
//1、创建一个Socket对象,指定服务器地址和端口
Socket socket = new Socket("localhost",8888);
//2、获取输出流,向服务器端发送数据
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);//将输出流包装成打印流
pw.write("用户名:tom;密码:123");
pw.flush();
socket.shutdownOutput();//关闭输出流
//3、获取输入流,用来获取服务端的响应信息
InputStream is = socket.getInputStream();//字节流
InputStreamReader isr = new InputStreamReader(is);//转换成字符流
BufferedReader br = new BufferedReader(isr);//为输入流添加缓存
String info = null;
while((info=br.readLine())!=null){
System.out.println("我是客户端,服务端说:"+info);
}
//4、关闭资源
br.close();
isr.close();
is.close();
pw.close();
os.close();
socket.close();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
7、Socket通信–基于UDP协议
1)用户数据报协议,无连接,不可靠,无序的;将数据封装成数据报
2)相关操作类
DatagramPacket:表示数据报包
DatagramSocket:进行端到端通信的类
服务器端代码:
package com.imooc.socket;
/**
* 基于UDP的用户登录功能
* 服务器端
*/
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
public class UDPServer {
public static void main(String[] args) throws IOException {
/**
* 接收服务器端的数据
*/
//1、创建服务端DatagramSocket,并指定端口
DatagramSocket socket = new DatagramSocket(8800);
//2、创建数据报,用户接收客户端发送的数据报
byte[] data = new byte[1024];
DatagramPacket packet = new DatagramPacket(data, data.length);
//3.接收客户端发送的数据
socket.receive(packet);//此方法在接受到数据报之前会一直阻塞
//读取数据
String info = new String(data, 0, packet.getLength());
System.out.println("我是服务器,客户端说:"+info);
/**
* 服务器端响应客户端
*/
//1、通过接收到的数据报获取客户端的地址和端口号
InetAddress address = packet.getAddress();
int port = packet.getPort();
//2、创建数据报,包含响应信息
byte[] data2 = "欢迎您!".getBytes();
DatagramPacket packet2 = new DatagramPacket(data2, data2.length, address,port);
//3、响应客户端
socket.send(packet2);
//4、关闭资源
socket.close();
}
}
客户端代码:
package com.imooc.socket;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.UnknownHostException;
/**
* 客户端,基于UDP的用户登录
* @author Administrator
*
*/
public class UDPClient {
public static void main(String[] args) throws IOException {
/**
* 向服务器端发送数据
*/
//1、定义服务器端的 地址、端口号、数据
InetAddress address = InetAddress.getByName("localhost");
int port = 8800;
//用户要发送的数据
byte[] data = "用户名:admin;密码:123".getBytes();
//2、创建数据报,包含发送的信息
DatagramPacket packet = new DatagramPacket(data, data.length,address,port);
//3、创建DatagramSocket对象,
DatagramSocket socket = new DatagramSocket();
//4、向服务器发送数据
socket.send(packet);
/**
* 接收服务端的响应
*/
//1、创建数据报,用户接收服务器端的响应
byte[] data2 = new byte[1024];
DatagramPacket packet2 = new DatagramPacket(data2, data2.length);
//2、接收服务器端响应的数据
socket.receive(packet2);
//3、取数据
String reply = new String(data2,0,packet2.getLength());
System.out.println("我是客户端,服务器端回应说:"+reply);
//4、关闭资源
socket.close();
}
}
8、Socket编程总结:
1)多线程中适当降低线程的优先级,否则会导致运行时速度非常慢
2)对于同一个socket,如果关闭了输出流,则与该输出流关联的socket也会被关闭,所以一般不用关闭流,直接关闭socket即可。
3)登录中可以将传输字符信息改为传输用户对象
Socket socket = new Socket("localhost",8888);
OutputStream os = socket.getOutputStream();
//使用ObjectOutputStream对象序列化流,传递对象
ObjectOutputStream oos = new ObjetcOutputStream(os);
User user = new User("admin","123");//封装为对象
oos.writeObjecet(user);//序列化
socket.shutdownOutput();
4)Socket编程传递文件
作业:udp多线程、扑克牌、上传文件
分析:
业务分析:业务流程