博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C#下如何实现服务器+客户端的聊天程序
阅读量:6259 次
发布时间:2019-06-22

本文共 7833 字,大约阅读时间需要 26 分钟。

最近也在接触SOCKET编程,在当今这样一个网络时代,很多技术都以网络为中心在诞生,至少我认为是这样的,而SOCKET套接字接口,在实现网络通讯上处于关键地位,所以不会SOCKET是不行的。

首先,本文主要是针对那些刚接触SOCKET编程的朋友,如果是高手,就可以不看此文啦,可以去陪陪老婆,比如逛街或看电视...
在开始之前,我们需要预习一些基础知识:
什么是SOCKET套接字?
SOCKET通常有那几种数据格式?
线程的概念?
(以上基本知识我就不讲了,网上这方面资料很多的,大家找资料看下吧)
我要介绍的是一个服务器端+客户端的聊天系统,程序比较简单,我先把程序运行的界面给大家看下:
Socket01.JPG
上面是服务器端运行界面;下面把客户端界面贴给大家看下:
Socket02.JPG
功能比较简单,服务器的端口号可以在“系统菜单”里面的参数配置进行修改的。
看了上面的图,下面我们就给大家把代码贴出来:(因为程序比较简单,所以本人就没有去分层啦)
服务器端代码:

  1 
using
 System;
  2 
using
 System.Collections.Generic;
  3 
using
 System.ComponentModel;
  4 
using
 System.Data;
  5 
using
 System.Drawing;
  6 
using
 System.Text;
  7 
using
 System.Windows.Forms;
  8 
  9 
using
 System.Net;
 10 
using
 System.Net.Sockets;
 11 
using
 System.Threading;
 12 
using
 System.Xml;
 13 
 14 
namespace
 Server
 15 
{
 16 
    
public
 partial 
class
 ServerMain : Form
 17 
    {
 18 
        
public
 ServerMain()
 19 
        {
 20 
            InitializeComponent();
 21 
        }
 22 
 23 
        
private
 
void
 ServerMain_Load(
object
 sender, EventArgs e)
 24 
        {
 25 
            
this
.CmdStar.Enabled 
=
 
true
;
 26 
            
this
.CmdStop.Enabled 
=
 
false
;
 27 
        }
 28 
 29 
        
private
 
void
 配置参数ToolStripMenuItem_Click(
object
 sender, EventArgs e)
 30 
        {
 31 
            Set TSet 
=
 
new
 Set();
 32 
            TSet.ShowDialog();
 33 
        }
 34 
 35 
        
private
 
void
 关于ToolStripMenuItem_Click(
object
 sender, EventArgs e)
 36 
        {
 37 
            About TAbout 
=
 
new
 About();
 38 
            TAbout.Show();
 39 
        }
 40 
        
///
 
<summary>
 41 
        
///
 获得XML文件中的端口号
 42 
        
///
 
</summary>
 43 
        
///
 
<returns></returns>
 44 
        
private
 
int
 GetPort()
 45 
        {
 46 
            
try
 47 
            {
 48 
                XmlDocument TDoc 
=
 
new
 XmlDocument();
 49 
                TDoc.Load(
"
Settings.xml
"
);
 50 
                
string
 TPort 
=
 TDoc.GetElementsByTagName(
"
ServerPort
"
)[
0
].InnerXml;
 51 
                
return
 Convert.ToInt32(TPort);
 52 
 53 
            }
 54 
            
catch
 { 
return
 
6600
; }
//
默认是6600
 55 
        }
 56 
 57 
        
//
声明将要用到的类
 58 
        
private
 IPEndPoint ServerInfo;
//
存放服务器的IP和端口信息
 59 
        
private
 Socket ServerSocket;
//
服务端运行的SOCKET
 60 
        
private
 Thread ServerThread;
//
服务端运行的线程
 61 
        
private
 Socket[] ClientSocket;
//
为客户端建立的SOCKET连接
 62 
        
private
 
int
 ClientNumb;
//
存放客户端数量
 63 
        
private
 
byte
[] MsgBuffer;
//
存放消息数据
 64 
 65 
        
private
 
void
 CmdStar_Click(
object
 sender, EventArgs e)
 66 
        {
 67 
            ServerSocket 
=
 
new
 Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 68 
            ServerInfo
=
new
 IPEndPoint(IPAddress.Any,
this
.GetPort());
 69 
            ServerSocket.Bind(ServerInfo);
//
将SOCKET接口和IP端口绑定
 70 
            ServerSocket.Listen(
10
);
//
开始监听,并且挂起数为10
 71 
 72 
            ClientSocket 
=
 
new
 Socket[
65535
];
//
为客户端提供连接个数
 73 
            MsgBuffer 
=
 
new
 
byte
[
65535
];
//
消息数据大小
 74 
            ClientNumb 
=
 
0
;
//
数量从0开始统计
 75 
 76 
            ServerThread 
=
 
new
 Thread(RecieveAccept);
//
将接受客户端连接的方法委托给线程
 77 
            ServerThread.Start();
//
线程开始运行
 78 
 79 
            CheckForIllegalCrossThreadCalls 
=
 
false
;
//
不捕获对错误线程的调用
 80 
 81 
            
this
.CmdStar.Enabled 
=
 
false
;
 82 
            
this
.CmdStop.Enabled 
=
 
true
;
 83 
            
this
.StateMsg.Text 
=
 
"
服务正在运行dot.gif
"
+
"
  运行端口:
"
+
this
.GetPort().ToString();
 84 
            
this
.ClientList.Items.Add(
"
服务于 
"
 
+
 DateTime.Now.ToString() 
+
 
"
 开始运行.
"
);
 85 
        }
 86 
        
 87 
        
//
接受客户端连接的方法
 88 
        
private
 
void
 RecieveAccept()
 89 
        {
 90 
            
while
 (
true
)
 91 
            {
 92 
                ClientSocket[ClientNumb] 
=
 ServerSocket.Accept();
 93 
                ClientSocket[ClientNumb].BeginReceive(MsgBuffer, 
0
, MsgBuffer.Length, 
0
new
 AsyncCallback(RecieveCallBack),ClientSocket[ClientNumb]);
 94 
                
this
.ClientList.Items.Add(ClientSocket[ClientNumb].RemoteEndPoint.ToString()
+
"
 成功连接服务器.
"
);
 95 
                ClientNumb
++
;
 96 
            }
 97 
        }
 98 
 99 
        
//
回发数据给客户端
100 
        
private
 
void
 RecieveCallBack(IAsyncResult AR)
101 
        {
102 
            
try
103 
            {
104 
                Socket RSocket 
=
 (Socket)AR.AsyncState;
105 
                
int
 REnd 
=
 RSocket.EndReceive(AR);
106 
                
for
 (
int
 i 
=
 
0
; i 
<
 ClientNumb; i
++
)
107 
                {
108 
                    
if
 (ClientSocket[i].Connected)
109 
                    {
110 
                        ClientSocket[i].Send(MsgBuffer, 
0
, REnd,
0
);
111 
                    }
112 
                    RSocket.BeginReceive(MsgBuffer, 
0
, MsgBuffer.Length, 
0
new
 AsyncCallback(RecieveCallBack), RSocket);
113 
114 
                }
115 
            }
116 
            
catch
 { }
117 
118 
        }
119 
120 
        
private
 
void
 CmdStop_Click(
object
 sender, EventArgs e)
121 
        {
122 
            ServerThread.Abort();
//
线程终止
123 
            ServerSocket.Close();
//
关闭SOCKET
124 
125 
            
this
.CmdStar.Enabled 
=
 
true
;
126 
            
this
.CmdStop.Enabled 
=
 
false
;
127 
            
this
.StateMsg.Text 
=
 
"
等待运行dot.gif
"
;
128 
            
this
.ClientList.Items.Add(
"
服务于 
"
 
+
 DateTime.Now.ToString() 
+
 
"
 停止运行.
"
);
129 
        }
130 
131 
132 
133 
    }
134 
}

客户端代码:

  1 
using
 System;
  2 
using
 System.Collections.Generic;
  3 
using
 System.ComponentModel;
  4 
using
 System.Data;
  5 
using
 System.Drawing;
  6 
using
 System.Text;
  7 
using
 System.Windows.Forms;
  8 
  9 
using
 System.Net;
 10 
using
 System.Net.Sockets;
 11 
 12 
namespace
 Client
 13 
{
 14 
    
public
 partial 
class
 ClientMain : Form
 15 
    {
 16 
        
public
 ClientMain()
 17 
        {
 18 
            InitializeComponent();
 19 
        }
 20 
 21 
        
private
 IPEndPoint ServerInfo;
 22 
        
private
 Socket ClientSocket;
 23 
        
private
 Byte[] MsgBuffer;
 24 
        
private
 Byte[] MsgSend;
 25 
 26 
        
private
 
void
 ClientMain_Load(
object
 sender, EventArgs e)
 27 
        {
 28 
            
this
.CmdSend.Enabled 
=
 
false
;
 29 
            
this
.CmdExit.Enabled 
=
 
false
;
 30 
 31 
            ClientSocket 
=
 
new
 Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 32 
            MsgBuffer 
=
 
new
 Byte[
65535
];
 33 
            MsgSend 
=
 
new
 Byte[
65535
];
 34 
            CheckForIllegalCrossThreadCalls 
=
 
false
;
 35 
 36 
            Random TRand
=
new
 Random();
 37 
            
this
.UserName.Text 
=
 
"
用户
"
 
+
 TRand.Next(
10000
).ToString();
 38 
        }
 39 
 40 
        
private
 
void
 CmdEnter_Click(
object
 sender, EventArgs e)
 41 
        {
 42 
            ServerInfo 
=
 
new
 IPEndPoint(IPAddress.Parse(
this
.ServerIP.Text), Convert.ToInt32(
this
.ServerPort.Text));
 43 
 44 
            
try
 45 
            {
 46 
                ClientSocket.Connect(ServerInfo);
 47 
 48 
                ClientSocket.Send(Encoding.Unicode.GetBytes(
"
用户: 
"
 
+
 
this
.UserName.Text 
+
 
"
 进入系统!\n
"
));
 49 
 50 
                ClientSocket.BeginReceive(MsgBuffer, 
0
, MsgBuffer.Length, 
0
new
 AsyncCallback(ReceiveCallBack), 
null
);
 51 
 52 
                
this
.SysMsg.Text 
+=
 
"
登录服务器成功!\n
"
;
 53 
                
this
.CmdSend.Enabled 
=
 
true
;
 54 
                
this
.CmdEnter.Enabled 
=
 
false
;
 55 
                
this
.CmdExit.Enabled 
=
 
true
;
 56 
            }
 57 
            
catch
 58 
            {
 59 
                MessageBox.Show(
"
登录服务器失败,请确认服务器是否正常工作!
"
);
 60 
            }
 61 
        }
 62 
 63 
        
private
 
void
 ReceiveCallBack(IAsyncResult AR)
 64 
        {
 65 
            
try
 66 
            {
 67 
                
int
 REnd 
=
 ClientSocket.EndReceive(AR);
 68 
                
this
.RecieveMsg.AppendText(Encoding.Unicode.GetString(MsgBuffer, 
0
, REnd));
 69 
                ClientSocket.BeginReceive(MsgBuffer, 
0
, MsgBuffer.Length, 
0
new
 AsyncCallback(ReceiveCallBack), 
null
);
 70 
 71 
            }
 72 
            
catch
 73 
            {
 74 
                MessageBox.Show(
"
已经与服务器断开连接!
"
);
 75 
                
this
.Close();
 76 
            }
 77 
 78 
        }
 79 
 80 
        
private
 
void
 CmdSend_Click(
object
 sender, EventArgs e)
 81 
        {
 82 
            MsgSend 
=
 Encoding.Unicode.GetBytes(
this
.UserName.Text 
+
 
"
说:\n
"
 
+
 
this
.SendMsg.Text 
+
 
"
\n
"
);
 83 
            
if
 (ClientSocket.Connected)
 84 
            {
 85 
                ClientSocket.Send(MsgSend);
 86 
                
this
.SendMsg.Text 
=
 
""
;
 87 
            }
 88 
            
else
 89 
            {
 90 
                MessageBox.Show(
"
当前与服务器断开连接,无法发送信息!
"
);
 91 
            }
 92 
        }
 93 
 94 
        
private
 
void
 CmdExit_Click(
object
 sender, EventArgs e)
 95 
        {
 96 
            
if
 (ClientSocket.Connected)
 97 
            {
 98 
                ClientSocket.Send(Encoding.Unicode.GetBytes(
this
.UserName.Text 
+
 
"
离开了房间!\n
"
));
 99 
                ClientSocket.Shutdown(SocketShutdown.Both);
100 
                ClientSocket.Disconnect(
false
);
101 
            }
102 
            ClientSocket.Close();
103 
104 
            
this
.CmdSend.Enabled 
=
 
false
;
105 
            
this
.CmdEnter.Enabled 
=
 
true
;
106 
            
this
.CmdExit.Enabled 
=
 
false
;
107 
        }
108 
109 
        
private
 
void
 RecieveMsg_TextChanged(
object
 sender, EventArgs e)
110 
        {
111 
            
this
.RecieveMsg.ScrollToCaret();
112 
        }
113 
114 
        
private
 
void
 SendMsg_KeyDown(
object
 sender, KeyEventArgs e)
115 
        {
116 
            
if
 (e.Control 
&&
 e.KeyValue 
==
 
13
)
117 
            {
118 
                e.Handled 
=
 
true
;
119 
                
this
.CmdSend_Click(
this
null
);
120 
            }
121 
        }
122 
123 
124 
125 
126 
    }
127 
}

我只对服务器端的代码做了注释,客户端就没有写注释了,因为代码是差不多的。区别在于客户端不需要监听,也不需要启用线程进行委托。
关于 ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
这句代码,我想给初学者解释一下,这里“AddressFamily.InterNetwork”表示的是使用IPV4地址,“SocketType.Stream”表示使用的是流格式(另外还有数据包格式和原始套接字格式),“ProtocolType.Tcp”表示使用TCP协议(另外还有很多其它协议,例如大家常看到的UDP协议)。
另外关于SOCKET类中的BeginReceive方法,请大家参考MSDN,里面有详细说明。
希望本人给的这个程序可以起到一个抛砖引玉的作用,不明白之处可以加QQ(17020415)或留言。
备注:

//2007-12-01
//今天有朋友加我QQ问我有关服务端“Settings.xml”文件的内容部分,我现在把内容贴出来,其实很简单,就是方便服务端修改端口的。
<?
xml version="1.0" encoding="utf-8" 
?>
 
<
Server
>
  
<
ServerPort
>
6600
</
ServerPort
>
</
Server
>

完整的源码我已经放在CSDN上面共享了,地址:

本文转自黄聪博客园博客,原文链接:http://www.cnblogs.com/huangcong/archive/2010/03/27/1698434.html,如需转载请自行联系原作者

你可能感兴趣的文章
vue2.0学习笔记(九):vue项目实战--持续更新(1)
查看>>
Vue.js入门教程-过滤器
查看>>
Python之使用Pandas库实现MySQL数据库的读写
查看>>
基于scikit-learn机器学习库的分类预测
查看>>
svg与视频结合的镂空效果实践总结
查看>>
Scikit中的特征选择,XGboost进行回归预测,模型优化的实战
查看>>
Sklearn入门介绍
查看>>
Android广告图片轮播,支持无限循环和设置轮播样式、切换时间等
查看>>
screenX/Y,clientX/Y,offsetX/Y和pageX/Y之间有什么区别?
查看>>
webpack4.0优化那些事儿
查看>>
数据结构与算法(位运算) --javascript语言描述
查看>>
数据结构与算法(回溯法) --javascript语言描述
查看>>
百度地图开发实例番外篇--实用方法(持续更新)
查看>>
“大数据应用场景”之隔壁老王(连载一)
查看>>
k均值聚类算法(k-means)
查看>>
修改springboot的端口来启动项目
查看>>
MaxCompute SQL原理解析及性能调优
查看>>
vue中慎用style的scoped属性
查看>>
深度学习在股票市场的应用
查看>>
redis源码分析之事务Transaction(下)
查看>>