前面有一篇文章写了关于Socket的一些理解,今天小编就用C#来实现以下某聊天软件的多人聊天系统(别问我为啥不用C++,只是因为C#效率高,一个字爽啊)。
我们先来看一下整体的思路:
整体思路是利用线程池达到接收多个聊天请求。先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时请求端与接收端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据
前端页面很简单,一个显示框一个输入框:
具体代码实现:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace WindowsFormsApplication2
{
public partial class Form1 : Form
{
public delegate void CallBack(string str);
public Socket socListen = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
public Form1()
{
InitializeComponent();
CheckForIllegalCrossThreadCalls = false;
}
private void Form1_Load(object sender, EventArgs e)
{
string ip_server = string.Empty;
//获取主机名
string name = Dns.GetHostName();
//通过主机名获取本地IP(包含所有IP,有几个网卡就有几个IP,IP分IPV4和IPV6,这里要注意我们用IPV4)
IPAddress[] ipadrlist = Dns.GetHostAddresses(name);
foreach (IPAddress ipa in ipadrlist)
{
//筛选出我们要的IP,这里因为我是多个网卡,所以我需要这样筛选,大家可以用自己的特征去筛选 IP我打码了
if (ipa.ToString().Contains("!!!!!!")) ip_server = ipa.ToString();
}
//小编自己用,大家可以不理会
ip_server = ipadrlist[3].ToString();
//注意,你要用的端口,这个端口要确保没有被占用
int port = 6000;
IPAddress address = IPAddress.Parse(ip_server);
//创建网络端口,包括ip和端口
IPEndPoint endPoint = new IPEndPoint(address, port);
// 绑定套接字
socListen.Bind(endPoint);
socListen.Listen(5);
//创建线程,开始接受请求,因为Accept方法会阻塞,所以要用多线程
Thread thread = new Thread(acceptConnect);
thread.Start();
}
private void button1_Click(object sender, EventArgs e)
{
//为简单起见,限制聊天消息在1024个字节
string msg = richTextBox2.Text.Trim();
if (msg.Length > 1024)
{
MessageBox.Show("the message is too long");
return;
}
byte[] btMsg = Encoding.UTF8.GetBytes(msg);
byte[] buffer = new byte[1024];
if (!socListen.Connected)
{
try
{
//对方IP
IPAddress address_Server = IPAddress.Parse("!!!!!!");
//除了对方IP 你还得知道对方的程序跑在什么端口
IPEndPoint endPoint_Server = new IPEndPoint(address_Server, 6000);
//连接对方
socListen.Connect(endPoint_Server);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
//发送消息
int len = socListen.Send(btMsg, btMsg.Length, SocketFlags.None);
richTextBox2.Clear();
}
public void acceptConnect()
{
while (true)
{
Socket client = null;
client = socListen.Accept();
//利用线程池循环接收多个请求
ThreadPool.QueueUserWorkItem(new WaitCallback(receiveMsg),client);
}
}
//接收聊天消息
public void receiveMsg(object clientObj)
{
Socket client = clientObj as Socket;
while (true)
{
byte[] bt = new byte[1024];
try
{
client.Receive(bt, bt.Length, SocketFlags.None);
string msg = Encoding.UTF8.GetString(bt);
string clientEndPoint = client.RemoteEndPoint.ToString();
listBox1.Items.Add("from Clinet point:"+ clientEndPoint.Substring(clientEndPoint.IndexOf(":")+1,clientEndPoint.Length-clientEndPoint.IndexOf(":")-1));
listBox1.Items.Add("\t" + msg);
}
catch (Exception ex)
{
client.Close();
}
}
}
}
}
地址运行效果如下:
注:
1、这里面还有很多细节小编没有去处理,核心功能是实现了的。但这并不是一个OK的工程,如果有这方面的需要的朋友,需要自己去处理细节问题。
2、 需要在Form1的构造函数内加上CheckForIllegalCrossThreadCalls = false;这行代码,不然子线程无法操作主界面的控件。
