目录
1. 前言
今天写个小项目——聊天室,这个项目是要实现用户的注册、退出以及用户之间的单聊、群聊等功能。现在先写一个单线程的版本,只实现服务器与客户端之间的信息传递。
2. 功能实现
实现客户端与服务器的信息传递
- 客户端发数据给服务器,服务器接收数据并打印
- 服务器响应数据给客户端,客户端接收数据并打印
3. 模块划分
将整个项目划分为两个模块:客户端、服务器
4. 功能分析
- 网络编程,每一个聊天室软件在本地电脑上都是一个进程,两人聊天即为两个进程间通信
- -> 两个计算机通信(基于TCP协议)
- -> 将计算机抽象为Java的一个类
- - > 套接字
- -> 利用java.net.Socket & java.net.ServerSocket
- -> 两个类之间建立联系
- -> 利用java.net.Socket & java.net.ServerSocket
- - > 套接字
- -> 将计算机抽象为Java的一个类
- -> 两个计算机通信(基于TCP协议)
-
4.1 Socket编程
- 当套接字在两台计算机上建立连接时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行通信。先有客户端套接字,再想办法连接服务器的套接字。
-
java.net.Socket类提供客户端对象
-
java.net.ServerSocket为服务器提供对象与客户端建立连接,并可以监听客户端
-
- 建立TCP连接:
- 1. 服务器实例化ServerSocket对象,并利用端口号(网络中) / 进程ID唯一的表示当前的进程
- 2. 服务器调用ServerSocket类的accept()阻塞方法,让服务器一直处于等待连接的状态,直到有客户端连接上指定的端口号再做出响应。
- 3. 客户端在服务器等待时,创建一个Socket对象,并制定IP地址(确定服务器)和端口号(确定服务器中的进程)。
- 4. Socket 类的构造函数尝试将客户端连接到指定的服务器和端口号。
- 5. 服务器端的accept() 方法返回一个新的 Socket对象,该对象连接到客户端。
- 建立连接后,利用I/O进行通信,客户端和服务器两个模块中各有一个输入流和输出流,客户端的输出流连接到服务器的输入流,而客户端的输入流连接到服务器的输出流(为了便于传输利用转换流将字符流转换为字节流)。
- TCP 是一个双向的通信协议,因此数据可以通过两个数据流在同一时间发送。
- 当套接字在两台计算机上建立连接时,服务器会创建一个 Socket 对象。客户端和服务器现在可以通过对 Socket 对象的写入和读取来进行通信。先有客户端套接字,再想办法连接服务器的套接字。
-
4.2 ServerSocket
服务器通过ServerSocket类的构造方法获取一个端口,并且侦听客户端请求。
//创建绑定到特定端口的服务器套接字
public ServerSocket(int port) throws IOException
- ServerSocket类的常用方法
//返回此套接字在其上侦听的端口
public int getLocalPort()
//侦听并接受到此套接字的连接
public Socket accept() throws IOException
-
4.3 Socket
java.net.Socket 类代表客户端和服务器都用来互相沟通的套接字。
//创建一个 流套接字并将其连接到指定主机上的指定端口号
public Socket(String host, int port) throws UnknownHostException, IOException
- Socket 类的常用方法
//返回此套接字连接到的远程端口号
public int getPort()
//返回套接字连接的地址
public InetAddress getInetAddress()
//返回此套接字的输入流
public InputStream getInputStream() throws IOException
//返回此套接字的输出流
public OutputStream getOutputStream() throws IOException
//关闭此套接字
public void close() throws IOException
-
4.4 InetAddress
该类表示互联网协议(IP)地址
//在给定主机名的情况下确定主机的 IP 地址
static InetAddress getByName(String host)
//返回本地主机
static InetAddress getLocalHost()
5. 使用技术
- Socket编程
- I/O
6. 代码
package com.qqy.chat.server.single;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
/**
* 服务器
* Author:qqy
*/
public class SingleServer {
public static void main(String[] args) {
try {
//1.创建服务端的ServerSocket,绑定端口
ServerSocket serverSocket=new ServerSocket(65521);
System.out.println("端口号为"+serverSocket.getLocalPort()+"的服务器已启动,等待客户端的连接...");
//2.接收客户端连接,Socket
Socket clientSocket=serverSocket.accept();
System.out.println("接收到客户端"+clientSocket.getRemoteSocketAddress()+"的连接");
//3.通过Socket发送和接收数据
//接收的是客户端的OutputStream
InputStream in=clientSocket.getInputStream();
//按行读
Scanner scanner=new Scanner(in);
System.out.println("接收到客户端的消息:"+scanner.nextLine());
//发送
OutputStream out=clientSocket.getOutputStream();
//字符流 -> 字节流
OutputStreamWriter writer=new OutputStreamWriter(out);
//切记加上\n,否则客户端无法读取,阻塞
writer.write("你好,我是服务器\n");
//一定要刷新
// 数据写完后,网络传输会将报文分包,到达目的地后再进行组装。
writer.flush();
//4.关闭ServerSocket/Socket
serverSocket.close();
System.out.println("服务器已关闭...");
} catch (IOException e) {
e.printStackTrace();
}
}
}
package com.qqy.chat.client.single;
import java.io.*;
import java.net.Socket;
import java.util.Scanner;
/**
* 客户端
* Author:qqy
*/
public class SingleClient {
public static void main(String[] args) {
try {
//1.创建Socket,指定服务器的地址和服务端口,连接指定服务
Socket clientSocket=new Socket("127.0.0.1",65521);
System.out.println("端口号为"+clientSocket.getLocalPort()+"的客户端已创建");
System.out.println("该客户端已连接上端口号为"+clientSocket.getPort()+"的服务器");
//2.通过Socket发送和接收数据
//发送
OutputStream out=clientSocket.getOutputStream();
OutputStreamWriter write=new OutputStreamWriter(out);
write.write("你好,我是客户端\n");
write.flush();
//接收的是服务器的OutputStream
InputStream in =clientSocket.getInputStream();
Scanner scanner=new Scanner(in);
System.out.println("接收到服务器的消息:"+scanner.nextLine());
//3.关闭Socket
clientSocket.close();
System.out.println("客户端已关闭...");
} catch (IOException e) {
e.printStackTrace();
}
}
}