C# socket通信的一般步聚
1、向服务器发出链接请求:
需要条件:a、服务器IP;b、服务器端口
实现:
private IPAddress _ipAddr=IPAddress.Parse("192.168.28.126");
int Port=8888;
//向服务器发出连接请求
TCPConnection conn = new TCPConnection(_ipAddr, port);
TcpClient _tcpc = conn.Connect();
//以下为判断是否链接得上
if (_tcpc == null)
{
MessageBox.Show("无法连接到服务器,请重试!",
"错误",
MessageBoxButtons.OK,
MessageBoxIcon.Exclamation);
}
else
{
NetworkStream netstream = _tcpc.GetStream();//提供用于访问网络的基本数据流
//向服务器发送用户名以确认身份
netstream.Write(Encoding.Unicode.GetBytes(user_tb.Text), 0, Encoding.Unicode.GetBytes(user_tb.Text).Length);
//得到登录结果
byte[] buffer = new byte[50];
netstream.Read(buffer, 0, buffer.Length);//该方法将数据读入 buffer 参数并返回成功读取的字节数。如果没有可以读取的数据,则 Read 方法返回 0。
string connResult = Encoding.Unicode.GetString(buffer).TrimEnd('\0');
if (connResult.Equals("cmd::Failed"))//登录不成功
{
MessageBox.Show("您的用户名已经被使用,请尝试其他用户名!",
"提示",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
return;
}
else //登录成功
{
string svrskt = svrip_tb.Text + ":" + svrport_tb.Text;
chat_frm chatFrm = new chat_frm(user_tb.Text, netstream, svrskt);//一个巧妙的参数传递方法
chatFrm.Owner = this;
this.Hide();
chatFrm.Show();
}
}
2、向服务器发送数据:(逆向推进)
1)、private NetworkStream _nws = _tcpc.GetStream();// 用于接受和发送的网络流
^
||
TcpClient _tcpc = conn.Connect();
^
||
TCPConnection conn = new TCPConnection(_ipAddr, port);//向服务器发出连接请求
^
||
private IPAddress _ipAddr=IPAddress.Parse("192.168.28.126");//服务器IP
int Port=8888;//服务器端口
2)、发送: _nws.Write(new byte[] { 0, 5 }, 0, 2);//发送广播请求 public override void Write(byte[] buffer, int offset, int size);
以下为发送给指定用户:
string _receiver = online_cb.Text;//对方用户
_nws.Write(Encoding.Unicode.GetBytes(_receiver), 0, Encoding.Unicode.GetBytes(_receiver).Length);
//以下为发送信息内容
_nws.Write(Encoding.Unicode.GetBytes(sendTxt), 0, Encoding.Unicode.GetBytes(sendTxt).Length);
3、启动接收信息线程(在page_load里直接启动)
/// <summary>
/// 用于接受消息的线程
/// </summary>
private Thread _receiveThread = null;
/// <summary>
/// 启动接受消息线程并显示相关信息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void chat_frm_Load(object sender, EventArgs e)
{
_receiveThread = new Thread(new ThreadStart(ReceiveMsg));//接下来会执行ReceiveMsg函数
_receiveThread.Start();
//以下这些都是相关控件的变化:
online_cb.Enabled = false;
user_lb.Text = "当前用户:" + _username;
svrskt_lb.Text = "服务器:" + _svrskt;
}
/// <summary>
/// 接受消息的线程执行体
/// </summary>
private void ReceiveMsg()
{
while (true)
{
byte[] packet = new byte[_maxPacket];//_maxPacket:缓冲区的大小:private int _maxPacket = 2048;//2K的缓冲区
//从 System.Net.Sockets.NetworkStream 读取数据。public override int Read(byte[] buffer, int offset, int size);
_nws.Read(packet, 0, packet.Length);
string _cmd = DecodingBytes(packet);
switch (_cmd)
{
/// "11"-服务器要求客户机更新在线列表
/// "12"-服务器要求客户机做闪屏振动
/// default-接受用户消息或者系统消息的正文
case "11":
{
byte[] onlineBuff = new byte[_maxPacket];
int byteCnt = _nws.Read(onlineBuff, 0, onlineBuff.Length);
IFormatter format = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
stream.Write(onlineBuff, 0, byteCnt);
stream.Position = 0;
StringCollection onlineList = (StringCollection)format.Deserialize(stream);
online_cb.Items.Clear();
foreach (string onliner in onlineList)
{
if (!onliner.Equals(_username))
{
online_cb.Items.Add(onliner);
}
}
break;
}
case "12":
{
Nudge();
break;
}
default:
{
string displaytxt = Encoding.Unicode.GetString(packet);
chatrcd_rtb.AppendText(displaytxt);
_sp1.Play();
break;
}
}
}
}
二、服务器的相关行为:
1、服务器监听:
IPAddress _ip = IPAddress.Parse("192.168.28.126");// Dns.GetHostAddresses(Dns.GetHostName())[0];//可能有多个,此时的IP是本地IP
_tcpl = new TcpListener(_ip, _port);
_tcpl.Start();//开始侦听传入的连接请求。
while (true)
{
byte[] packetBuff = new byte[_maxPacket];// 接收数据缓冲区大小64K
/*
//AcceptSocket 是一个阻止方法,该方法返回可用于发送和接收数据的 Socket。
//如果希望避免阻止,请使用 Pending 方法来确定传入连接队列中的连接请求是否可用。
//返回的 Socket 是使用远程主机的 IP 地址和端口号初始化的。
//您可以使用 Socket 类中任何可用的 Send 和 Receive 方法与远程主机进行通信。
//------------使用 Socket 后,请确保调用其 Close 方法。如果应用程序相对简单,-----------
//请考虑使用 AcceptTcpClient 方法代替 AcceptSocket 方法。
//TcpClient 提供了一些通过网络在阻止同步模式下发送和接收数据的简单方法。
*/
Socket newClient = _tcpl.AcceptSocket();//本地TcpListener 接受新的请求
//返回的 Socket 是使用远程主机的 IP 地址和端口号初始化的
newClient.Receive(packetBuff);//将接受的远程SOCKET,将数据存入接受缓冲区
//客户端向服务器发送用户名请求:以下为接收
string userName = Encoding.Unicode.GetString(packetBuff).TrimEnd('\0');
/*
//接着就进行相关的处理:
处理的结果,发送命令给客户端:
如:
newClient.Send(Encoding.Unicode.GetBytes("cmd::Failed"));//发送命令为:cmd::Failed
或newClient.Send(Encoding.Unicode.GetBytes("cmd::Successful"));
*/
//将新连接加入转发表并创建线程为其服务
_transmit_tb.Add(userName, newClient);//直接把用户名、Socket对象作为一对值加入转发表
/*定义如下:
/// <summary>
/// 保存所有客户端会话的哈希表
/// </summary>
private Hashtable _transmit_tb = new Hashtable();
*/
}