基于socket、多线程的客户端服务器端聊天程序
服务器端:
- using System;
- using System.Windows.Forms;
- using System.Net.Sockets;
- using System.Net;//IPAddress,IPEndPoint(ip和端口)类
- using System.Threading;
- using System.Collections.Generic;
- using System.IO;
- namespace MyChatRoomServer
- {
- public partial class Server : Form
- {
- public Server()
- {
- InitializeComponent();
- //一个线程正在访问当前UI线程,要关闭微软设置的对文本框操作的检查
- TextBox.CheckForIllegalCrossThreadCalls = false;
- }
- Thread threadWatch = null;//负责监听客户端连接请求的线程
- Socket socketWatch = null;//负责监听的套接字
- private void btnBeginListen_Click(object sender, EventArgs e)
- {
- //创建服务端负责监听的套接字,参数(IPV4寻址协议,使用流格式,使用Tcp传输协议)
- socketWatch = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
- //获得文本框中IP地址对象
- IPAddress address = IPAddress.Parse(txtIP.Text.Trim());
- //获取端口号
- IPEndPoint endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));
- //将负责监听的套接字绑定到唯一的IP和端口上
- socketWatch.Bind(endpoint);
- //设置监听队列的长度,意思就是同时发送请求只能10个
- socketWatch.Listen(10);
- //创建一个新的套接字专门负责跟客户端通信,注意:Accept方法会阻塞当前程序
- //Socket sockConnection = socketWatch.Accept();
- //创建负责监听的线程,并传入监听的方法
- threadWatch = new Thread(WatchConnecting);
- threadWatch.IsBackground = true;//设置为后台线程,只要前台的一结束,那么程序就结束
- threadWatch.Start();//启动线程
- ShowMsg("服务器启动监听成功!");
- }
- //保存了服务器端所有负责和客户端通信的套接字
- Dictionary<string, Socket> dict = new Dictionary<string, Socket>();
- //保存了服务器端所有负责调用通信套接字Recive方法的线程
- Dictionary<string, Thread> dictThread = new Dictionary<string, Thread>();
- //Socket sokConnection = null;//负责通信的套接字
- /// <summary>
- /// 监听客户端请求的方法
- /// </summary>
- void WatchConnecting()
- {
- while (true) //加上循环持续不断的监听新的客户端的连接
- {
- //开始监听客户端连接请求,注意:Accept方法会阻断当时线程
- Socket sokConnection = socketWatch.Accept();
- //将列表控件中追加一个客户端的ip端口字符串,作为客户端的唯一标识
- lblOnline.Items.Add(sokConnection.RemoteEndPoint.ToString());
- //将与客户端通信的套接字对象sokConnection添加到键值对集合中,并以客户端IP端口作为键
- dict.Add(sokConnection.RemoteEndPoint.ToString(), sokConnection);
- //创建一个委托
- ParameterizedThreadStart pts = new ParameterizedThreadStart(RecMsg);
- //创建通信线程
- Thread thr = new Thread(pts);
- thr.IsBackground = true; //设置为后台
- thr.Start(sokConnection); //将线程的参数传入
- dictThread.Add(sokConnection.RemoteEndPoint.ToString(),thr);
- ShowMsg("客户端连接成功!"+sokConnection.RemoteEndPoint.ToString());
- }
- }
- /// <summary>
- /// 服务端负责监听客户端发来的数据方法
- /// </summary>
- void RecMsg(object socketClientPara)
- {
- Socket socketClient = socketClientPara as Socket;
- while (true)
- {
- //定义一个2M的接受数据的缓存区
- byte[] arrMsgRec = new byte[1024 * 1024 * 2];//手动准备2M空间
- //将接受到的数据存入arrMsgRec数组,并返回真正接收到的数据长度
- int length = socketClient.Receive(arrMsgRec);
- try
- {
- length = socketClient.Receive(arrMsgRec);
- }
- catch (SocketException ex)
- {
- ShowMsg("异常" + ex.Message+",RemoteEnd=" + socketClient.RemoteEndPoint.ToString());
- //从通信套接字集合中删除被中断连接的通信套接字
- dict.Remove(socketClient.RemoteEndPoint.ToString());//删除异常的端口和IP
- //从通信线程中删除被中断的连接通信线程对象
- dictThread.Remove(socketClient.RemoteEndPoint.ToString());//删除异常的线程
- //有异常则跳出,不执行后面的
- //从列表中删除被中断的连接IP:port
- lblOnline.Items.Remove(socketClient.RemoteEndPoint.ToString());
- break;
- //return;
- }
- catch (Exception ex)
- {
- ShowMsg("异常" + ex.Message);
- //有异常则跳出,不执行后面的
- return;
- }
- if (arrMsgRec[0] == 0)//判断传过来的第一个数据是0,则代表是文本数据
- {
- //将数组转成字符串,此时是将数组所有的元素都转成字符串,而真正接收到的只是服务器端传来的一些字符
- string strMsgRec = System.Text.Encoding.UTF8.GetString(arrMsgRec, 1, length-1);
- ShowMsg(strMsgRec);
- }
- //如果是1,则代表发送过来的是文件数据(文件/图片...)
- else if (arrMsgRec[0] == 1)
- {
- //保存文件选择框对象
- SaveFileDialog sfd = new SaveFileDialog();
- if(sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
- {
- string fileSavePath = sfd.FileName;//获得文件保存路径
- //创建文件流,让文件流来根据路径创建一个文件
- using (FileStream fs = new FileStream(fileSavePath, FileMode.Create))
- {
- fs.Write(arrMsgRec,1,length-1);
- ShowMsg("文件保存成功:"+fileSavePath);
- }
- }
- }
- }
- }
- void ShowMsg(string msg)
- {
- txtMsg.AppendText(msg + "\r\n");
- }
- private void btnCloseServer_Click(object sender, EventArgs e)
- {
- //threadWatch.Abort();//关闭线程
- //ShowMsg("服务器启动监听成功!");
- }
- //发送消息到客户端
- private void btnSend_Click(object sender, EventArgs e)
- {
- if (string.IsNullOrEmpty(lblOnline.Text))
- {
- MessageBox.Show("请在左侧选择要发送的好友");
- }
- else
- {
- string strMsg = txtMsgSend.Text.Trim();
- //将要发送的字符串转成utf8对应的字节数组
- byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
- string strClientKey = lblOnline.Text;
- //通过key找到字典集合中对应的与某个用户客户端通信的套接字的send方法,发数据给对方
- try
- {
- dict[strClientKey].Send(arrMsg);
- //一旦上面的出现异常,下面的就不执行
- ShowMsg("发送了数据出去:" + strMsg);
- }
- //sokConnection.Send(arrMsg);
- catch (SocketException ex)
- {
- ShowMsg("发送时异常:"+ex.Message);
- return;
- }
- catch (Exception ex)
- {
- ShowMsg("发送时异常:" + ex.Message);
- return;
- }
- }
- }
- //服务器群发消息
- private void btnSendToAll_Click(object sender, EventArgs e)
- {
- string strMsg = txtMsgSend.Text.Trim();
- //将要发送的字符串转成utf8对应的字节数组
- byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
- //便利当前字典里面所有的通信套接字
- foreach (Socket s in dict.Values)
- {
- s.Send(arrMsg);
- }
- ShowMsg("群发完毕!:)");
- }
- }
- }
客户端:
- using System;
- using System.Windows.Forms;
- using System.Net;
- using System.Net.Sockets;
- using System.Threading;
- using System.IO;
- namespace MyChatRoomClient
- {
- public partial class FChatClient : Form
- {
- public FChatClient()
- {
- InitializeComponent();
- //一个线程正在访问当前UI线程,要关闭微软设置的对文本框操作的检查
- //关闭跨线程检查
- TextBox.CheckForIllegalCrossThreadCalls = false;
- }
- IPAddress address = null;
- IPEndPoint endpoint = null;
- Socket socketClient = null; //客户端套接字
- Thread threadClient = null;//客户端负责接受服务端发来的消息的线程
- //客户端发送请求到服务器
- private void btnConnect_Click(object sender, EventArgs e)
- {
- address = IPAddress.Parse(txtIP.Text.Trim());
- endpoint = new IPEndPoint(address, int.Parse(txtPort.Text.Trim()));
- socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
- try
- {
- socketClient.Connect(endpoint);
- //创建线程,监听服务器端发来的消息
- threadClient = new Thread(RecMsg);
- threadClient.IsBackground = true;
- threadClient.Start();
- }
- catch (Exception ee)
- {
- MessageBox.Show(ee.ToString());
- }
- }
- /// <summary>
- /// 监听服务器端发来的消息
- /// </summary>
- void RecMsg()
- {
- while (true)
- {
- //定义一个2M的接受数据的缓存区
- byte[] arrMsgRec = new byte[1024 * 1024 * 2];//手动准备2M空间
- //将接受到的数据存入arrMsgRec数组,并返回真正接收到的数据长度
- int length = socketClient.Receive(arrMsgRec);
- //将数组转成字符串,此时是将数组所有的元素都转成字符串,而真正接收到的只是服务器端传来的一些字符
- string strMsgRec = System.Text.Encoding.UTF8.GetString(arrMsgRec,0,length);
- ShowMsg(strMsgRec);
- }
- }
- #region 在窗体文本框中显示消息-void ShowMsg(string msg)
- /// <summary>
- /// 在窗体文本框中显示消息
- /// </summary>
- /// <param name="msg">消息</param>
- void ShowMsg(string msg)
- {
- txtMsg.AppendText(msg + "\r\n");
- }
- #endregion
- #region 选择要发送的文件-btnChooseFile_Click
- //选择要发送的文件
- private void btnChooseFile_Click(object sender, EventArgs e)
- {
- OpenFileDialog ofd = new OpenFileDialog();
- if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
- {
- txtFilePath.Text = ofd.FileName;
- }
- }
- #endregion
- //向服务器发送文本消息
- private void btnSendMsg_Click(object sender, EventArgs e)
- {
- string strMsg = txtMsgSend.Text.Trim();
- byte[] arrMsg = System.Text.Encoding.UTF8.GetBytes(strMsg);
- byte[] arrMsgSend = new byte[arrMsg.Length + 1];
- arrMsgSend[0] = 0;//设置标识位,0代表是文字
- Buffer.BlockCopy(arrMsg, 0, arrMsgSend, 1, arrMsg.Length);
- socketClient.Send(arrMsgSend);
- ShowMsg("我发送了:" + strMsg);
- }
- //向服务端发送文件
- private void btnSendFile_Click(object sender, EventArgs e)
- {
- //用文件流打开用户选择的文件
- using (FileStream fs = new FileStream(txtFilePath.Text, FileMode.Open))
- {
- byte[] arrFile = new byte[1024*1024*2];//定义一个2M缓存区
- //将文件数据读到数组arrFile中,并获得读取的真是数据长度
- int length = fs.Read(arrFile,0,arrFile.Length);
- byte[] arrFileSend = new byte[length + 1];
- arrFileSend[0] = 1;//代表发送的是文件数据
- //for (int i = 0; i < length; i++)
- //{
- // arrFileSend[i + 1] = arrFile[i];
- //}
- //数据块的拷贝,将arrFile从第0个开始拷贝,拷贝到arrFileSend,从第一个开始存放
- Buffer.BlockCopy(arrFile,0,arrFileSend,1,length);
- //arrFile.CopyTo(arrFileSend,length);只能从0开始拷贝
- //发送了包含了标识位的新数据到服务端
- socketClient.Send(arrFileSend);
- }
- }
- }
- }
基于socket、多线程的客户端服务器端聊天程序的更多相关文章
- 基于socket实现的简单的聊天程序
记得八年前第一次使用socket做的一个五子棋程序,需要序列化棋子对象,传递到对方的电脑上. 一个偶然的机会,第二次使用socket做点事情.先看聊天服务器端的实现: 服务器端要实现以下功能: ...
- 网络编程应用:基于TCP协议【实现一个聊天程序】
要求: 基于TCP协议实现一个聊天程序,客户端发送一条数据,服务器端发送一条数据 客户端代码: package Homework1; import java.io.IOException; impor ...
- [JavaWeb基础] 024.Socket编程之简单的聊天程序
1.Socket的简介 1)什么是Socket 网络上的两个程序通过一个双向的通讯连接实现数据的交换,这个双向链路的一端称为一个Socket.Socket通常用来实现客户方和服务方的连接.Socket ...
- python socket编程 实现简单p2p聊天程序
目标是写一个python的p2p聊天的项目,这里先说一下python socket的基础课程 一.Python Socket 基础课程 Socket就是套接字,作为BSD UNIX的进程通信机制,取后 ...
- 一个基于Socket的http请求监听程序实现
首先来看以下我们的需求: 用java编写一个监听程序,监听指定的端口,通过浏览器如http://localhost:7777来访问时,可以把请求到的内容记录下来,记录可以存文件,sqlit,mysql ...
- Python基于Socket实现简易多人聊天室
前言 套接字(Sockets)是双向通信信道的端点. 套接字可以在一个进程内,在同一机器上的进程之间,或者在不同主机的进程之间进行通信,主机可以是任何一台有连接互联网的机器. 套接字可以通过多种不同的 ...
- Socket 入门- 客户端回射程序
结果输出:------------------------------------------------------客户端:xx@xxxxxx:~/Public/C$ ./postBackCli.o ...
- Android 基于TCP多线程通信实现群聊天的功能
1.TCP多线程原理图 2.实现方法 (1)服务器端 (2)客户端 3.java后台代码 主界面 package com.lucky.test50socket2; import android.ann ...
- socket学习笔记——线程(聊天程序)
server.c #include <stdio.h> #include <pthread.h> #include <semaphore.h> #include & ...
随机推荐
- ios 中NSDateFormater中的特殊字符
今天要把一个字符串转化为日期格式,这个字符串是服务器传过来的,如下: 2015-02-28T14:40:15 我开始使用这个格式来转化 yyyy-MM-ddThh:mm:ss ,一直返回nil,原来 ...
- Spring MVC配置静态资源的正常访问
SpringMVC如果过滤器过滤范围配置了/或者/*,那么框架会过滤所有请求,包括自己写的请求和静态资源请求,这样静态资源就不能正常加载,包括js文件.css文件.图片资源访问的时候都会出现404页面 ...
- perl 引用(一)
1. 普通变量引用 variable reference 引用就好比C语言的指针,引用变量存储被引用变量的地址.赋值时注意要在变量前加上 \;使用时要多加一个 $ . 当然,引用也可以成为简单变量,可 ...
- ACM/ICPC 之 四道MST-Kruskal解法-练习题(POJ1251-POJ1287-POJ2031-POJ2421)
由于题目简单,部分题意写在代码中(简单题就应该多练英文...),且较少给注释,需要注意的地方会写在代码中,虽然四个题意各有千秋,但万变不离其宗,细细思考一番会发现四道题都属于很直接的最小生成树问题,由 ...
- ACM/ICPC 之 递归(POJ2663-完全覆盖+POJ1057(百练2775)-旧式文件结构图)
POJ2663-完全覆盖 题解见首注释 //简单递推-三个米诺牌(3*2)为一个单位打草稿得出规律 //题意-3*n块方格能被1*2的米诺牌以多少种情况完全覆盖 //Memory 132K Time: ...
- MySQL 5.6 my.cnf 模版
[client] port = socket = /var/run/mysqld/mysqld.sock [mysqld_safe] thp-setting=never socket = /var/r ...
- 两种js数组去重的方法
方法一: 新建一个数组,遍历原数组,在新数组内用IndexOf查找原数组内的每一项,如果没有找到,则添加到其中 代码如下: function arrayNew(arrs ){ var newArray ...
- IOS-在ARC项目中使用非ARC框架或者类库
1.在ARC项目中使用非ARC框架或者类库 IOS 4引入了Automatic Reference Count(ARC),编译器可以在编译时对obj-c对象进行内存管理. 之前,obj-c的内存管理方 ...
- HTML/CSS题库
一. 填空题 使用文本编辑器编辑完HTML后,扩展名可以是__html___或___htm__. 表格的标签是____table______,单元格的标签是____td______. 在编辑ta ...
- The Triangle
针对如下形式的ACM试题,大多出自南阳理工学院的在线ACM试题(网址: 南阳理工在线评测系统),在此非常感谢,同时也非常感谢作者的分享! 时间限制:1000 ms | 内存限制:65535 KB ...