本文内容来自Java Socket应用—通信是这样练成的,记录下学习的内容
Java Socket
TCP/IP协议是目前应用最为广泛的协议,TCP传输控制协议,IP互联网协议。
应用层协议:
- HTTP
- FTP
- SMTP简单邮件传送协议
- Telnet远程登录服务
IP地址和端口号组成了所谓的Socket,Socket是网络上运行的程序之间双向通信链路的终结点,是TCP和UDP的基础。
InetAddress
InetAddress类标识网络上的硬件资源,标识互联网协议(IP)地址。
获取InetAddress
的实例
1.通过类方法获取
InetAddress address = InetAddress.getLocalHost();
2.根据机器名获取InetAddress
实例
InetAddress address2 = InetAddress.getByName("WinfredZen");
3.根据IP地址来获取InetAddress
实例
InetAddress address3 = InetAddress.getByName("192.168.0.1");
一些常用方法
方法 | 说明 |
---|---|
getHostName() | 获取计算机名 |
getHostAddress() | 获取IP地址 |
获取字节形式的IP地址
byte[] bytes = address.getAddress();
System.out.println("字节数组形式的IP:"+Arrays.toString(bytes));
URL
URL
统一资源定位符,表示Internet上某一资源的地址。
URL
由两部分组成:协议名称和资源名称,中间由冒号隔开。
在java.net
包中,提供了URL
类来表示URL
构造方法
URL imooc = new URL("http://www.baidu.com");
URL url = new URL(imooc, "index.html?username=tom#test")
常用方法
示例说明使用上面创建的的url
方法 | 说明 | 示例 |
---|---|---|
getProtocol() | 获取协议 | http |
getHost() | 获取主机 | www.baidu.com |
getPort() | 获取端口 | -1 java.net.URL.getPort() 规定,若URL的实例未申明(省略)端口号,则返回值为-1 |
getPath() | 获取文件路径 | /index.html |
getFile() | 获取文件名 | /index.html?username=tom |
getRef() | 获取相对路径 | test |
getQuery() | 获取查询字符串 | username=tom |
使用URL读取网页内容
1.通过URL
对象的openStream()
方法可以得到指定资源的输入了
2.通过输入流可以读取、访问网络上的数据
如下:
try {
URL url = new URL("http://www.baidu.com");
//获取URL对象所表示的资源的字节输入流
InputStream in = url.openStream();
//将字节输入流转换为字符输入流
InputStreamReader isr = new InputStreamReader(in, "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();
in.close();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
TCP
TCP
协议是面向连接、可靠的、有序的,以字节流的方式发送数据。
Java中基于TCP协议实现网络通信的类
1.客户端的Socket
类
2.服务端的ServerSocket
类
Socket通信模型
Socket通信实现步骤
1.创建ServerSocket
和Socket
2.打开连接到Socket
的输入/输出流
3.按照协议对Socket
进行读/写操作
4.关闭输入输出流、关闭Socket
编程实现基于 TCP 的 Socket 通信
服务器端
1.创建ServerSocket
对象,绑定监听端口
2.通过accept()
方法监听客户端请求
3.连接建立后,通过输入流读取客户端发送的请求消息
4.通过输出流向客户端发送响应信息
5.关闭相关资源
package com.imooc;
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;
/*
* 服务端
*/
public class Server {
public static void main(String[] args) {
try {
//创建服务端的socket
ServerSocket serverSocket = new ServerSocket(8882);
//调用accept()方法开始监听
System.out.println("***服务器即将启动,等待客户端的连接***");
Socket socket = serverSocket.accept();
//获取输入流,读取客户端发送的登录信息
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();
//获取输出流,响应客户端的请求
OutputStream os = socket.getOutputStream();
PrintWriter pw = new PrintWriter(os);
pw.write("欢迎您!");
pw.flush();
//关闭资源
pw.close();
os.close();
br.close();
isr.close();
is.close();
socket.close();
serverSocket.close();
System.out.println("服务端关闭连接");
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端
1.创建Socket对象,指明需要连接的服务器的地址和端口号
2.连接建立后,通过输出流向服务器端发送请求消息
3.通过输入流获取服务器响应的消息
4.关闭相关资源
package com.imooc;
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;
public class Client {
public static void main(String[] args) {
try {
//创建socket,指定服务器地址和端口
Socket socket = new Socket("localhost", 8882);
//获取输出流,向服务器端发送登录信息
OutputStream os = socket.getOutputStream();//字节输出流
PrintWriter pw = new PrintWriter(os);//将输出流包装为打印流
pw.write("用户名:admin;密码:123");
pw.flush();
//关闭输出流
socket.shutdownOutput();
//获取输入流
InputStream is = socket.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String info = null;
while ((info=br.readLine()) != null) {
System.out.println("***我是客户端,服务端说:"+ info+"***");
}
//关闭资源
br.close();
is.close();
pw.close();
os.close();
socket.close();
System.out.println("客户端关闭");
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用多线程实现多客户端的通信
应用多线程来实现服务器与多客户端之间的通信。
基本步骤:
1.服务端创建ServerSocket
,循环调用accept()
等待客户端连接
2.客户端创建一个socket
并请求和服务端连接
3.服务端接受客户端请求,创建socket
与该客户建立专线连接
4.建立连接的两个socket
在一个单独的线程上对话
创建ServerThread
类继承自Thread
package com.imooc;
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 {
private Socket socket = null;
public ServerThread(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
InputStream is = null;
InputStreamReader isr = null;
BufferedReader br = null;
OutputStream os = null;
PrintWriter pw = null;
try {
//获取输入流,读取客户端发送的登录信息
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();
//获取输出流,响应客户端的请求
os = socket.getOutputStream();
pw = new PrintWriter(os);
pw.write("欢迎您!");
pw.flush();
} catch (IOException e) {
e.printStackTrace();
}finally {
//关闭资源
try {
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) {
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
服务器端代码:
package com.imooc;
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.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
/*
* 服务端
*/
public class MutiServer {
public static void main(String[] args) {
try {
//创建服务端的socket
ServerSocket serverSocket = new ServerSocket(8882);
System.out.println("***服务器即将启动,等待客户端的连接***");
Socket socket = null;
//记录连接数量
int count = 0;
//循环监听等待客户端的连接
while (true) {
//调用accept()方法开始监听
socket = serverSocket.accept();
//创建一个新的线程
ServerThread serverThread = new ServerThread(socket);
//启动线程
serverThread.start();
count++;
System.out.println("连接数量:"+count);
InetAddress address = socket.getInetAddress();
System.out.println("客户单当前IP地址:"+address.getHostAddress());
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
调试,控制台可能有如下的输出:
客户单当前IP地址:127.0.0.1
***我是服务器,客户端说:用户名:admin;密码:123***
连接数量:2
客户单当前IP地址:127.0.0.1
***我是服务器,客户端说:用户名:admin;密码:123***
编程实现基于 UDP 的 Socket 通信
UDP协议(用户数据报协议)是无连接、不可靠的、无序的
UDP协议以数据报作为数据传输的载体。
进行数据传输时,首先需要将要传输的数据定义成数据报(Datagram),在数据报中指明数据所要到达的Socket(主机地址和端口号),然后再将数据报发送出去。
相关的操作类
DatagramPacket
表示数据包DatagramSocket
进行端到端通信的类
服务端
1.创建DatagramSocket
,指定端口号
2.创建DatagramPacket
,用来接收客户端发送的数据
3.接收客户端发送的数据信息
4.读取数据
package com.imooc;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
/*
* 服务端,实现基于UDP的用户登录
*/
public class UDPServer {
public static void main(String[] args) throws IOException {
/*
* 接收客户端发送的数据
*/
//创建服务端DatagramSocket,指定端口
DatagramSocket socket = new DatagramSocket(8889);
//创建数据报,用于接收客户端发送的数据
byte[] data = new byte[1024];//创建字节数组,指定接收的数据包的大小
DatagramPacket packet = new DatagramPacket(data, data.length);
//接收客户端发送的数据
socket.receive(packet);//此方法在接收到数据之前,会一直阻塞
//读取数据
String info = new String(data, 0, packet.getLength());
System.out.println("我是服务器,客户端说:" + info);
/*
* 向客户端响应数据
*/
//定义客户端的地址,端口号、数据
InetAddress address = packet.getAddress();
int port = packet.getPort();
byte[] dataToSend = "欢迎您".getBytes();
//创建数据报,包含响应的数据信息
DatagramPacket packet2 = new DatagramPacket(dataToSend, dataToSend.length,address, port);
//响应客户端
socket.send(packet2);
//关闭资源
socket.close();
}
}
客户端
1.定义发送信息
2.创建DatagramPacket
,包含我们要发送的信息
3.创建DatagramSocket
,用来实现数据的发送
4.发送数据
package com.imooc;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
/*
* 客户端实现
*/
public class UDPClient {
public static void main(String[] args) throws IOException {
/*
* 向服务器发送数据
*/
//定义服务器的地址、端口号
InetAddress address = InetAddress.getByName("localhost");
int port = 8889;
byte[] data = "用户名:admin;密码:123".getBytes();
//创建数据报,包含发送的数据
DatagramPacket packet = new DatagramPacket(data, data.length, address, port);
//创建DatagramSocket对象
DatagramSocket socket = new DatagramSocket();
//向服务器发送数据报
socket.send(packet);
/*
* 接收服务器响应数据
*/
//创建数据报,用于接收数据端响应的数据
byte[] data2 = new byte[1024];
DatagramPacket packet2 = new DatagramPacket(data2, data2.length);
//接收服务器响应的数据
socket.receive(packet2);
//读取数据
String reply = new String(data2,0, packet2.getLength());
System.out.println("我是客户端,服务器说:"+reply);
//关闭资源
socket.close();
}
}
总结
1.多线程的优先级
未设置优先级可能会导致运行时速度非常慢,可降低优先级
2.是否关闭输出流和输入流
对应同一个socket,如果关闭了输出流,则与该输出流关联的socket也会被关闭,所以一般不用关闭流,直接关闭socket即可
3.使用TCP通信传输对象
4.socket编程传递文件