基于socket通信编写聊天工具

Socket网络通信,通信协议主要包含两种方式:TCP/UDP
其中TCP保证服务端获取到了数据,才能继续向服务端写入数据,而UDP则是不管服务端有没有收到消息,持续向服务端写入数据
Socket网络通信基本模型如下图:
在这里插入图片描述
首先 ,服务端开启连接监听服务,监听连接到服务端的连接
此时客户端,开始尝试向服务端获取连接,
当服务端接收到了客户端的连接后,通过此连接创建对应的Socket对象,并等待客户端发送数据
客户端此时发现已经连接上了服务端后,开始向服务端写入数据,
服务端接收到了客户端的数据,开始解析数据,并选择性的是否对客户端进行回应或者结束会话
客户端发送完数据后,可以选择等待接收服务端发送的数据,也可以结束会话
当客户端或者服务断开socket服务或者读写IO流关闭的时候,会话结束
项目最终代码已上传至GitHub:点击进入

1 socket初次尝试

新建项目
这里建了一个空的socketChat项目,同时新建了2个模块,一个是客户端client,一个是服务端service

在这里插入图片描述
IEDA开发工具,右键项目,选择new Module,

在这里插入图片描述
服务端开启服务并监听连接:

package com.lgli.socketservice;


import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.channels.SocketChannel;

/**
 * TCPService
 * 基于TCP协议的socket服务端
 * @author lgli
 * @since 1.0
 */
public class TCPService {

    /**
     * 监听端口
     */
    private static final int port = 8080;


    public static void main(String[] args) throws Exception{
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("等待客户端等待连接......");
        Socket socket = serverSocket.accept();
        InetAddress inetAddress = socket.getInetAddress();
        System.out.println("服务端获取到了客户端的一个连接......"+inetAddress);
        InputStream inputStream = socket.getInputStream();
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        String result  = "";
        while((result = reader.readLine()) != null){
            System.out.println(inetAddress+"说的是:"+result);
        }
        reader.close();
        inputStream.close();
        socket.close();
    }
}

客户端开启连接,并发送数据

package com.lgli.socketclient;

import java.io.BufferedOutputStream;
import java.io.OutputStream;
import java.net.Socket;

/**
 * TCPClient
 * 客户端发送请求连接
 * @author lgli
 * @since 1.0
 */
public class TCPClient {
    public static void main(String[] args) throws Exception{
        System.out.println("开始连接localhost服务端......");
        Socket socket = new Socket("localhost",8080);
        System.out.println("连接localhost服务端成功......");
        OutputStream outputStream = socket.getOutputStream();
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
        String str = "你好,服务端,我想连接你";
        bufferedOutputStream.write(str.getBytes(),0,str.getBytes().length);
        bufferedOutputStream.flush();
        bufferedOutputStream.close();
    }
}

然后开启服务端:
在这里插入图片描述
服务端等待客户端的连接
此时开启客户端:
在这里插入图片描述
然后来看服务端的处理结果:
在这里插入图片描述
服务端成功收到客户端发来的数据,并记录了连接来源127.0.0.1,(在本地自己搭建的,所以就是在自娱自乐了)

2 socket会话改进

基于上面的过程,如果服务端根据客户端发送的数据,回复数据,然后连接不关闭,继续监听,客户端同时也接收服务端发送的数据,然后根据服务端发送的数据,回复数据……持续这个过程,那么一个简易的一对一的聊天工具就成型了。
那么改造上述代码
先来服务端,改造2件事:持续监听、根据接收数据回复数据

package com.lgli.socketservice;


import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

/**
 * TCPService
 * 基于TCP协议的socket服务端
 * @author lgli
 * @since 1.0
 */
public class TCPService {

    /**
     * 监听端口
     */
    private static final int port = 8080;


    public static void main(String[] args) throws Exception{
        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("等待客户端等待连接......");
        Socket socket = serverSocket.accept();
        InetAddress inetAddress = socket.getInetAddress();
        System.out.println("服务端获取到了客户端的一个连接......"+inetAddress);
        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        while(true){
            //持续监听获取输入的数据
            String result  = reader.readLine();
            System.out.println("客户端:"+result);
            //服务端回复数据
            Scanner scanner = new Scanner(System.in);
            String next = scanner.next();
            writer.write(next);
            writer.newLine();
            writer.flush();
        }
    }


}

如代码所示,服务端持续获取客户端发送的数据,同时,接收后,等待用户输入,然后发送给客户端
客户端,类似的改造2件事,持续向服务端发送数据、接收服务端返回的数据
package com.lgli.socketclient;

import java.io.*;
import java.net.Socket;
import java.util.Scanner;

/**

  • TCPClient
  • 客户端发送请求连接
  • @author lgli
  • @since 1.0
    */
    public class TCPClient {
    public static void main(String[] args) throws Exception{
    System.out.println(“开始连接localhost服务端…”);
    Socket socket = new Socket(“localhost”,8080);
    System.out.println(“连接localhost服务端成功…”);
    BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
    BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    while(true){
    //持续输入数据
    Scanner scanner = new Scanner(System.in);
    String next = scanner.next();
    writer.write(next);
    writer.newLine();
    writer.flush();
    //读取服务端发送的数据
    String s = reader.readLine();
    System.out.println(“服务端:”+s);
    }
    }
    }
    先启动服务端,后启动客户端,我们可以看到理想的效果了。。。
    额 CSDN上传的图片不能超过5M,而且不能上传视频。。。
    有需要的可以关注我的公众号,那里更详细些
    在这里插入图片描述

3 服务端和多客户端通信

目前这里服务端和客户端是一对一的关系,这里我们需要服务端同时可以接受到来自许多的客户端的连接,并建立会话。如何做?
简单来说就是,服务端需要持续监听这个连接,然后来一个连接,则创建会话,同时自己也要依然监听。这里很简单的,我们需要用一个多线程来做这个操作,当创建好一个套接字Socket连接,则获取一个线程去做会话操作,主线程依然在循环监听socket连接。
客户端很简单的,直接复制一个出来,叫clients,代码几乎一模一样了。

在这里插入图片描述
需要对服务端做一些修改:

package com.lgli.socketservice;


import java.io.*;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;

/**
 * TCPService
 * 基于TCP协议的socket服务端
 * @author lgli
 * @since 1.0
 */
public class TCPService {

    /**
     * 监听端口
     */
    private static final int port = 8080;


    public static void main(String[] args) throws Exception{
        //循环监听,监听有连接,则放入线程,让线程去实现方法
        ServerSocket serverSocket = new ServerSocket(port);
        while(true){
            System.out.println("等待客户端等待连接......");
            //获取到一个socket连接
            final Socket socket = serverSocket.accept();
            //获取Runnable对象
            Runnable runnable = ()->{
                try{
                    InetAddress inetAddress = socket.getInetAddress();
                    int port = socket.getPort();
                    String clientName = inetAddress+":"+port;
                    System.out.println("服务端获取到了客户端的一个连接......"+clientName);
                    BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                    BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
                    while(true){
                        //持续监听获取输入的数据
                        String result  = reader.readLine();
                        System.out.println("客户端("+clientName+"):"+result);
                        //服务端回复数据
                        Scanner scanner = new Scanner(System.in);
                        String next = scanner.next();
                        writer.write(next);
                        writer.newLine();
                        writer.flush();
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }

            };
            //启动新的线程去执行这个会话操作
            Thread thread = new Thread(runnable);
            thread.start();
        }
    }


}

这里我们也就是多开启线程去执行操作。上面代码实例化Runnable对象,使用了Java8的lambda表达式,相对于之前的写法,此写法方便更好的阅读代码。具体可查看官方介绍
此时,我们也可以得到理想的效果了。可以到公众号上去看。

4 服务端和客户端UDP协议通信

UDP协议通信,客户端不在需要得到服务端的相应,只管发送数据,不需要得到服务端的相应
这里就简单的举个例子
服务端

package com.lgli.socketservice;

import java.net.DatagramPacket;
import java.net.DatagramSocket;

/**
 * UDPService
 *
 * @author lgli
 * @since 2020/5/4 19:35
 * @since 1.0
 */
public class UDPService {

    private static final int port = 8081;

    public static void main(String[] args) throws Exception{
        DatagramSocket datagramSocket = new DatagramSocket(port);
        //预先执行获取打包数据
        byte [] receive = new byte[2048];
        DatagramPacket datagramPacket = new DatagramPacket(receive,receive.length);
        datagramSocket.receive(datagramPacket);
        System.out.println("服务端接收到来自"+datagramPacket.getAddress()+":"+datagramPacket.getPort()+"的数据:"+datagramPacket.getData());
    }
}

客户端

package com.lgli.socketclient;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * UDPClient
 *
 * @author lgli
 * @since 2020/5/4 10:58
 * @since 1.0
 */
public class UDPClient {
    public static void main(String[] args) throws Exception{
        String data = "你好,客户端";
        //打包发送数据
        DatagramPacket datagramPacket = new DatagramPacket(data.getBytes(),data.getBytes().length, InetAddress.getByName("localhost"),8081);
        DatagramSocket datagramSocket = new DatagramSocket();
        while(true){
            datagramSocket.send(datagramPacket);
            System.out.println("客户端1发送了数据.....");
        }
    }

}

这里我们单纯只是运行客户端,我们可以看到,没有报错发生,而且数据也持续的在发送,在这里插入图片描述
启动服务端,接收到客户端发送的数据:
在这里插入图片描述
基于UDP通信方式,相较于TCP而言,服务端接收数据可能存在不连续,数据丢失等情况,对于响应速度而言,UDP是要优于TCP的。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值