文档章节

基于Socket的网络聊天室编程(第一版)

 木宛城主
发布于 2015/03/02 19:41
字数 1806
阅读 49
收藏 1
点赞 0
评论 0

一:什么是套接字

在网络编程中最常用的方案便是Client/Server (客户机/服务器)模型。在这种方案中客户应用程序向服务器程序请求服务。一个服务程序通常在一个众所周知的地址监听对服务的请求,也就是说,服务进程一直处于休眠状态,直到一个客户向这个服务的地址提出了连接请求。在这个时刻,服务程序被"惊醒"并且为客户提供服务-对客户的请求作出适当的反应。为了方便这种Client/Server模型的网络编程,90年代初,由Microsoft联合了其他几家公司共同制定了一套WINDOWS下的网络编程接口,即Windows Sockets规范,它不是一种网络协议,而是一套开放的、支持多种协议的Windows下的网络编程接口。现在的Winsock已经基本上实现了与协议无关,你可以使用Winsock来调用多种协议的功能,但较常使用的是TCP/IP协议。Socket实际在计算机中提供了一个通信端口,可以通过这个端 口与任何一个具有Socket接口的计算机通信。应用程序在网络上传输,接收的信息都通过这个Socket接口来实现。

简单的来说,socket非常类似于电话插座。以一个电话网为例。电话的通话双方相当于相互通信的2个程序,电话号码就是IP地址。任何用户在通话之前,首先要占有一部电话机,相当于申请一个socket;同时要知道对方的号码,相当于对方有一个固定的socket。然后向对方拨号呼叫,相当于发出连接请求。对方假如在场并空闲,拿起电话话筒,双方就可以正式通话,相当于连接成功。双方通话的过程,是一方向电话机发出信号和对方从电话机接收信号的过程,相当于向socket发送数据和从socket接收数据。通话结束后,一方挂起电话机相当于关闭socket,撤消连接。

二:Socket的一般应用模式(Server-Client)

客户端Client Socket连接服务端指定的端口(负责接收和发送服务端消息)

  • 必须指定要连接的服务端地址和断口。
  • 通过创建一个Socket对象来初始化一个到服务端的连接。

服务端Welcoming Socket监听到客户端连接,创建Connection Socket(负责和客户端通讯)

  • 一个负责接监听客户端连接的套接字
  • 每成功接收到一个客户端连接便在服务端产生一个对应Socket

Socket的通讯过程:

客户端:

  1. 申请一个Socket
  2. 连接到指定服务器(指明了IP地址和端口号)

服务器端:

  1. 申请一个Socket
  2. 绑定到一个IP地址和端口上
  3. 开启侦听,等待接受连接

 

socket通信的基本流程图:

 三:网络聊天室原理与实现-服务端:

  • 开始监听客户连接 -WatchConnection()
Thread threadWatchPort = null;//监听端口线程
        Socket socketWatchPort = null;
        //存储客户端连接的信息
        Dictionary<string, ClientConnection> dictConnections = new Dictionary<string, ClientConnection>();
        //IP地址
        IPAddress address = null;
        //IP节点
        IPEndPoint endpoint = null;
        /// <summary>
        /// 开始监听用户连接
        /// </summary>
        /// 函数原型是int PASCAL listen(SOCKET,int);
        ///其中第二参数的含义楼主理解错误,并非最大可连接数,而是最多可缓存的监听个数。
        ///这里listen()维护一个队列,每一个请求监听,但尚未被accept()的请求都放在队列里,而一旦监听被accept()之后,该监听就从队列移走了。
        private void WatchConnection()
        {
            try
            {
                //创建IP地址
                address = IPAddress.Parse(txtIP.Text);
                //创建IP节点(包含IP和端口)
                endpoint = new IPEndPoint(address, int.Parse(txtPort.Text));
                //创建一个监听套接字(基于TCP的流式套接字)
                socketWatchPort = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //将套接字绑定到主机上某个端口
                socketWatchPort.Bind(endpoint);
                //能同时处理的连接数
                socketWatchPort.Listen(10);
                threadWatchPort = new Thread(WatchPort);
                threadWatchPort.Name = "threadWatchPort";
                //设为后台线程,当所有前台线程停止后会自动关闭
                threadWatchPort.IsBackground = true;
                threadWatchPort.Start();
                ShowMsg("服务器启动完毕,等待客户端连接");
            }
            catch (Exception ex)
            {
                ShowErrorMsg("",ex);
            }
        }
  • 在另一线程监听指定端口- WatchPort()

 

private void WatchPort()
        {
            while (true)
            {
                try
                {
                    //cSok和客户端通信套接字
                    Socket cSok = socketWatchPort.Accept();
                    ClientConnection conn = new ClientConnection(this, cSok);
                    ShowMsg("客户端" + cSok.RemoteEndPoint.ToString() + "连接成功");
                    dictConnections.Add(cSok.RemoteEndPoint.ToString(), conn);
                    AddClientToList(cSok.RemoteEndPoint.ToString());
                }
                catch (Exception ex)
                {
                    ShowErrorMsg("",ex);
                    //break;
                }
            }
        }

 

  • 和客户端连接的通道类:

 

public class ClientConnection
    {
        Thread threadClient = null;
        Socket socket = null;
        FrmMain frmMain = null;
        bool doesClose = false;
        public ClientConnection(FrmMain frmMain,Socket socket)
        {
            this.frmMain = frmMain;
            this.socket = socket;
            threadClient = new Thread(WatchClientMsg);
            threadClient.IsBackground = true;
            threadClient.Start();   
        }
        #region 监听客户端消息 -WatchClientMsg();
        /// <summary>
        /// 监听客户端消息
        /// </summary>
        private void WatchClientMsg()
        {
            while (!doesClose)
            {
                try
                {
                    byte[] byteMsgRec = new byte[1024 * 1024 * 4];
                    int length = socket.Receive(byteMsgRec, byteMsgRec.Length, SocketFlags.None);
                    if (length > 0)
                    {
                        string strMsgRec = Encoding.UTF8.GetString(byteMsgRec, 1, length - 1);
                        ShowMsg(socket.RemoteEndPoint.ToString() + "说:" + strMsgRec);
                    }
                }
                catch (Exception ex)
                {
                    if (socket!=null)
                    {
                        ShowErr("客户端"+socket.RemoteEndPoint.ToString()+"断开连接:",ex);
                        frmMain.RemoveListItem(socket.RemoteEndPoint.ToString());
                        break;
                    }
                    //
                }
            }
        }
        #endregion

        #region 发送窗口抖动 SendShake()
        public void SendShake()
        {
            try
            {
                byte[] finalByte = new byte[1];
                finalByte[0] = 2;
                socket.Send(finalByte);
            }
            catch (Exception ex)
            {
                ShowErr("SendShake()",ex);
            }
        }
        #endregion

        #region 发送消息 -SendMsg(string msg)
        /// <summary>
        /// 发送消息 标志:第一位是:0
        /// </summary>
        /// <param name="msg"></param>
        public void SendMsg(string msg)
        {
            try
            {
                byte[] msgSendByte = Encoding.UTF8.GetBytes(msg);
                byte[] finalByte = new byte[msgSendByte.Length + 1];
                finalByte[0] = 0;
                Buffer.BlockCopy(msgSendByte, 0, finalByte, 1, msgSendByte.Length);
                socket.Send(finalByte);
            }
            catch (Exception ex)
            {
                ShowErr("SendMsg(string msg)",ex);
                throw;
            }

        }
        #endregion

        #region 发送文件 -SendFile(string fileName)
        /// <summary>
        /// 发送文件 标记:第一位为1
        /// </summary>
        /// <param name="fileName">文件路径</param>
        public void SendFile(string fileName)
        {
            FileStream fs = null;
            try
            {
                fs = new FileStream(fileName, FileMode.Open);
                byte[] byteFile = new byte[1024 * 1024 * 5];
                int length = fs.Read(byteFile, 0, byteFile.Length);
                if (length > 0)
                {
                    byte[] byteFinalFile = new byte[length + 1];
                    byteFinalFile[0] = 1;
                    Buffer.BlockCopy(byteFile, 0, byteFinalFile, 1, length);
                    socket.Send(byteFinalFile);
                }
            }
            catch (Exception ex)
            {
                ShowErr("SendFile(string fileName)", ex);
            }
            finally
            {
                fs.Close();
            }
        }
        #endregion

        #region 关闭与客户端连接-Close()
        public void Close()
        {
            doesClose = true;
            threadClient.Abort();
            socket.Shutdown(SocketShutdown.Both);
            socket.Close();
            socket = null;
        }
        #endregion

        #region 在面板上显示消息 -ShowMsg()
        private void ShowMsg(string msg)
        {
            this.frmMain.ShowMsg(msg);
        }
        private void ShowErr(string errMsg,Exception ex)
        {
            this.frmMain.ShowErrorMsg(errMsg, ex);
        }
        #endregion

    }

 

三:网络聊天室原理与实现-客户端:

  • 初始化客户端
//初始化客户端Socket用于连接服务器端
        private void InitSocketAndConnect()
        {
            try
            {
                dgShowMsg = new DGShowMsg(DoShowMsg);
                //创建一个客户端Socket
                clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                address = IPAddress.Parse(txtIp.Text.Trim());
                endP = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));
                //连接到指定服务器的指定端口
                clientSocket.Connect(endP);
                ShowMsg("连接成功!");
            }
            catch (Exception ex)
            {
                ShowErr("InitSocketAndConnect()",ex);
            }
        }
  • 接收消息
private void WatchMsg()
        {
            while (true)
            {
                byte[] msgByte = new byte[1024 * 1024 * 2];
                int length = 0;
                try
                {
                    length = clientSocket.Receive(msgByte,msgByte.Length,SocketFlags.None);
                    if (length>0)
                    {
                        if (msgByte[0]==0)//接受文字
                        {
                            ShowMsg("对方说:"+Encoding.UTF8.GetString(msgByte,1,length-1));
                        }
                        else if (msgByte[0]==1)//接受文件
                        {
                            SaveFileDialog sfd = new SaveFileDialog();
                            if (sfd.ShowDialog()==DialogResult.OK)
                            {
                                string savePath = sfd.FileName;
                                using (FileStream fs=new FileStream (savePath,FileMode.Create) )
                                {
                                    fs.Write(msgByte,1,length-1);
                                }
                                ShowMsg("文件保存成功:"+savePath);
                            }
                        }
                        else//抖动窗体
                        {
                            ShakeWindow();
                        }
                    }
                }
  • 发送消息到服务端
/// <summary>
       /// 发送消息
       /// </summary>
       /// <param name="sender"></param>
       /// <param name="e"></param>
        private void btnSendMsg_Click(object sender, EventArgs e)
        {
            if (clientSocket!=null)
            {
                try
                {
                    string msgSend = txtInput.Text.Trim();
                    byte[] orgByte = Encoding.UTF8.GetBytes(msgSend);
                    byte[] finalByte=new byte[orgByte.Length+1];
                    finalByte[0] = 0;
                    Buffer.BlockCopy(orgByte,0,finalByte,1,orgByte.Length);
                    clientSocket.Send(finalByte);
                    ShowMsg("我说:"+msgSend);

                }
                catch (SocketException ex)
                {
                    ShowErr("发送消息时",ex);
                }
            }
        }
  • 抖动窗体:
/// <summary>
  /// 抖动窗体
  /// </summary>
        private void doShakeWin()
        {
             Random ran = new Random();
             System.Drawing.Point point = this.Location;
            for (int i = 0; i < 30; i++)
            {
                this.Location = new System.Drawing.Point(point.X + ran.Next(8), point.Y + ran.Next(8));
                System.Threading.Thread.Sleep(15);
                this.Location = point;
                System.Threading.Thread.Sleep(15);
            }
        }

程序参考出处:

http://jameszou.blog.51cto.com/2173852/641032

 

源代码下载:

http://files.cnblogs.com/OceanEyes/Ocean.Eyes.SocketWork.Solution.rar

 

 

 

 

 

© 著作权归作者所有

共有 人打赏支持
粉丝 2
博文 222
码字总数 199010
作品 0
黄浦
[Qt] 基于Tcp协议的聊天室实现

时间:2017年6月21日 一、写在前面: 平时做图形学的东西多一些,虽然一直对网络编程很感兴趣,但是没有什么机会去尝试一下。最近正好赶上期末的课程实习,然后就参考Qt官方的 Network Prog...

Mahabharata_ ⋅ 2017/06/22 ⋅ 0

网页版即时通讯

web聊天室的实现方法有多种,包括:基于ajax技术的实现,基于Comet(Pushlet)技术的实现,基于XMPP协议的实现,以及基于flash的XmlSocket和远程共享对象的实现。 1)基于ajax技术的实现。 ...

为梦而来 ⋅ 2014/04/25 ⋅ 0

AgileEAS.NET SOA 中间件平台.Net Socket通信框架-完整应用例子-在线聊天室系统-下载配置

一、AgileEAS.NET SOA中间件Socket/Tcp框架介绍 在文章AgileEAS.NET SOA 中间件平台Socket/Tcp通信框架介绍一文之中我们对AgileEAS.NET SOA中间Socket/Tcp框架进行了总体的介绍,我们知道 Ag...

agilelab ⋅ 2013/10/21 ⋅ 0

鱼鱼聊天室--JoyChat

鱼鱼聊天室简介:利用Java语言开发基于TCP/IP协议和Socket编程技术的聊天室软件。软件具有友好的聊天界面,以及流畅的网 络通信效果,目前最新版本已具有一般聊天室的基本功能,包括:群聊,...

匿名 ⋅ 2009/07/06 ⋅ 0

ACE高性能网络编程

一、课程目标 本次课程针对使用ACE工具包进行高性能网络应用开发,通过本次课程的学习,学员将具备以下能力: 二、参训要求 参加本次课程的学员须具备以下能力: 三、课程实践环境 四、课程大...

kwarph ⋅ 2013/06/14 ⋅ 0

PHP游戏开发之大家一起来玩泡泡堂

在fooking完成的第一版里面附带了一个简单的聊天室来说明了fooking的功能,这次利用周末两天写了一个稍微比聊天室复杂点的小游戏!能跑动,能放泡,能聊天,能控制死亡。演示地址:http://18...

呵大官人 ⋅ 2014/12/15 ⋅ 5

TCP、HTTP协议以及SOCKET编程

一、TCP 协议 TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议,由IETF的RFC 793定义。在简化的计算机网络OSI模型中,它完成第四层...

什么都不懂的小石 ⋅ 04/22 ⋅ 0

Websocket和PHP Socket编程

谈谈php中使用websocket-实例 http://blog.csdn.net/xueling022/article/details/52902358 本来是搜一些html5 websocket资料看的,结果被引去看了php的socket编程。下面是一些简单的例子,在...

mickelfeng ⋅ 2014/10/28 ⋅ 2

想做程序员,C语言可以开发哪些项目?

(1)C语言是面向过程的,而C++是面向对象的 (2)C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运...

小辰GG ⋅ 2017/12/21 ⋅ 0

Python实现文字聊天室

你是否想过用所学的Python开发一个图形界面的聊天室程序啊? 像这样的: image 如果你想开发这样一个有点怀旧风格的聊天程序,那么可以接着看; 要开发这个聊天程序,你需要具备以下知识点:...

实验楼 ⋅ 2017/12/01 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

Mahout基于内存的DataMode 推荐引擎Demo2

Mahout基于内存的DataMode 推荐引擎Demo2 //注释的部分是基于文件也可以理解为基于日志文件的, //DataModel 可以有很多种,实现abstractDataMode的子类,原则上都可以作为数据源,个人觉得,...

xiaomin0322 ⋅ 5分钟前 ⋅ 0

Docker部署Tomcat及Web应用

一、在线下载docker yum install -y epel-releaseyum install docker-io # 安装dockerchkconfig docker on # 加入开机启动service docker start # 启动docker服务 1 ...

Jeam_ ⋅ 5分钟前 ⋅ 0

研发运营一体化能力成熟度模型

研发运营一体化是指在 IT 软件及相关服务的研发及交付过程中,将应用的需求、开发、测试、部 署和运营统一起来,基于整个组织的协作和应用架构的优化,实现敏捷开发、持续交付和应用运营的无...

stars永恒 ⋅ 10分钟前 ⋅ 0

jQuery缩小放大触发事件

jquery的resize()方法使用 <html> <head> <script type="text/javascript" src="/jquery/jquery.js"></script> <script type="text/javascript"> var i = 0; $(document).ready(function(){ ......

RobertZou ⋅ 11分钟前 ⋅ 0

eclipse python 搭建

https://jingyan.baidu.com/article/9113f81b68ebce2b3214c7e0.html https://www.cnblogs.com/ZhangRuoXu/p/6397756.html https://blog.csdn.net/zhangphil/article/details/78962159 字符集......

之渊 ⋅ 11分钟前 ⋅ 0

weex,react native,flutter

weex: 一次编写,处处运行 RN: 学一次,到处写(针对安卓,IOS平台特性 各自写,会有很大一部分是一样的代码) 这些方案是否真正的解决了跨平台问题呢?从目前的状况来看,很显然是没有的,因...

东东笔记 ⋅ 17分钟前 ⋅ 0

Spring Cloud微服务分布式云架构-集成项目

Spring Cloud集成项目有很多,下面我们列举一下和Spring Cloud相关的优秀项目,我们的企业架构中用到了很多的优秀项目,说白了,也是站在巨人的肩膀上去整合的。在学习Spring Cloud之前大家必...

明理萝 ⋅ 22分钟前 ⋅ 1

SpringMVC图片上传问题解决

当我们上传图片时一直发现: MultipartFile file = null; if (request instanceof MultipartHttpServletRequest) 匹配不上, 解决方案: 在前端xml加入如下配置代码即可 <!-- 图片上传bean --...

泉天下 ⋅ 24分钟前 ⋅ 0

Spring表达式语言(SpEL)

1、SpEL引用 Spring EL在bean创建时执行其中的表达式。此外,所有的Spring表达式都可以通过XML或注解的方式实现。下面将使用Spring表达式语言(SpEL),注入字符串,整数,Bean到属性。 SpEL的...

霍淇滨 ⋅ 40分钟前 ⋅ 0

Gradle使用阿里云镜像

gradle 生命周期中有一个初始化( Initialization )的过程,这个过程运行在 build script 之前,我们可以在这个地方做一点系统全局的设置,如配置仓库地址。 你可以在以下几个位置实现仓库地址...

明MikeWoo ⋅ 49分钟前 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部