1.基本的通信架构
通信架构分为两种:CS架构(客户端/服务器),BS架构(浏览器/服务器)
cs架构:
- 客户端、服务端需要程序员开发
- 需要用户安装客户端
bs架构:
- 不需要安装客户端,通过浏览器就能访问
- 程序员只需开发服务端
2.网络通信三要素
1.ip,设备的唯一标识
2.端口号,程序的id标识
3.协议,连接和数据传输的规则
2.1 IP地址对象--InetAddress
2.2 端口号
端口分类
2.3 协议
UDP协议
- 无连接,不可靠通信
- 事先不建立连接,数据按照包发,一包数据包含:自己的IP,程序端口,目的地IP,程序端口和数据(限制在64KB内)等
- 发送消息的时候不管对方是否在线,数据是否在中途丢失也不管,接收方接收到消息也不会返回确认,所以是不可靠的
- 通信的效率较高,传输的速率高,丢了极个别的包影响不大
TCP协议
- 面向连接,可靠通信
- TCP的最终目的:要保证在不可靠的信道上实现可靠传输
- TCP主要有三个步骤实现可靠传输:三次握手连接,传输数据进行确认,四次挥手断开连接
可靠连接:确定通信双方,收发消息都是正常无问题的(全双工通信)
三次握手
四次挥手断开连接
3.UDP通信
bash
//
1.将传输的字符放入字节数组(getbytes方法,将字符串转为字节)
2.传入的字节长度
3.接收端的ip地址
4.接收端的端口号
public DatagramPacket(byte[] buf,int length,InetAddress address, int port)
消息通信,一发一收实现步骤
客户端
- 1.创建客户端对象DatagramSocket对象
- 2.创建DatagramPacket对象封装需要发送的数据(数据包对象)
- 3.使用DatagramSocket对象的send方法,传入DatagramPacket对象
- 4.关闭释放资源,close
服务端
- 1.创建DatagramSocket对象并指定端口(服务端对象)
- 2.创建DatagramPacket对象接收数据(数据包对象)
- 3.使用DatagramSocket对象的receive方法,传入DatagramPacket对象
- 4.关闭,释放资源。close()
消息通信,多发多收实现步骤
bash
//服务端
public class UdpSocketServer {
public static void main(String[] args) throws IOException {
/**
* UDP不可靠通信,服务器
*/
DatagramSocket datagramSocket = new DatagramSocket(9000);//申明一个服务器对象
byte[]bytes=new byte[1024*64];
DatagramPacket datagramPacket=new DatagramPacket(bytes,bytes.length);//申明一个容器用于接收传输过来的数据
while(true)
{
datagramSocket.receive(datagramPacket);//服务器对象接收容器的类容
String str=new String(bytes,0,datagramPacket.getLength());//将字节数组转换成字符串
System.out.println(str);
}
}
}
bash
//客户端
public class UdpSocketConsumer {
public static void main(String[] args) throws Exception {
Scanner scanner=new Scanner(System.in);
while (true)
{
System.out.println("开始输入吧!");
String str = scanner.nextLine();//将传输的数据转换成字节数组
if (str.equals("exit"))
{
break;
}
byte[] bytes = str.getBytes();
DatagramSocket datagramSocket=new DatagramSocket();//申明一个客户端对象
DatagramPacket datagramPacket=new DatagramPacket(bytes,bytes.length, InetAddress.getLocalHost(),9000);//用于存储传输数据,长度,ip,端口
datagramSocket.send(datagramPacket);//发送消息
}
}
}
4.TCP通信
特点:面向连接,可靠通信
TCP通信的步骤
服务端
- 创建ServerSocket对象,并填写服务端端口
- 调用ServerSocket对象的accept方法,等待客户端的连接,连接上后,返回一个Socket管道对象
- 通过得到的Socket对象调用getInputStream方法得到字节输入流,完成数据的接收
- 关闭释放资源,关闭socket连接
客户端
- 创建客户端对象
- 通过客户端对象调用字节输出流
- 把低级的字节输出流包装成数据输出流
- 用高级输出流对象,调用writeUTF()进行输出
- 关闭流,关闭客户端通信
当客户端建立连接后,后续又关闭连接,需要对服务端进行异常处理,否则服务端一直监听离线的客户端,会报错
4.1 TCP实现多发多收
必须将接收到的socket对象交给多个线程管理
聊天室多人通信---cs架构
bash
//客户端,负责向服务器发送消息,服务器转发给其他线程
public class TCPSocketConsumer {
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 8999);
new TCPSocketConsumerThead(socket).start();
OutputStream outputStream = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(outputStream);
Scanner sc=new Scanner(System.in);
System.out.println("服务已经启动,开始聊天");
while (true)
{
String line = sc.nextLine();
if (line.equals("exit"))
{
socket.close();
outputStream.close();
break;
}
dos.writeUTF(line);
}
}
}
bash
//客户端线程,负责接收服务端的消息,看到关于自己的线程信息
public class TCPSocketConsumerThead extends Thread{
private Socket socket;
public TCPSocketConsumerThead(Socket socket)
{
this.socket=socket;
}
@Override
public void run() {
try {
InputStream inputStream = socket.getInputStream();
DataInputStream dataInputStream = new DataInputStream(inputStream);
while(true)
{
try {
String s = dataInputStream.readUTF();
System.out.println(s);
} catch (Exception e) {
}
}
} catch (IOException e) {
System.out.println("自己下线了");
}
}
}
bash
//服务端,负责接收客户端的消息
public class TCPSocketServer {
public static List<Socket> sockets=new ArrayList<>();
public static void main(String[] args) throws Exception {
System.out.println("------服务端启动-----");
ServerSocket serverSocket = new ServerSocket(8999);
while (true)
{
Socket accept = serverSocket.accept();
sockets.add(accept);
System.out.println(accept.getInetAddress()+"已经上线");
TcpSocketServerThead thead=new TcpSocketServerThead(accept);
thead.start();
}
}
}
bash
//服务端线程,负责将服务端接收到的信息传递给其他客户端线程
public class TcpSocketServerThead extends Thread{
private Socket socket;
private DataInputStream dataInputStream;
public TcpSocketServerThead(Socket socket) {
this.socket=socket;
}
@Override
public void run()
{
while(true){ try
{
InputStream inputStream = socket.getInputStream();
dataInputStream = new DataInputStream(inputStream);
String s = dataInputStream.readUTF(dataInputStream);
transAll(s);
System.out.println(s);
} catch (Exception e) {
System.out.println(socket.getInetAddress()+"已经下线");
TCPSocketServer.sockets.remove(socket);
try {
socket.close();
dataInputStream.close();
break;
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
}}
public void transAll(String msg) throws IOException
{
for(Socket s:TCPSocketServer.sockets)
{
OutputStream outputStream = s.getOutputStream();
DataOutputStream dataOutputStream = new DataOutputStream(outputStream);
dataOutputStream.writeUTF(msg);
dataOutputStream.flush();
}
}
}
BS架构
bash
//之开发服务端,通过网页通信
public class BSSocket {
public static void main(String[] args) throws IOException {
System.out.println("------服务端启动-----");
ServerSocket serverSocket = new ServerSocket(8999);
while (true)
{
Socket accept = serverSocket.accept();
System.out.println(accept.getInetAddress()+"已经上线");
BSSocketThread bsSocketThread=new BSSocketThread(accept);
bsSocketThread.start();
}
}
}
bash
//线程
public class BSSocketThread extends Thread{
private Socket socket;
public BSSocketThread(Socket socket)
{
this.socket=socket;
}
@Override
public void run() {
try {
OutputStream outputStream = socket.getOutputStream();
PrintStream printStream=new PrintStream(outputStream);
printStream.println("HTTP/1.1 200 OK");
printStream.println("Content-Type:text/html;charset=UTF-8");
printStream.println();
printStream.println("<div style='color:red;'>程序员666</div>");
printStream.close();
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
使用BS架构实现通信的话,必须遵守HTTP协议
如果每次访问都创建一个线程,如果并发很高的话,就可能实现宕机。需要使用线程池