Java最初就是作为网络编程语言出现的,其对网络提供了高度的支持,使得客户端和服务器的沟通变成了现实。网络编程中,最常使用的就是Scoket。如QQ,MSN都使用了Scoket相关的技术。今天学习了Java有关Scoket套接字相关的知识。
代码示例:
/*
* InetAddress类
*/
public class Test01 {
public static void main(String[] args) throws UnknownHostException {
//获取本机的InetAddress实例
InetAddress address = InetAddress.getLocalHost();
System.out.println("计算机名:"+address.getHostName());
System.out.println("IP地址:"+address.getHostAddress());
byte[] bytes = address.getAddress();//获取字节数组形式的IP地址
System.out.println("字节数组形式的IP地址:"+Arrays.toString(bytes));
System.out.println(address);//直接输出InetAddress对象(计算机名/IP地址)
//根据IP地址获取InetAddress实例
// InetAddress address2 = InetAddress.getByName("idea-PC");
InetAddress address2 = InetAddress.getByName("192.168.1.67");
System.out.println("计算机名:"+address2.getHostName());
System.out.println("IP地址:"+address2.getHostAddress());
//其他静态方法可以自己试验
}
}
代码示例:
/*
* URL常用方法
*/
public class Test02 {
public static void main(String[] args) {
try {
//创建一个URL实例
URL imooc = new URL("http://www.imooc.com");
//?后面表示参数,#后面表示锚点
URL url = new URL(imooc, "/index.html?username=tom#test");
System.out.println("协议:"+url.getProtocol());;
System.out.println("主机:"+url.getHost());
//如果未指定端口号,则使用默认的端口号,此时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) {
e.printStackTrace();
}
}
}
代码示例:
/*
* 使用URL读取网页内容
*/
public class Test03 {
public static void main(String[] args) {
try {
//创建一个URL实例
URL url = new URL("http://www.baidu.com");
//通过URL的openStream()方法获取URL对象所表示的资源的字节输入流
BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream(), "utf-8"));
String data = br.readLine();
while(data != null) {
System.out.println(data);
data = br.readLine();
}
br.close();
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
运行结果(注意编码,可以将结果copy到一个html文件中打开查看结果):
TCP协议是面向连接、可靠地、有序的,以字节流的方式发送数据。
基于TCP协议实现网络通信的类
* 客户端的Socket类
* 服务器端的ServerSocket类
/*
* 基于TCP协议的Socket通信,实现用户登录
* 服务器端
*/
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.获取输出流,响应客户端请求
PrintWriter pw = new PrintWriter(socket.getOutputStream());
pw.write("欢迎您!");
pw.flush();
socket.shutdownOutput();
//5.关闭相关资源
socket.close();
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
* 客户端
*/
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.获取输入流,并读取服务器端的响应信息
BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String info = null;
while((info = br.readLine())!=null) {
System.out.println("我是客户端,服务器说:"+info);
}
socket.shutdownInput();
//4.关闭资源
// br.close();
// pw.close();
// os.close();
socket.close();//相当于 切断了流通道,所以流的close()方法可以不需要
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//先启动服务器端,再启动客户端
现实生活中,常常都是启动一个服务器端,而有多个客户端与之连接,那么该怎么实现呢?应用多线程来实现服务器与多客户端之间的通信。
基本步骤
1. 服务器端创建ServerSocket,循环调用accept()等待客户端连接
2. 客户端创建一个socket并请求和服务器端连接
3. 服务器端接受客户端请求,创建socket与该客户端建立专线连接
4. 建立连接的两个socket在一个单独的线程上对话
5. 服务器端继续等待新的连接
具体的实现可以在上面的代码上进行修改,新建一个ServerThread类继承Thread,每一个socket启动一个线程。
代码实现:
/*
* 服务器线程处理类
*/
public class ServerThread extends Thread{
//和本线程相关的Socket
Socket socket = null;
public ServerThread(Socket socket) {
this.socket = socket;
}
//线程执行的操作,响应客户端请求
@Override
public void run() {//直接从Server类拷贝代码过来,进行流的处理
InputStream is = null;
InputStreamReader isr = null;
BufferedReader br = null;
OutputStream os = null;
PrintWriter pr = null;
try {
is = socket.getInputStream();
isr = new InputStreamReader(is);
br = new BufferedReader(isr);
String data = null;
while((data = br.readLine()) != null) {
System.out.println("客户端发来消息:"+data);
}
socket.shutdownInput();
//发送响应
os = socket.getOutputStream();
pr = new PrintWriter(os);
pr.write("欢迎您!");
pr.flush();
socket.shutdownOutput();
} catch (IOException e) {
e.printStackTrace();
} finally {//在finally里中进行资源的关闭,确保代码一定会执行
try {
//关闭资源,可以进行流的关闭,但我感觉没必要
if(socket!=null)
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*
* 服务器端
*/
public class Server {
@SuppressWarnings("resource")
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = null;
//记录客户端数量
int count = 0;
System.out.println("服务器启动,等待中。。。");
//循环监听等待客户端的连接
while(true){
socket = serverSocket.accept();
//创建一个新的线程
ServerThread st = new ServerThread(socket);
st.start();
count++;
System.out.println("客户端数量为:"+count);
InetAddress address = socket.getInetAddress();
System.out.println("当前客户端的IP:"+address.getHostAddress());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
* 客户端(与上同)
*/
运行结果:略。。。(先启动服务器后,多启动几次客户端查看效果)
/*
* 服务器端,实现基于UDP的用户登录
*/
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.接收客户端发送的数据
System.out.println("***服务器端已经启动,等待客户端发送数据***");
socket.receive(packet);//此方法在接收到数据报之前会一直阻塞
//4.读取数据
String info = new String(data, 0, packet.getLength());
System.out.println("我是服务器,客户端说:"+info);
/*
* 向客户端响应数据
*/
//1.定义客户端的地址、端口号、数据
InetAddress address = packet.getAddress();
int port = packet.getPort();
byte[] data2 = "欢迎您!".getBytes();
//2.创建数据报,包含响应的数据
DatagramPacket packet2 = new DatagramPacket(data2, data2.length, address, port);
//3.响应客户端
socket.send(packet2);
//4.关闭资源
socket.close();
}
}
/*
* 客户端
*/
public class UDPClient {
public static void main(String[] args) throws IOException {
/*
* 向服务器端发送数据
*/
//1.定义服务器的地址、端口号、数据
InetAddress address = InetAddress.getByName("localhost");
int port = 8800;
byte[] data = "用户名:carry;密码:423".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 info = new String(data2, 0, packet2.getLength());
System.out.println("我是客户端,服务器说:"+info);
//4.关闭资源
socket.close();
}
}
运行结果:略(别忘了先启动服务器,在运行客户端代码!)
现实中也存在多个客户端访问一个服务器的例子,同TCP一样,我们采用多线程来解决多客户端与服务器的通信问题。具体实现步骤可以参照TCP
代码示例:
/*
* 服务器线程处理类
*/
public class UDPServerThread extends Thread {
DatagramPacket packet = null;
DatagramSocket socket = null;
// byte[] data = null;
public UDPServerThread(DatagramPacket packet, DatagramSocket socket) {
this.packet = packet;
this.socket = socket;
// this.data = data;
}
@Override
public void run() {
try {
// 4.读取数据
String info = new String(packet.getData(), 0, packet.getLength());
System.out.println("我是服务器,客户端说:" + info);
/*
* 向客户端响应数据
*/
// 1.定义客户端的地址、端口号、数据
InetAddress address = packet.getAddress();
int port = packet.getPort();
byte[] data2 = "欢迎您!".getBytes();
// 2.创建数据报,包含响应的数据
DatagramPacket packet2 = new DatagramPacket(data2, data2.length, address, port);
// 3.响应客户端
socket.send(packet2);
} catch (IOException e) {
e.printStackTrace();
}
}
}
/*
* 服务器端,实现基于UDP的用户登录
*/
public class UDPServer2 {
public static void main(String[] args) throws IOException {
/*
* 接收客户端发送的数据
*/
//1.创建服务器端DatagramSocket,指定端口
DatagramSocket socket = new DatagramSocket(8800);
//2.创建数据报,用于接受客户端发送的数据
byte[] data = new byte[1024];//创建字节数组,指定接收的数据报的大小
DatagramPacket packet = null;
//3.接收客户端发送的数据
int count = 0;
System.out.println("***服务器端已经启动,等待客户端发送数据***");
while(true) {
packet = new DatagramPacket(data, data.length);
socket.receive(packet);//此方法在接收到数据报之前会一直阻塞
UDPServerThread serverThread = new UDPServerThread(packet, socket);
serverThread.start();
count++;
System.out.println("客户端数量为:"+count);
}
}
}
/*
* 客户端(与上同)
*/
运行结果:略
有兴趣的可以去看laurenyang老师的Java Socket应用—通信是这样练成的。谢谢老师的讲解!