对于步入编程行业不深的初学者或是已经有所领会的人来说,当学习一项新的技术的时候,非常渴望有一个附上注释完整的Demo。本人深有体会,网上的例子多到是很多,但是很杂不完整,写代码这种东西来不得半点马虎,要是错了一点,那也是运行不了的。这对于初学者来说更加的头疼,因为他根本不知道错在哪里,盲目的改只能错上加错。最后不得不去找找看看有没有能够直接运行的例子再加以模仿。

下面是博主在学习Java的socket时写的一个完整的例子,并且带上了完整的注释。它是一个简单的聊天程序,但是它可以设置任意多用户同时登录,然后相互两两交流。博主仅仅在自己电脑上实现同时登录,然后两两相互交流。

程序的大体思路是这样的:

①该用户作为服务端也就是被请求连接端和主动请求连接其他端时不一样的,其次有可能被其他的用户连接很多次,那么你作为服务端,就会有很多连接,同样的道理,你作为客户端也会有很多的连接。为了程序更加通俗易懂,博主写的时候,设置了很多容器,将不一样的东西分开放置。做到解耦合,不然到后面自己都分不清了。

②你可以一次写两个类,client1,client2,,client1先作为服务端,client2作为客户端,客户端去连接服务端,从而实现client1的服务端功能和client2的客户端功能。每次实现一个功能就先将服务端和客户端的功能整合一下,互换角色,看是否存在错误。

③在实现了两个用户的情况下再去写第三个类client3,代码就是复制粘贴。当然你也可以直接创建一个client3的类,然后直接在类的main方法中改了端口号,和用户名就行。第三个实现后,第四个第五个也就实了。

下面是具体的代码:

package jack;

import java.awt.List;

import java.awt.event.ActionEvent;

import java.awt.event.ActionListener;

import java.io.BufferedReader;

import java.io.DataInputStream;

import java.io.DataOutputStream;

import java.io.EOFException;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.PrintWriter;

import java.net.ServerSocket;

import java.net.Socket;

import java.time.format.TextStyle;

import java.util.ArrayList;

import javax.swing.JButton;

import javax.swing.JFrame;

import javax.swing.JLabel;

import javax.swing.JPanel;

import javax.swing.JScrollPane;

import javax.swing.JTabbedPane;

import javax.swing.JTextArea;

import javax.swing.JTextField;

import javax.swing.JToolBar;

import javax.swing.plaf.basic.BasicTabbedPaneUI.TabbedPaneLayout;

import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter.DEFAULT;

import org.omg.CORBA.PRIVATE_MEMBER;

/**

* 被请求连接时称为服务器;主动连接对方时称为客户端。

* @author Administrator

*

*/

public class Client4 extends JFrame implements ActionListener{

private static int sign = 0; //用来记录选项卡的标签,0标示第一个。

private JToolBar toolBar1,toolBar2;

private JLabel waitPortLabel,hostLabel,portLabel;

private JTextField waitPortTF,hostTF,portTF,sendTF;

private JTabbedPane tab;

private JButton sendB,leaveB,deleteB,connB;

private ArrayList serverThreads; //存放服务器线程的容器。即存放被请求连接时所创建的线程的容器。

private ArrayList clientThreads; //存放客户端线程的容器。即存放主动连接对方时所创建的线程的容器。

private ArrayList servers; //服务对象的容器。

private ArrayList serverTextAreas; //作为服务器时,存放所创建的对话记录显示区域的容器。

private ArrayList clientTextAreas; //作为客户端时,岑芳所创建的对话记录显示区域的容器。

private ArrayList serverSockets; //存放被请求连接时所创建的socket的容器。

private ArrayList clientSockets; //存放主动请求连接时所创立的socket的容器。

private ArrayList serverPWriters; //存放服务端输出流对象的容器。

private ArrayList serverBReaders; //存放服务端输入流对象的容器。

private ArrayList clientPWriters; //存放客户端输出流对象的容器。

private ArrayList clientBReaders; //存放客户端输入流对象的容器。

private ArrayList ports; //存放作为服务器时已连接的端口。

private int port = 2041; //指定自己开放的第一个端口号,方便其他人连接。

private String name; //储存自己的名称。

public Client4(String name) throws IOException{

super(name);

this.name = name;

toolBar1 = new JToolBar();

toolBar2 = new JToolBar();

waitPortLabel = new JLabel("等待端口");

hostLabel = new JLabel("主机");

portLabel = new JLabel("端口");

waitPortTF = new JTextField("2041");

hostTF = new JTextField("127.0.0.1");

portTF = new JTextField(5);

sendTF = new JTextField();

tab = new JTabbedPane();

sendB = new JButton("发送");

leaveB = new JButton("离线");

deleteB = new JButton("删除页");

connB = new JButton("连接端口");

servers = new ArrayList();

serverTextAreas = new ArrayList();

clientTextAreas = new ArrayList();

serverSockets = new ArrayList();

clientSockets = new ArrayList();

serverPWriters = new ArrayList();

serverBReaders = new ArrayList();

clientPWriters = new ArrayList();

clientBReaders = new ArrayList();

serverThreads = new ArrayList();

clientThreads = new ArrayList();

ports = new ArrayList();

toolBar1.add(waitPortLabel);

toolBar1.add(waitPortTF);

toolBar1.add(hostLabel);

toolBar1.add(hostTF);

toolBar1.add(portLabel);

toolBar1.add(portTF);

toolBar1.add(connB);

toolBar2.add(sendTF);

toolBar2.add(sendB);

toolBar2.add(leaveB);

toolBar2.add(deleteB);

waitPortTF.setEnabled(false); //设置等待的textfield不可以编辑。

hostTF.setEnabled(false); //设置连接的ip地址不可编辑,当然这里可以更改成其他电脑的ip地址。

this.getContentPane().add(toolBar1, "North"); //添加工具栏到最上方。

this.getContentPane().add(tab,"Center"); //添加选项卡窗格。

this.getContentPane().add(toolBar2,"South"); //添加工具栏到下方。

this.setBounds(200, 200, 350, 300);

this.setVisible(true);

this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

sendB.addActionListener(this);

leaveB.addActionListener(this);

deleteB.addActionListener(this);

connB.addActionListener(this);

//主线程进入之后 在server.accept()阻塞等待客户端来连接。

while(true){

ServerSocket server = new ServerSocket(port); //作为服务器,开发自己供连接的端口。

servers.add(server);

Socket serverSocket = server.accept(); //等待对方连接。

serverSockets.add(serverSocket);

ports.add(port); //将已连接的端口加入容器。

PrintWriter serverPWriter = new PrintWriter(serverSocket.getOutputStream(),true);//初始化输出流对象。

serverPWriters.add(serverPWriter);

BufferedReader serverBReader = new BufferedReader(new InputStreamReader(serverSocket.getInputStream()));//初始化输入流对象。

serverBReaders.add(serverBReader);

serverPWriter.println("连接"+name+"成功"); //将连接成功的消息发送给请求方,提醒对方连接成功。

serverPWriter.println(name); //将自己的名称发送给对方,方便对方设置选项卡的名称。

String content = serverBReader.readLine(); //此时对方也发送了上面两条消息过来。读入对方发送过来的提醒消息。

String name2 = serverBReader.readLine(); //读取对方的名称。方便后面设置选项卡的名称。

System.out.println(content);

System.out.println(name2);

MyJTextArea serverTextArea = new MyJTextArea(sign);

sign++; //new了一个textArea后,sign自动增加1,好和选项卡对应,

//知道这个选项卡加到哪个容器了,是服务器的还是客户端的。

serverTextAreas.add(serverTextArea);

this.tab.addTab(name2,new JScrollPane(serverTextArea));//在选项卡窗格中添加一个选项卡。

serverTextArea.setEditable(false); //设置对话记录显示区域不可编辑。

serverTextArea.setLineWrap(true); //设置对话记录显示区域自动换行。

serverTextArea.append(content+"\n"); //在对话记录区域输出连接成功这条消息。

ChatThread thread = new ChatThread(); //new了一个线程去执行run方法,用于和对方交流,对方也会开启一个线程来和你交流。

//这里要开启新线程的原因是main线程经过一轮后会在上面accept方法处阻塞。

serverThreads.add(thread);

thread.start(); //启动该线程,方便接收对方发来的消息。

port++; //端口号加一,开放下一个供连接的端口。

waitPortTF.setText(port+""); //更新显示等待的下个端口。

}

}

private class ChatThread extends Thread {

private String[] serverContents = new String[10]; //当作为服务端时,用来存放相互发送消息时的一句话。

private String[] clientContents = new String[10]; //当作为客户端时,用来存放相互发送消息时的一句话。

private boolean isServerThread = true; //判断当前在执行run方法的线程是不是服务端线程。

@Override

public void run() {

while(true){

if(serverThreads.size()>0){ //判断当前的线程是否是服务线程。先判断是否大于0,是为了防止serverThreads

for(int i=0;i

if(Thread.currentThread() == serverThreads.get(i)){ //拿当前线程和服务端容器里的线程去比,看是否是服务端的线程。

isServerThread = true;

}

}

}

if(clientThreads.size()>0){ //判断当前的线程是否是客户线程。 先判断是否大于0,是为了防止clientThreads

for(int i=0;i

if(Thread.currentThread() == clientThreads.get(i)){ //拿当前线程和客户端容器里的线程去比,看是否是客户端的线程。

isServerThread = false;

}

}

}

if(isServerThread){ //如果是服务端的线程,将readline方法接受到的值赋给相应的content。

for(int i=0;i

if(Thread.currentThread() == serverThreads.get(i)){ //判断具体是服务端里的那条线程。

try {

serverContents[i] = serverBReaders.get(i).readLine();//将对方发送过来的消息赋值给这条线程的接受消息字符串。

} catch (IOException e) {

e.printStackTrace();

return; //出现异常时直接退出方法。

}

if(serverContents[i]==null){ //在自己点击离线按钮时,serverContents[i]为null,

return; //因此在这里进行处理,避免后面报错。

}

if(serverContents[i].equals("关闭")){ //接收到对方因点击离开按钮而发出的消息“离开”,关闭自己的连接。

sendTF.setText("已断开连接");

serverPWriters.get(i).close();

try {

serverBReaders.get(i).close();

serverSockets.get(i).close();

} catch (IOException e) {

e.printStackTrace();

}

return; //关闭完后退出。

}

serverTextAreas.get(i).append(serverContents[i]+"\n");//将接受到的消息显示在自己的对话记录区域。

break;

}

}

}else{ //如果是客户线程,将readline方法接受到的值赋给相应的content。

for(int i=0;i

if(Thread.currentThread() == clientThreads.get(i)){ //判断具体是客户端中的哪一条线程。

try {

clientContents[i] = clientBReaders.get(i).readLine();//拿到对方发送过来的消息并保存给自己的字符串。

} catch (IOException e) {

e.printStackTrace();

}

if(clientContents[i] == null){ //当自己点击离线按钮时,clientContents[i]会为null,

return; //为了防止下面报错,在这里进行处理。

}

if(clientContents[i].equals("关闭")){ //接收到对方因点击离线按钮而发出的消息“关闭”,而关闭自己的连接。

sendTF.setText("已断开连接");

clientPWriters.get(i).close();

try {

clientBReaders.get(i).close();

clientSockets.get(i).close();

} catch (IOException e) {

e.printStackTrace();

}

return;

}

clientTextAreas.get(i).append(clientContents[i]+"\n");//作为客户端时,将接受到的消息显示在对话记录显示区域。

break;

}

}

}

}

}

}

@Override

public void actionPerformed(ActionEvent e) {

switch(e.getActionCommand()){

case "连接端口": //如果是连接端口,则执行以下操作。

try {

Socket clientSocket = new Socket(hostTF.getText(),Integer.parseInt(portTF.getText()));//拿到工具栏一中填的端口号并生成socket去连接对方。

clientSockets.add(clientSocket);

PrintWriter clientPWriter = new PrintWriter(clientSocket.getOutputStream(),true);//初始化输出流对象。

clientPWriters.add(clientPWriter);

BufferedReader clientBReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));//初始化输入流对象。

clientBReaders.add(clientBReader);

clientPWriter.println("连接"+name+"成功"); //将连接成功的消息发送给对方,提醒对方连接成功。

clientPWriter.println(name); //将自己的名称发送给对方,方便对方设置选项卡的名称。

System.out.println(11111111);

String content = clientBReader.readLine(); //读入对方发送过来的提醒消息。对方已执行了上面两条语句,发送了对应的消息。

System.out.println(22222222);

String name2 = clientBReader.readLine(); //读取对方的名称。

System.out.println(content);

System.out.println(name2);

MyJTextArea clientTextArea = new MyJTextArea(sign);

sign++; //配和选项卡的index,记录每个选项卡加到那个容器里去了,是服务器的容器还是客户端的容器。

clientTextAreas.add(clientTextArea);

this.tab.addTab(name2,new JScrollPane(clientTextArea)); //在选项卡窗格中添加一个选项卡。

clientTextArea.setEditable(false); //设置对话记录区域不可编辑。

clientTextArea.setLineWrap(true); //设置对话记录区域自动换行。

clientTextArea.append(content+"\n"); //在对话记录区域显示连接成功这条消息。

ChatThread clientThread = new ChatThread();

clientThreads.add(clientThread);

clientThread.start(); //启动该线程,方便和对方相互发送消息,因为主线程已经在上面accept()处阻塞。

} catch (IOException e1) {

e1.printStackTrace();

}

break;

case "发送":

if(serverTextAreas.size()>0){ //如果是被请求连接时而创建的选项卡要发送消息。即服务端。

for(int i=0;i

if(tab.getSelectedIndex() == serverTextAreas.get(i).getSign()){ //通过获取当前选项卡的index去和jtextArea的sign比,因为他们的index和sign是匹配的,

String sendContent = sendTF.getText(); //从而确定它是服务端的哪条线程,该用那对输入输出流进行发送和接受消息。

if(serverSockets.get(i).isClosed()){ //如果已断开连接,则直接返回。

sendTF.setText("已断开连接");

return;

}

if(sendContent.equals("")){ //如果发送的内容为空则直接接结束。

sendTF.setText("请输入内容");

return;

}else{

serverPWriters.get(i).println(name+": "+sendContent); //将发送消息框中的消息发送出去,并在前面加上自己的姓名。

serverTextAreas.get(i).append("我: "+sendContent+"\n");//在自己的对话记录区域加上这句话。

sendTF.setText(""); //将发送消息框中的数据清空。

return;

}

}

}

}

if(clientTextAreas.size()>0){ //如果是客户端。

for(int i=0;i

if(tab.getSelectedIndex() == clientTextAreas.get(i).getSign()){ //通过获取当前选项卡的index去和jtextArea的sign比,因为他们的index和sign是匹配的,

String sendContent = sendTF.getText(); //从而确定它是客户端的哪条线程,该用那对输入输出流进行发送和接受消息。

if(clientSockets.get(i).isClosed()){ //如果连接已断开则直接返回。

sendTF.setText("已断开连接");

return;

}

if(sendContent.equals("")){ //如果发送的内容为空则直接接结束。

sendTF.setText("请输入内容");

return;

}else{

clientPWriters.get(i).println(name+": "+sendContent); //将发送消息框中的消息发送出去,并在前面加上自己的姓名。

clientTextAreas.get(i).append("我: "+sendContent+"\n"); //在自己的对话记录区域加上这句话

sendTF.setText(""); //将发送消息框中的数据清空。

return;

}

}

}

}

break;

case "离开":

if(serverTextAreas.size()>0){ //如果是服务端。

for(int i=0;i

if(tab.getSelectedIndex() == serverTextAreas.get(i).getSign()){ //更前面一样的道理,判断是服务端的那个选项卡需要闭关。

serverPWriters.get(i).println("关闭"); //发送关闭消息,提醒对方也要关闭该socket连接。

sendTF.setText("已断开连接");

serverPWriters.get(i).close();

try {

serverBReaders.get(i).close();

serverSockets.get(i).close();

} catch (IOException e1) {

e1.printStackTrace();

}

break;

}

}

}

if(clientTextAreas.size()>0){ //如果是客户端。

for(int i=0;i

if(tab.getSelectedIndex() == clientTextAreas.get(i).getSign()){ //更前面一样的道理,判断是客户端的那个选项卡需要闭关。

clientPWriters.get(i).println("关闭"); //发送关闭消息,提醒对方也要关闭该socket连接。

sendTF.setText("已断开连接");

clientPWriters.get(i).close();

try {

clientBReaders.get(i).close();

clientSockets.get(i).close();

} catch (IOException e1) {

e1.printStackTrace();

}

break;

}

}

}

break;

case "删除页":

if(serverTextAreas.size()>0){ //为了防止下面的serverTextAreas数组越界。

for(int i=0;i

if(tab.getSelectedIndex() == serverTextAreas.get(i).getSign()){ //跟上面一样的道理,判断当前选项卡是属于服务端还是客户端。

if(serverSockets.get(i).isClosed()){ //先判断是否断开连接,否则不允许关闭。

tab.remove(i); //删除当前选项卡。

}else{

sendTF.setText("请先关闭当前的连接");

return;

}

}

}

}

if(clientTextAreas.size()>0){ //为了防止下面的serverTextAreas数组越界。

for(int i=0;i

if(tab.getSelectedIndex() == clientTextAreas.get(i).getSign()){ //跟上面一样的道理,判断当前选项卡是属于服务端还是客户端。

if(clientSockets.get(i).isClosed()){ //先判断是否断开连接,否则不允许关闭。

tab.remove(i); //删除当前选项卡。

}else{

sendTF.setText("请先关闭当前的连接");

return;

}

}

}

}

break;

default:

break;

}

}

public static void main(String[] args) throws IOException{

new Client4("喜洋洋");

}

}

效果图:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

java tcp聊天程序_java实现基于Tcp的socket聊天程序相关推荐

  1. java tcp 编程实例_Java实现基于TCP的通讯程序实例解析

    Java中的TCP通信程序 TCP可以实现两台计算机之间的数据交互通信的两端,要严格区分客户端与服务端 两端通信时的步骤: 1.服务端程序,需要事先启动,等待客户端连接 2.客户端主动连接服务器端,才 ...

  2. TCP/IP网络编程之基于TCP的服务端/客户端(一)

    TCP/IP网络编程之基于TCP的服务端/客户端(一) 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于 ...

  3. TCP/IP网络编程之基于TCP的服务端/客户端(二)

    回声客户端问题 上一章TCP/IP网络编程之基于TCP的服务端/客户端(一)中,我们解释了回声客户端所存在的问题,那么单单是客户端的问题,服务端没有任何问题?是的,服务端没有问题,现在先让我们回顾下服 ...

  4. 我用 tensorflow 实现的“一个神经聊天模型”:一个基于深度学习的聊天机器人

    我用 tensorflow 实现的"一个神经聊天模型":一个基于深度学习的聊天机器人 个工作尝试重现这个论文的结果A Neural Conversational Model(aka ...

  5. Java网络编程,使用Java实现UDP和TCP网络通信协议,以及基于UDP的在线聊天室。

    文章目录 前言 一.网络编程概念 1.网络 2. 网络编程的目的 3.想要达到这个效果需要什么 4.网络分层 二.网络编程Java类 1.IP地址:InetAddress 2.端口 3.TCP连接 3 ...

  6. 基于C#的socket聊天室(附源码)

    基于C#-socket聊天室 前言 源代码:https://gitee.com/TL0902/term/blob/master/C%23%E8%81%8A%E5%A4%A9%E5%AE%A4/Tcha ...

  7. 在Autodesk应用程序商店发布基于浏览器的Web应用程序

    你一定已经听说过Autodesk应用程序商店了,通过Autodesk应用程序商店,你可以免费下载或购买来自全球的优秀开发者发布的应用程序,来帮助你更快更方便的完成你的工作.而且作为开发者,您也可以在A ...

  8. java开发websocket聊天室_java实现基于websocket的聊天室

    [实例简介] java实现基于websocket的聊天室 [实例截图] [核心代码] chatMavenWebapp └── chat Maven Webapp ├── pom.xml ├── src ...

  9. java实现端口映射_Java BIO实现TCP端口转发(端口映射)功能源码

    开发环境及开发目标说明: 开发背景:为了网络的安全,工作的网络环境的变得比较复杂,很多主机的端口一定程度上的受到了保护,需要从能访问到的中间机器做跳转. 开发环境:JDK1.6 + Eclipse4. ...

最新文章

  1. C4D运动图形基本训练学习教程
  2. zookeeper原理与使用
  3. 5分钟图解Hbase列式存储
  4. 【PAT乙级】1007 素数对猜想 (20 分)
  5. 编写彩色空间转换程序:YUVtoRGB
  6. 水晶报表自定义函数(替换并截取特殊字符后内容)
  7. 编写高质量代码改善C#程序的157个建议——建议157:从写第一个界面开始,就进行自动化测试...
  8. TextTree - 文本资料收集轻量级工具
  9. MathType 换行后无法对齐,怎么都对不齐!!!强迫症晚期(见图)
  10. __name__ == '__main__'
  11. perl下载与环境安装
  12. C++中string类下的begin,end,rbegin,rend的用法
  13. PS轻松打造低多边形风格图像
  14. Word技巧:如何使用正则表达式高效替换
  15. 获取iPhone相册的照片
  16. lisp画弯箭头_在CAD中直接用命令画箭头
  17. 工业基础类IFC—IFC Viewers
  18. ISO 18184纺织品抗病毒活性的测定
  19. PySerial学习系列1--serial.tools
  20. GD32F450芯片管脚排列图

热门文章

  1. 数据挖掘竞赛,算法刷题网址汇总
  2. NLP文本分类大杀器:PET范式
  3. NLP预训练家族 | 自成一派的GPT!
  4. eclipse 如何关联git_作为一名初学Java者 如何做简单的Java项目
  5. q语言 科学计数_3岁宝宝说话结巴,被诊断语言障碍,我用1招让孩子口齿清晰,打脸众人!...
  6. ajax ashx 请选择文件,ajax+jquery+ashx如何实现上传文件
  7. 吴恩达《机器学习》第十一章:机器学习系统的设计
  8. Stanford CS230深度学习(三)调参、正则化和优化算法
  9. angular使用动态组件后属性值_Angular 2-组件
  10. anaconda pycharm_搭建 Python 高效开发环境: Pycharm + Anaconda