在计算机通信领域,socket 被翻译为“套接字”,它是计算机之间进行通信的一种约定或一种方式。通过 socket 这种约定,一台计算机可以接收其他计算机的数据,也可以向其他计算机发送数据
套接字之间的连接过程可以分为三个步骤 :
(1)服务器监听。
(2)客户端请求。
(3)连接确认 。
c#的Socket通讯实现聊天室,支持消息与图片发送
c#实现的主要步骤:
1:第一步创建一个开始监听的Socket
2:第二步创建Ip地址和端口号对象
3:第三步让监听的Socket绑定Ip地址跟端口号
4:第四步设置监听队列
5:最后开启一个线程循环监听连接的客户端
聊天室服务器主要起到中转的作用:
当有一个 客服端发送信息或者图片过来的时候,将其发送给所有连接上的客服端
客户端将消息解析成byte数组,在byte数组第一位加上一个数用来表示发送的消息是什么类型的该怎么去解析它(目前支持:文字0,图片1),可以理解成一个简单的协议,双方之间约定好怎么封装,怎么解析内容
界面展示
代码部分展示
服务端:
public partial class ServerSocket : Form
{
public ServerSocket()
{
InitializeComponent();
}
//启动服务器
private void bt_start_Click(object sender, EventArgs e)
{
try
{
//第一步创建一个开始监听的Socket
Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//第二步创建Ip地址和端口号对象
IPAddress ip = IPAddress.Any;
IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(this.txt_Point.Text));
//第三步让监听的Socket绑定Ip地址跟端口号
socketWatch.Bind(point);
//设置监听队列
socketWatch.Listen(int.Parse(this.tb_num.Text));
Thread t = new Thread(Listen);
t.IsBackground = true;
t.Start(socketWatch);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
//循环监听连接的客户端
Socket socketSend;
void Listen(object o)
{
Socket socketWatch = o as Socket;
while (true)
{
//负责监听的Socket来接收客户端的连接,创建跟客户通讯的Socket
socketSend = socketWatch.Accept();
//将远程的连接客服端存储字典集合
SocketHelper.Intanter.dicScoket.Add(socketSend.RemoteEndPoint.ToString(), socketSend);
//添加地址到下拉框与list集合中去
AddCbItem(socketSend.RemoteEndPoint.ToString());
AddContent(socketSend.RemoteEndPoint.ToString() + "连接成功");
Thread t = new Thread(Recive);
t.IsBackground = true;
t.Start(socketSend);
}
}
//循环接收发送过来的数据
void Recive(object o)
{
Socket socketSend = o as Socket;
while (true)
{
try
{
string str = "";
//客户端连接成功后,服务器接收客服端发送过来的数据
byte[] buffer = new byte[1024 * 1024 * 2];
//实际接收的有效字节
int r = socketSend.Receive(buffer);
if (r == 0)
{
break;
}
if (buffer[0] == 0)
{
str = socketSend.RemoteEndPoint.ToString() + ":\r\n" + " " + Encoding.UTF8.GetString(SocketHelper.Intanter.RemoveFbyte(buffer), 0, r);
}
else if (buffer[0] == 1)
{
str = socketSend.RemoteEndPoint.ToString() + ":\r\n" + " " + SocketHelper.Intanter.ShowImgByByte(SocketHelper.Intanter.RemoveFbyte(buffer));
SocketHelper.Intanter.SendMessage(buffer);
}
byte[] newbuffer = SocketHelper.Intanter.SendMessageToClient(str);
SocketHelper.Intanter.SendMessage(newbuffer);
AddContent(socketSend.RemoteEndPoint.ToString() + ":" + str);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
//将连接的IP添加到下拉框
public void AddCbItem(string ItemName)
{
this.BeginInvoke(new MethodInvoker(delegate
{
cb_IP.Items.Add(ItemName);
SocketHelper.Intanter.IPItem.Add(ItemName);
}));
}
//将接受到的内容显示出来
private void AddContent(string content)
{
this.BeginInvoke(new MethodInvoker(delegate
{
rtb_log.AppendText(content + " ");
rtb_log.AppendText("\r\n");
rtb_log.Focus();//先获取焦点
rtb_log.Select(rtb_log.TextLength, 0);//选中数据末尾0个字符
rtb_log.ScrollToCaret();//将滚动条移动到当前位置
//记录收到的字符个数
//toolStripStatusLabel1.Text += (int.Parse(toolStripStatusLabel1.Text) + content.Length).ToString();
}));
}
//弹出客户端
private void bt_Open_Click(object sender, EventArgs e)
{
new ClientSocket().Show();
}
//选择连接的客户端发送数据
private void bt_send_Click(object sender, EventArgs e)
{
string msg = this.rtb_sendmsg.Text.Trim();
//byte[] buffer = Encoding.UTF8.GetBytes(msg);
获得用户在下拉框的IP地址
//string ip = this.cb_IP.Text;
//SocketHelper.Intanter.dicScoket[ip].Send(buffer);
//socketSend.Send(buffer);
socketSend.Send(SocketHelper.Intanter.SendMessageToClient(msg, SocketHelper.MessageType.news));
}
}
客户端:
public partial class ClientSocket : Form
{
public ClientSocket()
{
InitializeComponent();
}
Socket socketSend;
string ImageFile;
private void bt_start_Click(object sender, EventArgs e)
{
try
{
//创建负责通讯的Socket
//第一步创建一个开始监听的Socket
socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//第二步创建Ip地址和端口号对象
IPAddress ip = IPAddress.Parse(this.tb_IP.Text);
IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(this.tb_Point.Text));
socketSend.Connect(point);//通过ip 端口号定位一个要连接的服务器端
Thread t = new Thread(Recive);
t.IsBackground = true;
t.Start();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message.ToString());
}
}
//将接受到的内容显示出来
private void AddContent(string content)
{
this.BeginInvoke(new MethodInvoker(delegate
{
rtb_log.AppendText(content + " ");
rtb_log.AppendText("\r\n");
rtb_log.Focus();//先获取焦点
rtb_log.Select(rtb_log.TextLength, 0);//选中数据末尾0个字符
rtb_log.ScrollToCaret();//将滚动条移动到当前位置
//记录收到的字符个数
//toolStripStatusLabel1.Text += (int.Parse(toolStripStatusLabel1.Text) + content.Length).ToString();
}));
}
void Recive()
{
while (true)
{
try
{
//客户端连接成功后,服务器接收客服端发送过来的数据
byte[] buffer = new byte[1024 * 1024 * 2];
//实际接收的有效字节
int r = socketSend.Receive(buffer);
if (r == 0)
{
break;
}
if (buffer[0] == 0)
{
string str = Encoding.UTF8.GetString(SocketHelper.Intanter.RemoveFbyte(buffer), 0, r - 1);
AddContent(str);
}
else if (buffer[0] == 1)
{
string ImageName = SocketHelper.Intanter.ShowImgByByte(SocketHelper.Intanter.RemoveFbyte(buffer));
AddCbItem(ImageName);
}
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
//将接收的图片放到listview里面
public void AddCbItem(string ItemName)
{
this.BeginInvoke(new MethodInvoker(delegate
{
ltb_Image.Items.Add(ItemName);
SocketHelper.Intanter.IPItem.Add(ItemName);
}));
}
private void bt_send_Click(object sender, EventArgs e)
{
try
{
string msg = $" " + this.tb_name.Text + " :" + this.rtb_sendmsg.Text.Trim();
socketSend.Send(SocketHelper.Intanter.SendMessageToClient(msg, SocketHelper.MessageType.news));
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
private void bt_OpeImage_Click(object sender, EventArgs e)
{
OpenFileDialog fileDialog = new OpenFileDialog();
DialogResult result = fileDialog.ShowDialog();
if (result == DialogResult.OK)
{
this.pt_Image.Image = Image.FromFile(fileDialog.FileName);
ImageFile = fileDialog.FileName;
}
}
private void bt_SendImage_Click(object sender, EventArgs e)
{
if (ImageFile != null && ImageFile != "")
{
socketSend.Send(SocketHelper.Intanter.SendMessageToClient(ImageFile, SocketHelper.MessageType.picture));
}
else
{
MessageBox.Show("未选择图片");
}
}
private void ltb_Image_SelectedIndexChanged(object sender, EventArgs e)
{
if (this.ltb_Image.SelectedItem == null)
{
return;
}
string FileName = this.ltb_Image.SelectedItem.ToString();
this.pt_Image.Image = Image.FromFile(Environment.CurrentDirectory + "\\Images\\" + FileName);// Image.FromFile(fileDialog.FileName);
}
}
SocketHelper类
public class SocketHelper
{
//将远程连接过来的客服端的IP地址和Socket存入集合
public static SocketHelper Intanter = new SocketHelper();
public Dictionary<string, Socket> dicScoket = new Dictionary<string, Socket>();
public List<string> IPItem = new List<string>();
//消息类型枚举
public enum MessageType
{
news,
picture
}
/// <summary>
/// 服务端接收数据后将数据发送给所有的客户端
/// </summary>
/// <param name="buffer">发送的消息字节</param>
/// <param name="ms">消息类型</param>
/// <returns></returns>
public void SendMessage(byte[] buffer)
{
//获得用户在下拉框的IP地址
var task1 = new Task(() =>
{
for (int i = 0; i < IPItem.Count; i++)
{
string ip = IPItem[i].ToString();
dicScoket[ip].Send(buffer.ToArray());
}
});
task1.Start();
}
/// <summary>
/// 将消息转换成消息协议格式
/// </summary>
/// <param name="buffer">发送的消息字节</param>
/// <param name="ms">消息类型</param>
/// <returns></returns>
public byte[] SendMessageToClient(string message, MessageType ms = MessageType.news)
{
List<byte> newbuffer = new List<byte>();
byte[] buffer = new byte[0];
switch (ms)
{
case MessageType.news:
newbuffer.Add(0);
buffer = Encoding.UTF8.GetBytes(message);
break;
case MessageType.picture:
newbuffer.Add(1);
buffer = SaveImage(message);
break;
default:
break;
}
newbuffer.AddRange(buffer);
return newbuffer.ToArray();
}
public byte[] RemoveFbyte(byte[] buffer)
{
List<byte> newbuffer = buffer.ToList();
newbuffer.RemoveAt(0);
return newbuffer.ToArray();
}
/// <summary>
/// 将图片以二进制流
/// </summary>
/// <param name="path"></param>
/// <returns></returns>
public byte[] SaveImage(String path)
{
FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read); //将图片以文件流的形式进行保存
BinaryReader br = new BinaryReader(fs);
byte[] imgBytesIn = br.ReadBytes((int)fs.Length); //将流读入到字节数组中
return imgBytesIn;
}
/// <summary>
/// 现实二进制流代表的图片
/// </summary>
/// <param name="imgBytesIn"></param>
public string ShowImgByByte(byte[] imgBytesIn)
{
lock (this)
{
string NewImageName = DateTime.Now.ToString("yyyy-mm-dd hh-mm-ss") + ".jpg";//ImageName(CenterId);//获得图片的名字
string ImagePath = Environment.CurrentDirectory + $"\\Images\\";//@"F:/AQPXImageURL/" + NewImageName.ToString() + ".jpg";
if (!Directory.Exists(ImagePath))
{
DirectoryInfo directoryInfo = new DirectoryInfo(ImagePath);
directoryInfo.Create();
}
ImagePath += NewImageName.ToString();
if (File.Exists(ImagePath))
{
return NewImageName;
}
MemoryStream ms = new MemoryStream(imgBytesIn);
Bitmap bmp = new Bitmap(ms);
bmp.Save(ImagePath, ImageFormat.Bmp);
ms.Close();
return NewImageName;
}
//return NewImageName;
//pictureBox1.Image = Image.FromStream(ms);
}
}
最后如果感兴趣的可以试着将代码进行优化升级,如:实现各种类型的文件发送(将消息头经过你自己个人的封装解析),希望能帮助到大家对socket的学习
附源码:c#socket通讯源码