美文网首页
Java笔记——Socket通信

Java笔记——Socket通信

作者: 麦香菌 | 来源:发表于2018-07-06 19:41 被阅读0次

重要的类

ServerSocket与Socket

1.ServerSocket类

ServerSocket类主要功能是在指定端口处建立监听服务。

ServerSocket serverSocket = new ServerSocket(Configuration.PORT);

一台计算机可以开启多个客户端,不同客户端用随机分配的端口区别,ServerSocket在指定端口阻塞监听客户端的请求。

Socket socket = serverSocket.accept();

若有客户端的socket请求连接,在服务端建立一个对应的Socket与之进行通信,再执行之后的语句。

2.Socket类

客户端可创建一个Socket对象连接服务端进行通信:

Socket socket = new Socket(InetAddress.getLocalHost(), Configuration.PORT);

Socket类的构造函数有两个参数,第一个参数是欲连接到的Server计算机的主机地址,第二个参数是该ServerSocket监听的端口号。
Socket对象建立成功之后,就可以在Client和Server之间建立一个连接,利用Socket类的方法getOutputStream()和getInputStream()获取和传输数据。

服务端

Socket实现多线程通信

为实现多个客户端连接服务器,解决ServerSocket.accept()阻塞的问题,把服务端对客户端的处理全部写到独立的线程里,并循环监听下一个客户端的连接请求。

while (true) {
    // 接收客户端Socket,监听,阻塞等待客户端连接,创建socket实例
    Socket socket = serverSocket.accept();
    // 客户端IP
    String ip = socket.getInetAddress().getHostAddress();
    // 客户端端口
    int port = socket.getPort();
    System.out.println("客户端UID:"+ip+":"+port);
    // 建立新线程
    Runnable runnable=new HandleMessageRunnable(socket, ip, port);
    Thread handleClientThread=new Thread(runnable);
    handleClientThread.start();
}

服务端线程

通过构造函数获取客户端的socket和clientUid

public HandleMessageRunnable(Socket socket, String ip, int port) {
    this.socket = socket;
    this.ip = ip;
    this.port = port;
    this.clientUid = ip + ":" + port;
}
1.添加客户端

将clientUid添加到ArrayList<String>,并通过HashMap实现clientUid与线程HandleMessageRunnable的映射关系。

//添加客户端
public void addClient() {
    clientUidArrayList.add(clientUid);
    hashMap.put(clientUid, this);
}
2.发送连接成功消息

通过输出流将当前客户连接成功的消息发送给对应客户端

public void sendConnectedMessage() throws IOException {
    // 获取输入流
    socketInputStream = socket.getInputStream();
    // 获取输出流
    socketOutputStream = socket.getOutputStream();
    // 向当前客户端传输连接成功信息
    String successMessage ="服务端提示信息:"+Configuration.NEWLINE+ 
                Util.getCurrentTime() + Configuration.NEWLINE + "成功连接服务器" +
                Configuration.NEWLINE+ "服务器IP: " + Util.getLocalHostAddress() + 
                ", 端口: " + Configuration.PORT + Configuration.NEWLINE+ "客户端IP: " 
                + ip + ", 端口: " + port + Configuration.NEWLINE;
    socketOutputStream.write(successMessage.getBytes());
}
3.更新所有在线客户端名单

a.为StringBuilder的开头添加消息类型(Configuration.TYPE_UPDATEONLINELIST)与分隔符(Configuration.SEPARATOR)。

StringBuilder(Configuration.TYPE_UPDATEONLINELIST + Configuration.SEPARATOR);

b.遍历客户列表(clientUidArrayList),用StringBuilder将客户列表里所有的clientUid拼接在一起,并用逗号隔开。

for (String clientUid : clientUidArrayList) {
    stringBuilder.append(clientUid);
    int index = clientUidArrayList.indexOf(clientUid);
    int size =  clientUidArrayList.size();
    if (index != size - 1) {
        stringBuilder.append(",");
    }
}

c.再次遍历客户列表,通过HashMap的映射关系将在线客户端名单一一传给每个客户端。

String  onlineClients=stringBuilder.toString();
for (String clientUid : clientUidArrayList) {
    OutputStream out = hashMap.get(clientUid).socket.getOutputStream();
    out.write(onlineClients.getBytes());
}

客户端

初始化客户端界面

a.创建对象,取消窗口关闭键。

// 创建客户端窗口对象
clientFrame = new ClientFrame();
// 窗口关闭键无效,必须通过退出键退出客户端
clientFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);

b.通过获取本机的屏幕分辨率设置客户端窗口的位置

int screenWidth = Util.getScreenWidth();
int screenHeight = Util.getScreenHeight();
int x = (screenWidth - Configuration.CLIENT_FRAME_WIDTH) / 2;
int y = (screenHeight - Configuration.CLIENT_FRAME_HEIGHT) / 2;
// 设置位置
clientFrame.setLocation(x, y);
// 设置窗口可见
clientFrame.setVisible(true);

连接服务端

a.创建socket对象连接服务器

Socket socket = new Socket(InetAddress.getLocalHost(), Configuration.PORT);

b.将创建的socket对象传递给客户端界面类(ClientFrame)

clientFrame.setSocket(socket);

c.在聊天消息框显示连接成功的欢迎信息

// 获取输入流
socketInputStream = socket.getInputStream();
// 获取输出流
socketOutputStream = socket.getOutputStream();
// 获取服务端发送的欢迎信息
byte[] buf = new byte[1024 * 1];
int len = socketInputStream.read(buf);
// 将欢迎信息显示在聊天消息框
String welcomeMessage=new String(buf, 0, len);
clientFrame.messageJTextArea.append(welcomeMessage);
clientFrame.messageJTextArea.append(Configuration.NEWLINE);

数据的发送、处理与接收

数据的发送、处理与接收

客户端界面(发送数据)

选择发送对象

a.在线名单列表:用int[]获取鼠标选中的在线客户行数,遍历int[],用StringBuilder拼接所有选中的客户的clientUid,并用逗号隔开。

int[] selectedIndex = onlineJTable.getSelectedRows();
// 将所有消息目标的uid拼接成一个字符串,以逗号分隔
receiverStringBuilder = new StringBuilder("");
for (int i = 0; i < selectedIndex.length; i++) {
    //获取第i行第0列的ip数据
    String ip = (String) tableModel.getValueAt(selectedIndex[i], 0);
    //获取第i行第1列的端口数据
    String port = (String) tableModel.getValueAt(selectedIndex[i], 1);
    receiverStringBuilder.append(ip);
    receiverStringBuilder.append(":");
    receiverStringBuilder.append(port);
    if (i != selectedIndex.length - 1) {
        receiverStringBuilder.append(",");
    }
}

b.发送消息按钮:向发送的客户名单发送消息,在聊天窗显示发送的消息

// 向服务器发送聊天信息
OutputStream out = socket.getOutputStream();
out.write((Configuration.TYPE_CHAT + Configuration.SEPARATOR + receivers +
          Configuration.SEPARATOR+ message).getBytes());                
messageJTextArea.append(Util.getCurrentTime() + 
          Configuration.NEWLINE + "发往 " + receivers+ Configuration.NEWLINE);
// 在聊天窗显示发送消息
messageJTextArea.append(message + Configuration.NEWLINE);

清空文本输入框

//清空文本输入框
sendJTextArea.setText("");

c.退出与清屏

// 向服务器发送退出信息
OutputStream out = socket.getOutputStream();
out.write((Configuration.TYPE_EXIT + Configuration.SEPARATOR).getBytes());
System.exit(0);
messageJTextArea.setText("");

服务端(处理数据)

循环监听并转发客户端的数据

a.读取输入流数据

len = socketInputStream.read(buf);
String message = new String(buf, 0, len);

b.获取分隔符在字符串的位置,将字符串拆分为消息类型与消息主题两部分

int separatorIndex=message.indexOf(Configuration.SEPARATOR);
// 消息类型:退出或者聊天
String messageType = message.substring(0, separatorIndex);
// 消息本体:空或者聊天内容
String messageContent = message.substring(separatorIndex + 1);

c.处理数据:若数据类型为退出客户端,则将客户端信息从clientUid列表和HashMap中移除

// 更新ArrayList和HashMap, 删除退出的uid和线程
removeClient();
// 更新在线名单
updateOnlineList();
// 结束循环,结束该服务线程
break;

更新在线客户名单,结束循环与线程

public void removeClient() {
    int index = clientUidArrayList.indexOf(clientUid);
    clientUidArrayList.remove(index);
    hashMap.remove(clientUid);
}

d.处理数据:若数据类型为聊天,则根据逗号位置拆分获取收信人地址与聊天内容

int messageContentSeparatorIndex=messageContent.indexOf(Configuration.SEPARATOR);
// 提取收信者地址
String[] receiveClientUidArray = messageContent.substring(0, 
                                        messageContentSeparatorIndex).split(",");
// 提取聊天内容
String word = messageContent.substring(messageContentSeparatorIndex + 1);

遍历所有要发送的客户端,通过输出流一一发送数据

public void sendChatMessage(String sendClientUid, String[] receiveClientUidArray, 
    String word) throws IOException {
    for (String clientUid : receiveClientUidArray) {
        OutputStream out = hashMap.get(clientUid).socket.getOutputStream();
        String message=Configuration.TYPE_CHAT+Configuration.SEPARATOR + 
                    sendClientUid + Configuration.SEPARATOR + word;
        out.write(message.getBytes());
    }
}

客户端(接受数据)

循环监听服务端处理完发来的消息

a.读取输入流数据

byte[] buf = new byte[1024 * 1];
socketInputStream = socket.getInputStream();
int len = socketInputStream.read(buf);
// 处理服务器传来的消息
String message = new String(buf, 0, len);

b.获取分隔符在字符串的位置,将字符串拆分为消息类型与消息主体两部分

int separatorIndex=message.indexOf(Configuration.SEPARATOR);
// 消息类型:更新在线名单或者聊天(发送类型)
String messageType = message.substring(0, separatorIndex);
// 消息本体:最新的在线名单或者聊天内容(发送内容)
String messageContent = message.substring(separatorIndex + 1);

c.处理消息:若消息类型为更新列表,则将消息主体拆分为ip地址与端口号,清空更新在线名单界面


在线名单

d.处理消息:若消息类型为聊天,则获取分隔符在消息主体的位置,将消息主体拆分为发送者与发送内容两部分

if (messageType.equals(Configuration.TYPE_CHAT)) {
    int messageContentSeparatorIndex=messageContent.indexOf(Configuration.SEPARATOR);
    String from = messageContent.substring(0, messageContentSeparatorIndex);//发送者
    String word = messageContent.substring(messageContentSeparatorIndex + 1);//发送内容
    clientFrame.messageJTextArea.append(Util.getCurrentTime() + Configuration.NEWLINE 
                + "来自 "+ from+ Configuration.NEWLINE + word + Configuration.NEWLINE);
    clientFrame.messageJTextArea.setCaretPosition(clientFrame.messageJTextArea.
                getDocument().getLength());
}

局域网内两台主机的通信

主机1
主机2

相关文章

  • Java socket

    Java Socket实现基于TCP和UDP多线程通信Java Socket编程

  • 相关文章

    我的笔记核心理论基础、Socket通信原理、RPC远过程调用协议、Dubbo博客 java注解-01、java注解...

  • Socket 通信 知识梳理

    1.socket是网络通信的一项技术,android的socket通信其实使用的是java的socket通信技术。...

  • Java笔记——Socket通信

    重要的类 1.ServerSocket类 ServerSocket类主要功能是在指定端口处建立监听服务。 一台计算...

  • Java socket应用—通信

    ** |Java socket应用—通信|** 1、网络基础简介 ...

  • 基于TCP的Socket网络编程基础(Java)

    Socket通信作为Java网络通讯的基础内容,建立网络通信连接至少要一对端口号(socket)。socket本...

  • Android中Socket编程(一)

    Socket通信简介 Java Socket可实现客户端-服务端的双向实时通信。在java.net包中定义了两个类...

  • 1.普通Socket的用法

    Java中网络通信是通过Socket实现的。Socket分为ServerSocket和Socket两大类。Serv...

  • java socket通信

    socket通信是通信的一种,有客户端和服务器端 服务器端是一个阻塞的状态,在死循环中,如果没有连接就会一直阻塞....

  • Java Socket通信

    目标 Demo是通过Java ServerSocket 和 Socket 通信实现客户端发送消息和发送文件到服务器...

网友评论

      本文标题:Java笔记——Socket通信

      本文链接:https://www.haomeiwen.com/subject/fzlbuftx.html