zoukankan      html  css  js  c++  java
  • C#下实现服务器/客户端的聊天程序

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

    首先,本文主要是针对那些刚接触SOCKET编程的朋友,如果是高手,就可以不看此文啦,可以去陪陪老婆,比如逛街或看电视...

    在开始之前,我们需要预习一些基础知识:
    什么是SOCKET套接字?
    SOCKET通常有那几种数据格式?
    线程的概念?
    (以上基本知识我就不讲了,网上这方面资料很多的,大家找资料看下吧)

    我要介绍的是一个服务器端+客户端的聊天系统,程序比较简单,我先把程序运行的界面给大家看下:


    上面是服务器端运行界面;下面把客户端界面贴给大家看下:



    功能比较简单,服务器的端口号可以在“系统菜单”里面的参数配置进行修改的。

    看了上面的图,下面我们就给大家把代码贴出来:(因为程序比较简单,所以本人就没有去分层啦)

    服务器端代码:
    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 = "服务正在运行"+" 运行端口:"+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 = "等待运行";
    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://download.csdn.net/user/lixyvip
  • 相关阅读:
    Notes on <High Performance MySQL> Ch5: Advanced MySQL Feature
    Differences between DATA_OBJECT_ID and OBJECT_ID
    [转载]Oracle Character set – Everything a New oracle DBA needs to know
    Write CLOB into HTML file
    Windows bat RMDIR command
    ORA01795: maximum number of expressions in a list is 1000
    Preliminary MySQL Study Notes
    Notes on <High Performance MySQL> Ch13: MySQL Server Status
    How To Convert Number into Words using Oracle SQL Query
    "Execute Immediate" in Java
  • 原文地址:https://www.cnblogs.com/leeolevis/p/1383106.html
Copyright © 2011-2022 走看看