1、类
a. InetSocketAddress
- 此类实现 IP 套接字地址(IP 地址 + 端口号)。
- 继承 java.net.SocketAddress
a.a 构造方法
InetSocketAddress(InetAddress addr, int port)
// 根据 IP 地址和端口号创建套接字地址。
InetSocketAddress(int port) // 一般作为接收端
// 创建套接字地址,其中 IP 地址为通配符地址,端口号为指定值。
InetSocketAddress(String hostname, int port)
// 根据主机名和端口号创建套接字地址。
a.b方法
InetAddress getAddress()
// 获取 InetAddress。
String getHostName()
// 获取 hostname。
int getPort()
// 获取端口号。
b. java.net.InetAddress
此类表示互联网协议 (IP) 地址。
c. DatagramPacket
c.a构造方法
DatagramPacket(byte[] buf, int length)
// 构造 DatagramPacket,用来接收长度为 length 的数据包。
DatagramPacket(byte[] buf, int length, InetAddress address, int port)
// 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
DatagramPacket(byte[] buf, int length, SocketAddress address)
// 构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。
c.b 方法
// get 方法
InetAddress getAddress()
// 返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。
byte[] getData()
// 返回数据缓冲区。
int getLength()
// 返回将要发送或接收到的数据的长度。
int getPort()
// 返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。
SocketAddress getSocketAddress()
// 获取要将此包发送到的或发出此数据报的远程主机的 SocketAddress(通常为 IP 地址 + 端口号)。
// set 方法
void setAddress(InetAddress iaddr)
// 设置要将此数据报发往的那台机器的 IP 地址。
void setData(byte[] buf)
// 为此包设置数据缓冲区。
void setData(byte[] buf, int offset, int length)
// 为此包设置数据缓冲区。
void setLength(int length)
// 为此包设置长度。
void setPort(int iport)
// 设置要将此数据报发往的远程主机上的端口号。
void setSocketAddress(SocketAddress address)
// 设置要将此数据报发往的远程主机的 SocketAddress(通常为 IP 地址 + 端口号)。
d.DatagramSocket
d.a构造方法
DatagramSocket()
// 构造数据报套接字并将其绑定到本地主机上任何可用的端口。
DatagramSocket(int port)
// 创建数据报套接字并将其绑定到本地主机上的指定端口。
DatagramSocket(int port, InetAddress laddr)
// 创建数据报套接字,将其绑定到指定的本地地址。
DatagramSocket(SocketAddress bindaddr)
// 创建数据报套接字,将其绑定到指定的本地套接字地址。
d.b方法
void bind(SocketAddress addr)
// 将此 DatagramSocket 绑定到特定的地址和端口。
void close()
// 关闭此数据报套接字。
void connect(InetAddress address, int port)
// 将套接字连接到此套接字的远程地址。
void connect(SocketAddress addr)
// 将此套接字连接到远程套接字地址(IP 地址 + 端口号)。
void disconnect()
// 断开套接字的连接。
int getPort()
// 返回此套接字的端口。
// 接收和发送方法
void receive(DatagramPacket p)
// 从此套接字接收数据报包。
void send(DatagramPacket p)
// 从此套接字发送数据报包。
e.ServerSocket
e.a 构造方法
ServerSocket()
// 创建非绑定服务器套接字。
ServerSocket(int port)
// 创建绑定到特定端口的服务器套接字。
e.b 方法
Socket accept()
// 侦听并接受到此套接字的连接。
void bind(SocketAddress endpoint)
// 将 ServerSocket 绑定到特定地址(IP 地址和端口号)。
void bind(SocketAddress endpoint, int backlog)
// 将 ServerSocket 绑定到特定地址(IP 地址和端口号)。
void close()
// 关闭此套接字。
f.Socket
此类实现客户端套接字(也可以就叫“套接字”)。套接字是两台机器间通信的端点。
f.a构造方法
Socket()
// 通过系统默认类型的 SocketImpl 创建未连接套接字
Socket(InetAddress address, int port)
// 创建一个流套接字并将其连接到指定 IP 地址的指定端口号。
f.b方法
void bind(SocketAddress bindpoint)
// 将套接字绑定到本地地址。
void close()
// 关闭此套接字。
InputStream getInputStream()
// 返回此套接字的输入流。
OutputStream getOutputStream()
// 返回此套接字的输出流。
int getPort()
// 返回此套接字连接到的远程端口。
InetAddress getInetAddress()
// 返回套接字连接的地址。
2、UDP 案例
a.服务端
public class ServerSocket {
// 服务器端
public static void main(String[] args) throws IOException {
// 服务端,指明主机即可,默认的 ip 127.0.0.1
DatagramSocket server = new DatagramSocket(8888);
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
// 构成死循环,一直会监听发送过来的程序
while(true) {
// 将接受到的数据放到 packet 中
server.receive(packet);
byte[] data = packet.getData();
int length = packet.getLength();
int port = packet.getPort();
InetAddress address = packet.getAddress();
System.out.println("接受来自 : "+address.getHostName()+" "+port+" 的数据!");
System.out.println(new String(data,0,length));
}
}
}
b. 客户端
public class ClientSocket {
public static void main(String[] args) throws IOException {
// 客户端,发送数据的一方
String ip = "localhost";
InetAddress addr = InetAddress.getByName("127.0.0.1");
DatagramSocket sendSocket = new DatagramSocket(8889, addr);
Scanner sc = new Scanner(System.in);
while(true) {
String str = sc.nextLine();
if("quit".equals(str)) {
break;
}
// 设置数据报
DatagramPacket packet = new DatagramPacket(new byte[1024], 1024);
// 构造数据包
// 将数据 set 到数据包里
packet.setData(str.getBytes());
// 通过 InetAddress.getByName("127.0.0.1) 方式返回一个InetAddress对象
// new DatagramPacket(str.getBytes(), str.getBytes().length, InetAddress.getByName("127.0.0.1"), 6666);
packet.setSocketAddress(new InetSocketAddress("localhost",8888));
// 发送数据
sendSocket.send(packet);
}
}
}
3、TCP案例
https://blog.youkuaiyun.com/qq_26525215/article/details/51362924
3.1单向通讯
客户机 -》 服务器
a.服务端
public class LoginServer {
public static void main(String[] args) throws Exception {
System.out.println("这里是 LogingServer");
// 创建服务端的 socket
ServerSocket server = new ServerSocket(8888);
// 监听传送过来的 socket ,如果没有就会阻塞
Socket socket = server.accept();
// 接受来自客户端的数据
InputStream in = socket.getInputStream();
DataInputStream dis = new DataInputStream(in);
System.out.println(dis.readUTF());
dis.close();
in.close();
socket.close();
server.close();
}
}
b.客户端
public class LoginClient {
public static void main(String[] args) throws Exception {
System.out.println("这里是 LogingClient");
// 创建 socket ,同时指定连接的 ip 和端口
Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
// 得到输出流,用于向服务器发送数据
OutputStream out = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(out);
dos.writeUTF("hello , I'm bigguy!");
dos.close();
out.close();
socket.close();
}
}
3.2双向通讯
a.服务端
public class LoginServer {
public static void main(String[] args) throws Exception {
System.out.println("这里是 LogingServer");
// 创建服务端的 socket
ServerSocket server = new ServerSocket(8888);
// 监听传送过来的 socket ,如果没有就会阻塞
Socket socket = server.accept();
// 接受来自客户端的数据
InputStream in = socket.getInputStream();
DataInputStream dis = new DataInputStream(in);
System.out.println(dis.readUTF());
OutputStream outputStream = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(outputStream);
dos.writeUTF("你好,登入成功");
dis.close();
outputStream.close();
dos.close();
in.close();
socket.close();
server.close();
}
}
b.客户端
public class LoginClient {
public static void main(String[] args) throws Exception {
System.out.println("这里是 LogingClient");
Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
OutputStream out = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(out);
dos.writeUTF("hello , I'm bigguy!");
// 得到网络中的输入流
InputStream in = socket.getInputStream();
DataInputStream dis = new DataInputStream(in);
System.out.println(dis.readUTF());
dos.close();
in.close();
dis.close();
out.close();
socket.close();
}
}
c.注意
- 服务端
- 服务端创建 ServerSocket ,在指定的端口监听并处理请求
- ServerSocket 通过 accept() 接受用户请求并返回对应的 Socket ,如果没有请求一直会处理阻塞的状态(程序一直运行),线程被阻塞
- 客户端
- 客户端创建 Socket , 需要指定服务端器的 ip 和 端口号,向服务端发送和接受响应
- 一但 socket建立网络连接后,通讯就和普通的IO操作一样
- 输入/输出流
- 建议使用 DataOutputStream 和 DataInputStream 和平台无关
- 对象流可以使用 ObjectInputStream / ObjectOutputStream
- 如果是字符串通信,可以使用 BufferdReader / PrintWriter
3.3服务端持续监听
a.客户端
public class LoginClient {
public static void main(String[] args) throws Exception {
Scanner sc = new Scanner(System.in);
System.out.println("这里是 LogingClient");
Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
System.out.println("请输入用户名和密码 : ");
String name = sc.next();
String password = sc.next();
User user = new User(name,password);
// 序列化对象,对象必须实现序列化接口
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(user);
// 得到网络中的输入流
InputStream in = socket.getInputStream();
DataInputStream dis = new DataInputStream(in);
System.out.println(dis.readUTF());
in.close();
dis.close();
oos.close();
socket.close();
}
}
b.服务端
public class LoginServer {
public static void main(String[] args) throws Exception {
System.out.println("这里是 LogingServer");
// 创建服务端的 socket
ServerSocket server = new ServerSocket(8888);
// 使服务端一直的运行(一直监听)
while(true) {
// 监听传送过来的 socket ,如果没有就会阻塞
Socket socket = server.accept();
// 接受来自客户端的数据
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Object user = ois.readObject();
User realUser = null;
if( user!=null && user instanceof User) {
realUser = (User)user;
}else {
throw new Exception("不能转化为 User 对象");
}
System.out.println("你好 : "+realUser.getName()+", 你的密码是 : "+realUser.getPassword());
OutputStream outputStream = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(outputStream);
dos.writeUTF("你好,登入成功");
outputStream.close();
dos.close();
socket.close();
}
// 产生了 死循环,不用关闭 serverSocket 了
}
}
c.POJO类
public class User implements Serializable{
// 使用对象流,对象必须序列化
private String name;
private String password;
.....
}
3.4多线程方式
- 3.3 的问题,当多个 socket 同时访问时候,服务端只能一个一个的处理,效率降低
- 考虑将处理socket 的任务作出多线程,并发处理 -> 提高效率
- 还可以将线程放在线程池中,减少创建一个线程的开销
a.服务端
public class LoginServer {
public static void main(String[] args) throws Exception {
System.out.println("这里是 LogingServer");
// 创建服务端的 socket
ServerSocket server = new ServerSocket(8888);
// 使服务端一直的运行(一直监听)
while(true) {
// 监听传送过来的 socket ,如果没有就会阻塞
Socket socket = server.accept();
// 接受来自客户端的数据
LoginThread sonTask = new LoginThread(socket);
new Thread(sonTask).start(); // 开启线程
}
// 产生了 死循环,不用关闭 serverSocket 了
}
}
b.客户端
public class LoginClient {
public static void main(String[] args) throws Exception {
Scanner sc = new Scanner(System.in);
System.out.println("这里是 LogingClient");
Socket socket = new Socket(InetAddress.getLocalHost(), 8888);
System.out.println("请输入用户名和密码 : ");
String name = sc.next();
String password = sc.next();
User user = new User(name,password);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(user);
// 得到网络中的输入流
InputStream in = socket.getInputStream();
DataInputStream dis = new DataInputStream(in);
System.out.println(dis.readUTF());
in.close();
dis.close();
oos.close();
socket.close();
}
}
c.线程类
public class LoginThread implements Runnable{
Socket socket = null;
public LoginThread(Socket socket) {
this.socket = socket;
}
public LoginThread() {
}
public void run() {
ObjectInputStream ois =null;
OutputStream outputStream = null;
DataOutputStream dos = null;
try {
ois = new ObjectInputStream(socket.getInputStream());
Object user = ois.readObject();
User realUser = null;
if( user!=null && user instanceof User) {
realUser = (User)user;
}else {
try {
throw new Exception("不能转化为 User 对象");
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("你好 : "+realUser.getName()+", 你的密码是 : "+realUser.getPassword());
outputStream = socket.getOutputStream();
dos = new DataOutputStream(outputStream);
dos.writeUTF("你好,登入成功");
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e1) {
e1.printStackTrace();
}finally {
// 关闭资源
try {
if(outputStream!=null) {
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(outputStream!=null) {
dos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(outputStream!=null) {
socket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
4.网络传输文件
a.客户端
将文件读到网络中的输出流中
public class FileClient {
public static void main(String[] args) throws Exception{
// 文件传输方,将文件发送给服务器端
Socket socket = new Socket(InetAddress.getByName("127.0.0.1"), 8888);
OutputStream outputStream = socket.getOutputStream();
String file = "C:\\Users\\Administrator\\Desktop\\文档总结\\视频文档\\javase\\网络编程.md";
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
int len = 0;
while((len=bis.read())!=-1) {
outputStream.write(len);
}
bis.close();
outputStream.close();
socket.close();
}
}
b.服务端
将文件从传过来的套接字中读出来,写到本地
public class FileServer {
public static void main(String[] args) throws Exception {
ServerSocket server = new ServerSocket(8888);
Socket socket = server.accept();
InputStream inputStream = socket.getInputStream();
OutputStream outputStream = socket.getOutputStream();
// 客户端传过来的的流文件
BufferedInputStream bis = new BufferedInputStream(inputStream);
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("./demo.md"));
int len = 0;
while((len=bis.read()) !=-1) {
bos.write(len);
}
bos.close();
bis.close();
server.close();
}
}