引子:

当前,互联网 体系结构的参考模型主要有两种,一种是OSI参考模型,另一种是TCP/IP参考模型。

一、OSI参考模型,即开放式通信系统互联参考模型(OSI/RM,Open Systems Interconnection Reference Model),是国际标准化组织(ISO)提出的一个试图使各种计算机在世界范围内互连为网络的标准框架,简称OSI。

OSI参考模型将实现网络互连的通信协议分为7层,自上而下分别是:

第7层应用层:OSI中的最高层,为用户提供各项互联网应用,如公司老板通过浏览器上网、发送电子邮件等。 常见的协议有:HTTP,HTTPS,FTP,TELNET,SSH,SMTP,POP3等。

第6层表示层:相当公司中替老板写信的助理。

第5层会话层:相当于公司中收寄信、写信封与拆信封的秘书。

第4层传输层:提供终端到终端的可靠连接,相当于公司中跑邮局的送信职员。

第3层网络层: 确保信件通过一系列路由到达目的地。

第2层数据链路层: 决定访问网络介质的方式,并处理流控制。

第1层物理层:处于OSI参考模型的最底层,物理层的主要功能是利用物理传输介质为数据链路层提供物理连接,以便透明地传输比特流;该层的常用设备有网卡、集线器、中继器、调制解调器、网线、双绞线、同轴电缆等各种物理设备。

数据发送时,从第七层传到第一层,接收数据则相反。

上三层总称为“应用层”,用来控制软件方面;下四层总称为“数据流层”,用来管理硬件。除了物理层之外,其他层都是用软件实现的。

二、TCP/IP参考模型。

第四层应用层:协议有DNS、FTP、HTTP、HTTPS、Telnet、SMTP等;

第三层传输层:TCP、UDP等;

第二层网际层: IP、ICMP等;

第一层网络接口层:ARP、RARP等。

我们在对上述两种参考模型有些了解后,接下来主要看TCP和UDP。我们先来看二者的区别:

1.TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接;

2.TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付;

3.TCP面向字节流,实际上是TCP把数据看成是一连串无结构的字节流;UDP是面向报文的,它没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(如IP电话,实时视频会议等)

4.每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信;

5.TCP首部开销20字节;UDP的首部开销小,只有8个字节;

6.TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道。

在Java中,对遵守TCP协议的类有ServerSocket和Socket,遵守UDP协议的类有DatagramSocket。我这里提供的聊天室项目,是基于TCP协议的。

该项目分为4个包,分别是utils(提供工具),ui(提供窗体界面),server(服务器子线程),client(客户端子线程)。项目文件包结构如下图所示:

项目运行后的效果如下图所示:

接下来是项目代码:

package 聊天室swing版.utils;
/**
* 工具类,服务器ip,端口等信息
*/
import java.net.InetAddress;
import java.net.UnknownHostException;
public abstract class HostInfo {
public static String IP=getIP();
public static final int PORT=10086;
public static final int NUM=50;
public static final String NEW_LINE="\r\n";
private static String getIP(){
try {
return InetAddress.getLocalHost().getHostAddress();
} catch (UnknownHostException e) {
e.printStackTrace();
return null;
}
}
}
-----------------------------------------------------------
package 聊天室swing版.utils;
/**
* 工具类,释放资源
*/
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
public abstract class Release {
public static void release(Socket socket,BufferedWriter bw){
release(null,socket,null,bw);
}
public static void release(Socket socket,BufferedReader br){
release(null,socket,br,null);
}
public static void release(Socket socket){
release(null,socket,null,null);
}
public static void release(ServerSocket server){
release(server,null,null,null);
}
public static void release(ServerSocket server,Socket socket,BufferedReader br,BufferedWriter bw){
if(server!=null){
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(br!=null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bw!=null){
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
} }
}
-----------------------------------------------------------------
package 聊天室swing版.server;
/**
* 服务器Server类
*/
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List; import 聊天室swing版.ui.UIserver;
import 聊天室swing版.utils.HostInfo;
import 聊天室swing版.utils.Release;
public class Server extends Thread {
private ServerSocket server;
public static List<Transport> clients=new ArrayList<>();
@Override
public void run() {
try {
server=new ServerSocket(Integer.parseInt(UIserver.jtf_port.getText().trim()));
} catch (IOException e) {
Release.release(server);
throw new RuntimeException("服务器端口被占!");
}
UIserver.bt_open.setText("已启动服务器");
UIserver.bt_open.setEnabled(false);
UIserver.jta_log.append("服务器成功启动!"+HostInfo.NEW_LINE);
new Accept().start();
}
class Accept extends Thread{
private Socket socket;
@Override
public void run() {
int num=0;
while(num<HostInfo.NUM){
try {
socket=server.accept();
} catch (IOException e) {
Release.release(socket);
throw new RuntimeException("客户端连接失败!");
}
num++;
String str="第 "+num+" 个客户端连接成功!==>"+socket.getInetAddress().getHostAddress()+" :"+socket.getPort()+HostInfo.NEW_LINE;
UIserver.jta_log.append(str);
clients.add(new Transport(socket));
}
UIserver.jta_log.append("超出服务器负荷!");
Release.release(server);
}
}
}
----------------------------------------------------
package 聊天室swing版.server;
/**
* 处理服务器Server类中接收客户端发来的信息,以及转发客户端发来的信息
*/
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket; import 聊天室swing版.utils.Release;
public class Transport extends Thread {
private Socket socket;
private String ip;
public Transport(Socket socket){
this.socket=socket;
this.ip=socket.getInetAddress().getHostAddress();
this.start();
}
@Override
public void run() {
BufferedReader br=null;
BufferedWriter ownbw=null;
try {
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
ownbw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
} catch (IOException e) {
Server.clients.remove(this);
Release.release(socket);
throw new RuntimeException("获取流失败!");
}
String str=null;
try {
while((str=br.readLine())!=null){
String[] split = str.split(":", 2);
if(split.length<=1){
ownbw.write("数据格式错误!");
ownbw.newLine();
ownbw.flush();
}
String desip=split[0];
String content=split[1];
BufferedWriter desbw=null;
boolean isOnLine=false;
for(Transport des:Server.clients){
if(desip.equals(des.ip)){
isOnLine=true;
desbw=new BufferedWriter(new OutputStreamWriter(des.socket.getOutputStream()));
desbw.write(str);
desbw.newLine();
desbw.flush();
}
}
if(!isOnLine){
ownbw.write("对方不在线!");
ownbw.newLine();
ownbw.flush();
}
}
} catch (IOException e) {
Server.clients.remove(this);
Release.release(socket);
throw new RuntimeException("获取流失败!");
}
}
}
------------------------------------------------------------
package 聊天室swing版.client;
/**
* 客户端类,向服务器发送信息,以及接收服务器发来的信息
*/
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket; import 聊天室swing版.ui.UIclient;
import 聊天室swing版.ui.UIserver;
import 聊天室swing版.utils.HostInfo;
import 聊天室swing版.utils.Release;
public class Client extends Thread {
private Socket socket;
@Override
public void run() {
try {
socket=new Socket(UIserver.jtf_ip.getText().toLowerCase(), Integer.parseInt(UIserver.jtf_port.getText().trim()));
} catch (IOException e) {
Release.release(socket);
throw new RuntimeException("客户端创建失败!");
}
BufferedReader br=null;
try {
br=new BufferedReader(new InputStreamReader(socket.getInputStream()));
} catch (IOException e) {
Release.release(socket);
throw new RuntimeException("获取流失败!");
}
String str=null;
try {
while((str=br.readLine())!=null){
UIclient.jta_chat.append(str+HostInfo.NEW_LINE);
}
} catch (IOException e) {
Release.release(socket);
throw new RuntimeException("获取流失败!");
}
}
public void send(){
new Send().start();
}
class Send extends Thread{
@Override
public void run() {
BufferedWriter bw=null;
try {
bw=new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
} catch (IOException e) {
Release.release(socket,bw);
throw new RuntimeException("获取流失败!");
}
String str=UIclient.jtf_desip.getText().trim()+":"+UIclient.jta_message.getText().trim();
try {
bw.write(str);
bw.newLine();
bw.flush();
} catch (IOException e) {
Release.release(socket,bw);
throw new RuntimeException("获取流失败!");
}
}
}
}
-------------------------------------------------------------
package 聊天室swing版.ui;
/**
* 服务器端UI
* 要先开启服务器,再开启客户端
*/
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField; import 聊天室swing版.server.Server;
import 聊天室swing版.utils.HostInfo;
public class UIserver extends JFrame {
private JPanel jp=new JPanel();
private JLabel jl_ipTips=new JLabel("服务器ip:");
public static JTextField jtf_ip=new JTextField(HostInfo.IP);
private JLabel jl_portTips=new JLabel("服务器端口:");
public static JTextField jtf_port=new JTextField(HostInfo.PORT+"");
public static JButton bt_open=new JButton("启动服务器");
public static JTextArea jta_log=new JTextArea();
private JScrollPane jsp_log=new JScrollPane(jta_log);
public UIserver(){
jp.setLayout(new FlowLayout());
jp.add(jl_ipTips);
jp.add(jtf_ip);
jp.add(jl_portTips);
jp.add(jtf_port);
jp.add(bt_open);
jp.add(jsp_log);
jl_ipTips.setPreferredSize(new Dimension(100, 50));
jtf_ip.setPreferredSize(new Dimension(150, 50));
jl_portTips.setPreferredSize(new Dimension(100, 50));
jtf_port.setPreferredSize(new Dimension(150, 50));
bt_open.setPreferredSize(new Dimension(260, 50));
jsp_log.setPreferredSize(new Dimension(260, 192));
jta_log.setLineWrap(true);
jta_log.setEditable(false);
bt_open.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent arg0) {
new Server().start();
}
});
add(jp);
setTitle("聊天应用控制服务器");
setBounds(100, 50, 300, 400);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args) {
new UIserver();
}
}
---------------------------------------------------------------
package 聊天室swing版.ui;
/**
* 客户端UI
*/
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField; import 聊天室swing版.client.Client;
public class UIclient extends JFrame {
private JPanel jp_chat=new JPanel();
public static JTextArea jta_chat=new JTextArea();
private JScrollPane jsp_chat=new JScrollPane(jta_chat);
private JPanel jp_send=new JPanel();
public static JTextField jtf_desip=new JTextField("请输入对方Ip");
public static JTextArea jta_message=new JTextArea();
private JScrollPane jsp_message=new JScrollPane(jta_message);
private JButton bt_send=new JButton("发送");
private Client client;
public UIclient(){
jp_chat.add(jsp_chat);
jta_chat.setLineWrap(true);
jta_chat.setEditable(false);
jsp_chat.setPreferredSize(new Dimension(550, 400));
jp_send.add(jtf_desip);
jp_send.add(jsp_message);
jta_message.setLineWrap(true);
jp_send.add(bt_send);
jtf_desip.setPreferredSize(new Dimension(100, 50));
jsp_message.setPreferredSize(new Dimension(250, 50));
bt_send.setPreferredSize(new Dimension(100, 50));
jtf_desip.addFocusListener(new FocusAdapter(){
@Override
public void focusGained(FocusEvent e) {
jtf_desip.setText("");
}
});
jta_message.addFocusListener(new FocusAdapter(){
@Override
public void focusGained(FocusEvent e) {
jta_message.setText("");
}
});
bt_send.addActionListener(new ActionListener(){ @Override
public void actionPerformed(ActionEvent arg0) {
client.send();
}
});
add(jp_chat, BorderLayout.CENTER);
add(jp_send, BorderLayout.SOUTH);
setTitle("群聊窗口");
setBounds(300, 100, 600, 500);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
client=new Client();
client.start();
}
public static void main(String[] args) {
new UIclient();
}
}

  您可以在Eclipse上运行下,如果有问题,欢迎留言。希望对您有所帮助!

JavaSE项目之聊天室的更多相关文章

  1. JavaSE项目之聊天室swing版

    引子: 当前,互联网 体系结构的参考模型主要有两种,一种是OSI参考模型,另一种是TCP/IP参考模型. 一.OSI参考模型,即开放式通信系统互联参考模型(OSI/RM,Open Systems In ...

  2. Django项目--web聊天室

    需求 做一个web聊天室,主要练习前端ajax与后台的交互: 一对一聊天和群组聊天 添加用户为好友 搜索并添加群组 管理员可以审批用户加群请求,群管理员可以有多个,群管理员可以删除,添加禁言群友 与聊 ...

  3. 小项目:聊天室 (jQuery,PHP,MySQL)

    这几天写了一个小项目,初衷是自己写一个有前端,有后端的东西练练手,所以,实际意义并不大,只是拿来试试手而已. 我对这个聊天室的构想是这样的:先建两个数据库,一个保存用户名和密码,另一个保存用户名和发送 ...

  4. 聊天室(下篇)GatewayWorker 与 Laravel 的整合

    思路 上一篇大概梳理了一下 GatewayWorker 的基础知识.这篇就来准备整合 GatewayWorker 到 Laravel. GatewayWorker 是基于 Socket 监听的服务器框 ...

  5. Linux聊天室项目 -- ChatRome(select实现)

    序 项目简介:采用I/O复用技术select实现socket通信,采用多线程负责每个客户操作处理,完成Linux下的多客户聊天室! OS:Ubuntu 15.04 IDE:vim gcc make D ...

  6. 基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍。最后我们将会实现一个基于Server-Sent Event和Flask简单的在线聊天室。

    基于Server-Sent Event的简单聊天室 Web 2.0时代,即时通信已经成为必不可少的网站功能,那实现Web即时通信的机制有哪些呢?在这门项目课中我们将一一介绍.最后我们将会实现一个基于S ...

  7. 《基于Node.js实现简易聊天室系列之项目前期工作》

    前期工作主要包括:项目的创建,web服务器的创建和数据库的连接. 项目创建 网上关于Node.js项目的创建的教程有很多,这里不必赘述.Demo所使用的Node.js的框架是express,版本为4. ...

  8. 学习JavaSE TCP/IP协议与搭建简易聊天室

    一.TCP/IP协议 1.TCP/IP协议包括TCP.IP和UDP等 2.域名通过dns服务器转换为IP地址 3.局域网可以通过IP或者主机地址寻找到相应的主机 4.TCP是可靠的连接,效率低,且连接 ...

  9. Asp.NET MVC 使用 SignalR 实现推送功能二(Hubs 在线聊天室 获取保存用户信息)

    简单介绍 关于SignalR的简单实用 请参考 Asp.NET MVC 使用 SignalR 实现推送功能一(Hubs 在线聊天室) 在上一篇中,我们只是介绍了简单的消息推送,今天我们来修改一下,实现 ...

随机推荐

  1. 《java.util.concurrent 包源码阅读》13 线程池系列之ThreadPoolExecutor 第三部分

    这一部分来说说线程池如何进行状态控制,即线程池的开启和关闭. 先来说说线程池的开启,这部分来看ThreadPoolExecutor构造方法: public ThreadPoolExecutor(int ...

  2. 2017上半年技术文章集合【Android】—184篇文章分类汇总

    地址: http://blog.csdn.net/androidstarjack/article/details/77923753 声明 | 本文是于亚豪 原创 终端研发部 前言: 2017年已经过大 ...

  3. docker-compose v3版本命令详解参考

    参考和指南 这些主题描述了Compose文件格式的第3版.这是最新的版本. Compose and Docker 兼容性矩阵 有几个版本的Compose文件格式 - 1,2,2.x和3.x.下表是快速 ...

  4. [flask实践] 解决mysql数据库不支持中文的问题

    接上一篇文章,在解决了mysql+flask环境配置问题之后,往数据库存中文字符串会报1366错误,提示不正确的字符.继而发现默认的mysql采用了Latin1字符集,这种编码是不支持中文的. 如果想 ...

  5. Linux驱动调试-根据oops的栈信息,确定函数调用过程

    上章链接入口: http://www.cnblogs.com/lifexy/p/8006748.html 在上章里,我们分析了oops的PC值在哪个函数出错的,那如何通过栈信息来查看出错函数的整个调用 ...

  6. sqlserver 查询所有表及记录行数

    --查询所有表名 select name from sysobjects where xtype='u' select * from sys.tables --查询所有表名及对应架构 select t ...

  7. vue2入门之vue-cli

    vue-cli vue在web前端可谓是大放异彩,尤其在国内与angular.react有三足鼎立之势.很多人想入门vue2而又苦于不知从何下手.因为vue2是以组件化开发的,最好要搭配webpack ...

  8. tolua++实现lua层调用c++技术分析

    tolua++技术分析 cocos2dx+lua 前言 一直都使用 cocos2dx + lua 进行游戏开发,用 Lua 开发可以专注于游戏逻辑的实现,另外一方面可以实现热更新:而且 lua 是一个 ...

  9. keepalived实现服务高可用

    第1章 keepalived服务说明 1.1 keepalived是什么? Keepalived软件起初是专为LVS负载均衡软件设计的,用来管理并监控LVS集群系统中各个服务节点的状态,后来又加入了可 ...

  10. netty使用从0到1

    本周强总在组内做了netty分享,内容相当不错,趁着这次分享记录的,以及以前研究,进行一下记录. java io形式存在三种,一种是BIO传统IO是阻塞IO,面向字符.字节服务都属于这一种.NIO官方 ...