接上一片博客,接着socket的学习。此次写的是服务器端被多个客户端连接,并且一个客户端发消息,其他连接的客户端都可接收到。
服务器端设计思路:
1.要有一个线程监听端口,当有客户端连接上时,就要产生一个socket对象负责和这个客户端通信,此时需要开启一个线程处理与这个客户端的通信。
2.转发给其他客户端时,需要知道所有连接客户端的信息。所以建立一个集合,保存连接客户端信息
3.创建一个客户端集合类,能够添加客户端信息,同时当一个客户端离开后能够从集合中移除信息
4.创建一个接受客户端消息的类,能够接受产生的socket的实例对象
ClientGeneric类:
class ClientGeneric
{
private Dictionary<string,Socket> clientDic=new Dictionary<string,Socket>();
private static ClientGeneric cg=new ClientGeneric();//单例模式,保证各个线程访问同一个对象
public static ClientGeneric Instance()
{
return cg;
}
//将连接的客户端信息和产生的socket对象加入集合中
public void AddClient(string key,Socket socket)
{
clientDic.Add(key,socket);
}
//当一个客户端离开后移除集合中客户端信息与产生的socket对象
public void RemoveClient(string key)
{
clientDic.Remove(key);
}
//群聊是发送消息,遍历集合中客户端信息,为每一个客户端发送消息
public void SendMsg(string msg)
{
byte[] msgBytes=Encoding.ASCII.GetBytes(msg.ToCharArray());
foreach(var kvp in clientDic)
{
kvp.Value.Send(msgBytes);
}
}
}
服务器端接受客户端会话处理类ServerSession:
class ServerSession
{
private Socket socket;
public ServerSession(Socket socket)
{
this.socket=socket;
}
public void GetMsg()
{
string remoteEndPoint=socket.RemoteEndPiont.ToString();//获取连接客户端的信息
ClientGeneric.Instance().AddClient(remoteEndPiont,socket);
string msg=string.Empty;
byte[] bytes=new byte[1024*1024];
int len=0;
while(true)//循环获取客户端发来的消息
{
len=socket.Receive(bytes);
msg=Encoding.UTF8.GetString(bytes,0,len);
if(msg.Equals("End"))
{
ClientGeneric.Instance().Remove(remoteEndPoint);//从集合中移除断开的客户端信息
Thread.CurrentThread.Abort();//停止当前监听的线程
socket.Close();
}
//将收到的信息进行转发给其他客户端
ClientGeneric.Instance().SendMsg(msg);
}
}
}
服务器监听类:
public partial class ServerForm
{
Socket socket_Server = null; //定义一个套接字接口对象,并初始化值为空
Thread myThread = null; //定义一个线程对象,并初始化值为空
Socket socket_Connet = null; //用于与客户端连接
string RemoteEndPoint; //客户端的网络结点
private void buttonStartListen_Click(object sender, EventArgs e)
{
socket_Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//new一个Socket对象,注意这里用的是流式Socket(针对于面向连接的TCP服务应用)而不是数据报式Socket(针对于面向无连接的UDP服务应用)。
string IP = textBoxIP.Text.Trim();
IPAddress ServerIP = IPAddress.Parse(IP); //提取IP地址
int port = int.Parse(textBoxPort.Text.Trim()); //port是端口号
IPEndPoint point = new IPEndPoint(ServerIP, port); //point为网络结点对象
socket_Server.Bind(point); //将结点绑定到套接字上
socket_Server.Listen(10); //设置连接队列的最大长度,可根据服务器的性能,可以设置更大程度。
textBoxListenMsg.AppendText("监听成功!\t\n");
myThread = new Thread(Listen_Disp);
myThread.IsBackground = true; //设置是否为后台线程,设置后台线程是为了使当关闭窗口时所有其他线程关闭
myThread.Start();
}
void Listen_Disp()
{
while (true)
{
socket_Connet = socket_Server.Accept();
//当收到一个客户端连接,就产生一个socket负责与其通信,并开启一个线程管理这个客户端
ServerSession ss = new ServerSession(socket_Connet);
Thread t= new Thread(ss.GetMsg);
t.Start();
t.IsBackground = true;
RemoteEndPoint = socket_Connet.RemoteEndPoint.ToString(); //客户端网络结点号
textBoxListenMsg.AppendText("成功与" + RemoteEndPoint + "客户端建立连接!\t\n"); //显示与客户端连接情况
OnlineList_Disp(RemoteEndPoint); //显示在线客户端
}
}
void OnlineList_Disp(string Info)
{
listBoxOnlineList.Items.Add(Info); //在线列表中显示连接的客户端套接字
}
}
客户端接收发送消息:
public partial class Form1 : Form
{
Socket socket = null; //定义一个套接字,初始化为空
Thread thread = null; //定义一个线程
IPEndPoint point = null;
public Form1()
{
InitializeComponent();
RichTextBox.CheckForIllegalCrossThreadCalls = false; //允许跨线程访问
}
private void buttonConnectToServer_Click(object sender, EventArgs e)
{
string IP = textBoxIP.Text.Trim();
int port = int.Parse(textBoxPort.Text.Trim());
IPAddress ip = IPAddress.Parse(IP);
point = new IPEndPoint(ip, port); //网络结点对象
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //初始化套接字
socket.Connect(point);//连接给定的IP地址和端口号
thread = new Thread(ShowMsg); //开启一个监听线程,监听服务器端发来的数据
thread.IsBackground = true; //后台线程
thread.Start();
OnlineList_Disp(textBoxIP.Text.Trim());
}
void OnlineList_Disp(string Info)
{
listBoxOnlineList.Items.Add(Info); //在线列表中显示连接的客户端套接字
}
void ShowMsg()
{
byte[] bytes = new byte[1024 * 1024 * 3]; //设置字节流数组为3M
int length = 0;
string Msg = string.Empty;
while (true)
{
length = socket.Receive(bytes);
Msg = System.Text.Encoding.UTF8.GetString(bytes, 0, length); //注意要把字节转化为字符串
richTextBoxReceiveMsg.AppendText("服务器端:"+Msg+"\t\n"); //显示接收的信息
}
}
void Receive_Show(string Info)
{
richTextBoxReceiveMsg.AppendText(Info + "\t\n");
}
private void button2_Click(object sender, EventArgs e)
{
string sendMsg = richTextBoxMsg.Text.Trim(); //要发送的信息
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(sendMsg); //将要发送的信息转化为字节数组,因为Socket发送数据时是以字节的形式发送的
socket.Send(bytes); //发送数据
richTextBoxReceiveMsg.AppendText("客户端:"+sendMsg+"\t\n");
richTextBoxMsg.Text = "";
}
private void button_Interput_Click(object sender, EventArgs e)
{
byte[] bytes = System.Text.Encoding.UTF8.GetBytes("End"); //将要发送的信息转化为字节数组,因为Socket发送数据时是以字节的形式发送的
socket.Send(bytes); //发送数据
thread.Abort();//将监听线程停止,不在接听服务器端发来的数据
socket.Close();
}
}
服务器端设计思路:
1.要有一个线程监听端口,当有客户端连接上时,就要产生一个socket对象负责和这个客户端通信,此时需要开启一个线程处理与这个客户端的通信。
2.转发给其他客户端时,需要知道所有连接客户端的信息。所以建立一个集合,保存连接客户端信息
3.创建一个客户端集合类,能够添加客户端信息,同时当一个客户端离开后能够从集合中移除信息
4.创建一个接受客户端消息的类,能够接受产生的socket的实例对象
ClientGeneric类:
class ClientGeneric
{
private Dictionary<string,Socket> clientDic=new Dictionary<string,Socket>();
private static ClientGeneric cg=new ClientGeneric();//单例模式,保证各个线程访问同一个对象
public static ClientGeneric Instance()
{
return cg;
}
//将连接的客户端信息和产生的socket对象加入集合中
public void AddClient(string key,Socket socket)
{
clientDic.Add(key,socket);
}
//当一个客户端离开后移除集合中客户端信息与产生的socket对象
public void RemoveClient(string key)
{
clientDic.Remove(key);
}
//群聊是发送消息,遍历集合中客户端信息,为每一个客户端发送消息
public void SendMsg(string msg)
{
byte[] msgBytes=Encoding.ASCII.GetBytes(msg.ToCharArray());
foreach(var kvp in clientDic)
{
kvp.Value.Send(msgBytes);
}
}
}
服务器端接受客户端会话处理类ServerSession:
class ServerSession
{
private Socket socket;
public ServerSession(Socket socket)
{
this.socket=socket;
}
public void GetMsg()
{
string remoteEndPoint=socket.RemoteEndPiont.ToString();//获取连接客户端的信息
ClientGeneric.Instance().AddClient(remoteEndPiont,socket);
string msg=string.Empty;
byte[] bytes=new byte[1024*1024];
int len=0;
while(true)//循环获取客户端发来的消息
{
len=socket.Receive(bytes);
msg=Encoding.UTF8.GetString(bytes,0,len);
if(msg.Equals("End"))
{
ClientGeneric.Instance().Remove(remoteEndPoint);//从集合中移除断开的客户端信息
Thread.CurrentThread.Abort();//停止当前监听的线程
socket.Close();
}
//将收到的信息进行转发给其他客户端
ClientGeneric.Instance().SendMsg(msg);
}
}
}
服务器监听类:
public partial class ServerForm
{
Socket socket_Server = null; //定义一个套接字接口对象,并初始化值为空
Thread myThread = null; //定义一个线程对象,并初始化值为空
Socket socket_Connet = null; //用于与客户端连接
string RemoteEndPoint; //客户端的网络结点
private void buttonStartListen_Click(object sender, EventArgs e)
{
socket_Server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//new一个Socket对象,注意这里用的是流式Socket(针对于面向连接的TCP服务应用)而不是数据报式Socket(针对于面向无连接的UDP服务应用)。
string IP = textBoxIP.Text.Trim();
IPAddress ServerIP = IPAddress.Parse(IP); //提取IP地址
int port = int.Parse(textBoxPort.Text.Trim()); //port是端口号
IPEndPoint point = new IPEndPoint(ServerIP, port); //point为网络结点对象
socket_Server.Bind(point); //将结点绑定到套接字上
socket_Server.Listen(10); //设置连接队列的最大长度,可根据服务器的性能,可以设置更大程度。
textBoxListenMsg.AppendText("监听成功!\t\n");
myThread = new Thread(Listen_Disp);
myThread.IsBackground = true; //设置是否为后台线程,设置后台线程是为了使当关闭窗口时所有其他线程关闭
myThread.Start();
}
void Listen_Disp()
{
while (true)
{
socket_Connet = socket_Server.Accept();
//当收到一个客户端连接,就产生一个socket负责与其通信,并开启一个线程管理这个客户端
ServerSession ss = new ServerSession(socket_Connet);
Thread t= new Thread(ss.GetMsg);
t.Start();
t.IsBackground = true;
RemoteEndPoint = socket_Connet.RemoteEndPoint.ToString(); //客户端网络结点号
textBoxListenMsg.AppendText("成功与" + RemoteEndPoint + "客户端建立连接!\t\n"); //显示与客户端连接情况
OnlineList_Disp(RemoteEndPoint); //显示在线客户端
}
}
void OnlineList_Disp(string Info)
{
listBoxOnlineList.Items.Add(Info); //在线列表中显示连接的客户端套接字
}
}
客户端接收发送消息:
public partial class Form1 : Form
{
Socket socket = null; //定义一个套接字,初始化为空
Thread thread = null; //定义一个线程
IPEndPoint point = null;
public Form1()
{
InitializeComponent();
RichTextBox.CheckForIllegalCrossThreadCalls = false; //允许跨线程访问
}
private void buttonConnectToServer_Click(object sender, EventArgs e)
{
string IP = textBoxIP.Text.Trim();
int port = int.Parse(textBoxPort.Text.Trim());
IPAddress ip = IPAddress.Parse(IP);
point = new IPEndPoint(ip, port); //网络结点对象
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //初始化套接字
socket.Connect(point);//连接给定的IP地址和端口号
thread = new Thread(ShowMsg); //开启一个监听线程,监听服务器端发来的数据
thread.IsBackground = true; //后台线程
thread.Start();
OnlineList_Disp(textBoxIP.Text.Trim());
}
void OnlineList_Disp(string Info)
{
listBoxOnlineList.Items.Add(Info); //在线列表中显示连接的客户端套接字
}
void ShowMsg()
{
byte[] bytes = new byte[1024 * 1024 * 3]; //设置字节流数组为3M
int length = 0;
string Msg = string.Empty;
while (true)
{
length = socket.Receive(bytes);
Msg = System.Text.Encoding.UTF8.GetString(bytes, 0, length); //注意要把字节转化为字符串
richTextBoxReceiveMsg.AppendText("服务器端:"+Msg+"\t\n"); //显示接收的信息
}
}
void Receive_Show(string Info)
{
richTextBoxReceiveMsg.AppendText(Info + "\t\n");
}
private void button2_Click(object sender, EventArgs e)
{
string sendMsg = richTextBoxMsg.Text.Trim(); //要发送的信息
byte[] bytes = System.Text.Encoding.UTF8.GetBytes(sendMsg); //将要发送的信息转化为字节数组,因为Socket发送数据时是以字节的形式发送的
socket.Send(bytes); //发送数据
richTextBoxReceiveMsg.AppendText("客户端:"+sendMsg+"\t\n");
richTextBoxMsg.Text = "";
}
private void button_Interput_Click(object sender, EventArgs e)
{
byte[] bytes = System.Text.Encoding.UTF8.GetBytes("End"); //将要发送的信息转化为字节数组,因为Socket发送数据时是以字节的形式发送的
socket.Send(bytes); //发送数据
thread.Abort();//将监听线程停止,不在接听服务器端发来的数据
socket.Close();
}
}