两端通信

上篇博文:http://www.cnblogs.com/wolf-sun/p/3329558.html

     介绍了客户端连接服务端,一对一,多对一的情况,下面实现服务器接收消息的功能。LZ这些弄的比较慢,也是边学习,边动手实现的。具体步骤在注释中写的比较清楚,不懂的可以留言,LZ会尽快回复。共同学习,共同进步。

接收消息时机

      什么时候接收消息?当服务器开始监听,有客户端连接,并且连接成功,此时负责通信的Socket已经创建,此时就可以接收消息了,可以通过Socket的Receive()方法接收消息。

 View Code

// 摘要:
// 从绑定的 System.Net.Sockets.Socket 套接字接收数据,将数据存入接收缓冲区。
//
// 参数:
// buffer:
// System.Byte 类型的数组,它是存储接收到的数据的位置。
//
// 返回结果:
// 接收到的字节数。
//
// 异常:
// System.ArgumentNullException:
// buffer 为 null。
//
// System.Net.Sockets.SocketException:
// 试图访问套接字时发生错误。 有关更多信息,请参见备注部分。
//
// System.ObjectDisposedException:
// System.Net.Sockets.Socket 已关闭。
//
// System.Security.SecurityException:
// 调用堆栈中的调用方没有所需的权限。
public int Receive(byte[] buffer);

     上面代码介绍了Receive方法接收参数及返回值。

复制代码
 1  private void ListenConn(object o)
 2         {
 3             //将参数o 转化为监听的socket
 4             Socket socketListener = o as Socket;
 5             //写入循环 每一个连接就创建一个通信用的socket
 6             while (true)
 7             {
 8                 //当有客户端连接成功 创建通信用的socket
 9                 Socket connSocket = socketListener.Accept();
10                 string ip = connSocket.RemoteEndPoint.ToString();
11                 ShowMsg(ip + " " + DateTime.Now.ToString() + " 连接成功");
12                //创建一个新线程去接收消息
13                 Thread th = new Thread(ReceiveMsg);
14                 th.Start(connSocket);
15 
16             }
17 
18 }
复制代码

接收消息的代码:

复制代码
 1   //接收客户端的消息
 2         private void ReceiveMsg(object o)
 3         {
 4             Socket connSocket = o as Socket;
 5 
 6             //通信用的socket连接成功 就可以接收消息了
 7             byte[] buffer = new byte[1024 * 1024 * 5];//5M缓存
 8             while (true)
 9             {
10                 //count是当前接收的字节个数
11                 int count = connSocket.Receive(buffer);
12                 string ip = connSocket.RemoteEndPoint.ToString();
13                 //判断接收到的字节个数 是0表示客户端关闭了
14                 if (count > 0)
15                 {
16 
17                     //将字节转换为字符串
18                     string msg = Encoding.UTF8.GetString(buffer, 0, count);
19                     ShowMsg(ip + " " + DateTime.Now.ToString() + "
" + msg);
20                 }
21                 else
22                 {
23                     //socket没办法发送空消息 如果收到空消息 客户端关闭
24                     ShowMsg(ip + ":" + "断开连接");
25                     connSocket.Close();
26                     break;
27 
28                 }
29 
30             }
31 
32         }
复制代码

测试:仍然用telnet命令来测试:telnet 127.0.0.1 50000

     测试结果:多对一,一对一,发送消息正常,关闭客户端,服务端正常显示哪个客户端断开连接。

     服务器端所有代码:

 View Code

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace Wolfy.ChatServer
{
public partial class Server : Form
{
public Server()
{
InitializeComponent();
//不让其检查跨线程的操作
Control.CheckForIllegalCrossThreadCalls = false;
}
//存放endpoin和通信用的socket
Dictionary<string, Socket> dic = new Dictionary<string, Socket>();
private void btnSend_Click(object sender, EventArgs e)
{
ServerSendMsg(this.txtInputMsg.Text);
}
/// <summary>
/// 服务器给客户端发送消息
/// </summary>
private void ServerSendMsg(string msg)
{
//服务器给客户端发消息
string userkey = comboBoxEndpoint.Text;
if (!string.IsNullOrEmpty(userkey))
{
ShowMsg(msg);
byte[] buffer = Encoding.UTF8.GetBytes(msg);
dic[userkey].Send(buffer);
msg = "";
}
else
{
MessageBox.Show("请选择客户端");
}
}

private void btnStartService_Click(object sender, EventArgs e)
{
//服务器ip地址
IPAddress ip = IPAddress.Parse(txtIPAddress.Text);
//ip地址和端口
IPEndPoint endpoint = new IPEndPoint(ip, int.Parse(txtPort.Text));
//创建用于监听的socket
Socket socketListener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//绑定ip和端口
socketListener.Bind(endpoint);
//开始监听 限制连接数 最多可以连接10个
socketListener.Listen(10);
ShowMsg("开始监听......");
//创建线程 去监听连接
Thread th = new Thread(ListenConn);
//将线程变为后台线程
th.IsBackground = true;
th.Start(socketListener);
}
private void ListenConn(object o)
{
//将参数o 转化为监听的socket
Socket socketListener = o as Socket;
//写入循环 每一个连接就创建一个通信用的socket
while (true)
{
//当有客户端连接成功 创建通信用的socket
Socket connSocket = socketListener.Accept();
string ip = connSocket.RemoteEndPoint.ToString();
ShowMsg(ip + " " + DateTime.Now.ToString() + " 连接成功");
//连接成功后加入字典
dic.Add(ip, connSocket);
comboBoxEndpoint.Items.Add(ip);
//创建一个新线程去接收消息
Thread th = new Thread(ReceiveMsg);
th.Start(connSocket);
}

}
//接收客户端的消息
private void ReceiveMsg(object o)
{
Socket connSocket = o as Socket;

//通信用的socket连接成功 就可以接收消息了
byte[] buffer = new byte[1024 * 1024 * 5];//5M缓存
while (true)
{
//count是当前接收的字节个数
int count = connSocket.Receive(buffer);
string ip = connSocket.RemoteEndPoint.ToString();
//判断接收到的字节个数 是0表示客户端关闭了
if (count > 0)
{
//将字节转换为字符串
string msg = Encoding.UTF8.GetString(buffer, 0, count);
ShowMsg(ip + " " + DateTime.Now.ToString() + " " + msg);
}
else
{
//socket没办法发送空消息 如果收到空消息 客户端关闭
ShowMsg(ip + ":" + "断开连接");
connSocket.Close();
break;
}

}

}
/// <summary>
/// 提示信息辅助方法
/// </summary>
/// <param name="msg"></param>
private void ShowMsg(string msg)
{
this.txtMsgView.AppendText(msg + " ");
}

private void txtInputMsg_KeyUp(object sender, KeyEventArgs e)
{
//如果用户按下了Enter键
if (e.KeyCode == Keys.Enter)
{
//则调用 服务器向客户端发送信息的方法
ServerSendMsg(this.txtInputMsg.Text);
}
}
}
}

客户端实现

     客户端创建的socket即负责连接又负责通信,所以这里和服务端不同。客户端连接、接收消息和发送消息代码如下:

 View Code

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Net;
using System.Threading;
namespace Wolf.ChatClient
{
public partial class Client : Form
{
public Client()
{
InitializeComponent();
//不让检查跨线程操作
Control.CheckForIllegalCrossThreadCalls = false;
}
Socket socket;
private void btnStartService_Click(object sender, EventArgs e)
{
//连接服务器的ip和端口
IPAddress ip = IPAddress.Parse(txtIPAddress.Text);
IPEndPoint endpoint = new IPEndPoint(ip, int.Parse(txtPort.Text));
//客户端的socket即负责连接又负责通信
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//连接服务器
socket.Connect(endpoint);
ShowMsg("连接成功......");
//和服务器连接成功后就可以接收服务端的消息了
Thread th = new Thread(ReceiveMsg);
th.IsBackground = true;
th.Start();

}
private void ReceiveMsg()
{
byte[] buffer = new byte[1024 * 1024 * 5];
while (true)
{
int count = socket.Receive(buffer);
string msg = Encoding.UTF8.GetString(buffer, 0, count);
ShowMsg(this.txtIPAddress.Text + ":" + this.txtPort.Text + " " + DateTime.Now.ToString() + " " + msg);
}
}
private void ShowMsg(string msg)
{
txtMsgView.AppendText(msg + " ");
}
private void btnSend_Click(object sender, EventArgs e)
{
ClientSendMsg(this.txtInputMsg.Text);
}

/// <summary>
/// 客户端向服务端发送消息
/// </summary>
/// <param name="msg"></param>
private void ClientSendMsg(string msg)
{
//向服务端发送消息
if (socket != null)
{
ShowMsg(msg);
byte[] buffer = Encoding.UTF8.GetBytes(msg);
socket.Send(buffer);
msg = "";
}
else
{
ShowMsg("<<<<请先连接服务器>>>");
}
}

private void txtInputMsg_KeyUp(object sender, KeyEventArgs e)
{
//如果用户按下了Enter键
if (e.KeyCode == Keys.Enter)
{
//则调用 服务器向客户端发送信息的方法
ClientSendMsg(this.txtInputMsg.Text);
}
}

private void Client_FormClosing(object sender, FormClosingEventArgs e)
{
//客户端关闭 关闭socket
socket.Shutdown(SocketShutdown.Both);
}
}
}

测试结果:


 结语:

       边学习,边动手,实现了两端通信,目前支持一对一,多对一通信功能,代码中针对关闭客户端的情况还有bug,有待进一步修改。

Alternate Text 作者:Wolfy
出处:http://www.cnblogs.com/wolf-sun/
本文以学习、研究和分享为主,欢迎转载,但必须在文章页面明显位置给出原文连接。愿与志同道合的朋友一起成长......
 
分类: 网络编程
原文地址:https://www.cnblogs.com/Leo_wl/p/3332357.html