Java网络编程
一、网络编程
1.1 网络通信
计算机之间的信息交互是通过服务器作为媒介的
数据传输离不开IO流
网络通信协议:计算机通信必须遵守的通信规则,不遵守则不能进行通信。通信协议对数据的传输格式、传输速率、传输步骤等做了同意规定。
TCP/IP协议:定义了计算机如何连入Internet网,以及数据之间传输的标准
通信协议:
TCP:面向连接,安全、可靠、数据大小没有限制
三次握手
1.客户端给服务器发送连接请求,等待服务器确认
2.服务器回复一个响应,通知客户端收到了请求
3.客户端再次向服务器发送信息,确认连接
UDP:面向无连接,不安全、不可靠、数据大小有限制
网络三要素
IP地址: 本机IP地址 127.0.0.1 、localhost
端口号: 一般四位
协议:TCP/UDP
1.2 TPC通信程序
1.2.1 两端通信时步骤
1.服务器程序,需要先启动,等待客户端的连接
2.客户端主动连接服务器,连接成功才能通信。服务器不能主动连接客户端
使用两个类实现TCP通信
1.客户端:java.net.Socket
//两个参数:1.服务器的ip地址;2.程序的端口号
public Socket(String host, int port) throws UnknownHostException, IOException{
this(host != null ? new InetSocketAddress(host, port) :
new InetSocketAddress(InetAddress.getByName(null), port),
(SocketAddress) null, true);
}
2.服务器:java.net.ServerSocket
//参数:指定程序的端口号,等待客户端的连接
public ServerSocket(int port) throws IOException {
this(port, 50, null);
}
1.2.2 代码实现
- 客户端与服务器通信
//客户端
public class Demo03Socket {
public static void main(String[] args) throws IOException {
//创建客户端对象
Socket socket = new Socket("127.0.0.1", 9999);
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();
Scanner scanner = new Scanner(System.in);
while (true) {
//发送消息
String msg = scanner.next();
out.write(msg.getBytes());
//接受消息
byte[] bytes = new byte[1024];
int len = in.read(bytes);
System.out.println(new String(bytes));
}
// in.close();
// out.close();
// socket.close();
}
}
//服务器
public class Demo03ServerSocket {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(9999);
//等待客户端的连接,返回值是客户端对象
Socket accept = serverSocket.accept();
InputStream in = accept.getInputStream();
OutputStream out = accept.getOutputStream();
Scanner scanner = new Scanner(System.in);
System.out.println("客户端连接");
while (true) {
//接受消息
byte[] b = new byte[1024];
// int len;
in.read(b);
System.out.println(new String(b));
//发送消息
String message = scanner.next();
out.write(message.getBytes());
}
// out.close();
// in.close();
// serverSocket.close();
}
}
- 多个客户端与服务器通信(客户端代码没变化,只需要创建多个类就行)
//服务器
public class Demo04ServerSocket {
public static void main(String[] args) throws IOException {
//创建服务器
ServerSocket serverSocket = new ServerSocket(9999);
while (true) {
Socket accept = serverSocket.accept();
System.out.println("客户端连接");
/*
这为什么要用线程呢
因为线程会从新开辟一个运行路线,main方法的程序执行到这就会跳过,然后又会回到上面的循环
如果没有线程,就会一直陷入在下面的无限循环对话中
*/
new Thread(() -> {
try {
//等待客户端的连接,返回值是客户端对象,具有阻塞的效果
InputStream in = accept.getInputStream();
OutputStream out = accept.getOutputStream();
Scanner scanner = new Scanner(System.in);
//接受消息
byte[] b = new byte[1024];
// int len;
while (true) {
in.read(b);
System.out.println(new String(b));
//发送消息
String message = scanner.next();
out.write(message.getBytes());
}
} catch (Exception e) {
}
}).start();
}
// out.close();
// in.close();
// serverSocket.close();
}
}
注意事项
- 线程的使用。在服务器端使用多线程的时候需要创建一个死循环,然后利用服务器创建客户端对象的阻塞来控制循环的执行次数。所以客户端对象的创建放在循环的里面。线程要放在创建客户端对象的代码下面,这样将服务器与客户端对象的通信代码脱离了主线程,也不会影响到其他客户端的创建。
- 客户端输出流的使用。当服务器接收从客户端传来的资源时(反之也一样),使用到了while循环,这时只有当客户端的输出流关闭了,服务器的whhile循环才不会阻塞,否则就会默认客户端还会传来资源,然后处于一直等待的状态。所以要记得客户端使用输出流后要关闭。关闭的方法有三:
- 直接关闭客户端对象:client.close();
- 关闭输出流:out.close();
- showdownOutput()方法。