C#基于TCP&UDP实现服务器与多个客户端之间的通信(客户端之间直接通信,不靠服务器端转发消息)
2014-06-12 21:06
1236 查看
一. 实验要求
1.Server支持多客户访问。
2.C与S之间使用TCP连接。
3.C与C之间直接通信(不是通过S传递)。
4.C与C之间直接通信既可以使用TCP,也可以使用UDP。
5.可以使用Socket,也可以使用TcpClient/UdpClient等。
二. 设计思路
1. 创建服务器端和客户端的主体框架:首先在服务器端的Windows窗体中添加5个Label控件,2个TextBox控件,2
个RichTextBox控件,1个ListBox控件和1个Button控件;然后在客户端的Windows窗体中添加4个Label控件,1
个TextBox控件,2个RichTextBox控件,1个ListBox控件,2个Button控件。建立程序的主要界面后,系统自动生
成界面的主要窗口生成代码。在服务器端和客户端的每个按钮的代码段中,分别添加事件触发的处理代码。其 中服务器端的2个TextBox用来存放IP地址和端口号,而ListBox用来显示当前连接服务器的客户端,RichTextBox
用来显示双方的通信信息。
2. 实现C/S的通信,使用的是TCP:
服务器端:
a)定义一个User类来存放客户的信息,包括名称与端口。
b)定义一个存放User类型的List集合,用于存放所有在线客户,获取本机的IP地址,并制定一个端口号,然
后创建一个TcpListener对象,并通过调用将对象的Start方法在指定的端口进行监听。
c)在单独的线程中,循环调用AcceptTcpClient方法接受客户端的连接请求,并根据该方法的返回结果得到 与该客户端对应的TcpClient对象,然后把该对象存放到List集合中。
d)每得到一个新的TcpClient对象,就创建一个与该客户对应的线程,在线程中与对应的客户进行通信;当 收到的是“Login”信息时,就把该客户的名称加到在线列表中,并把接收到的包括用户名及随机产生的
端口在内的信息转发给所有在线客户;若收到的是“Logout”信息,则把该用户从在线列表中移除;若 收到的是“Talk”信息,则彼此进行通信。
客户端:
a)定义一个User类来存放客户的信息,包括名称与端
4000
口。
b)利用TcpClient的构造函数创建一个TcpClient对象,与服务器端建立
连接。
c)利用TcpClient对象的GetStream方法得到网络流,然后利用该网络流
与服务器进行数据传输。
d)创建一个线程监听指定的端口,循环接收并处理服务器发送过来的消 息。
e)完成工作后,向服务器发送关闭消息,并关闭与服务器的连接。
3.实现C/C直接通信,使用的是UDP:
a)在客户端,用户一旦连接上服务器,就随机产生一个端口,并把用户和此端口发给服务器,服务器再将 各用户对应的名称及端口转发给所有在线用户,因此,服务器在C与C通信的过程中只是充当转发端口的
角色,一旦用户得知要与之通信的用户的IP地址及端口时,就可直接通信。
b)在每个用户随机产生的端口进行UDP监听,当用户接收到服务器转发的其他用户端口时,创建一个线程 循环接收并处理客户端发送过来的消息。
c)客户端与客户端发送消息时,指定想发送的那个客户端的IP地址和端口,就可以进行通信。
三.TCP+UDP通信核心代码:
服务器端:
/// <summary>保存连接的所有用户</summary>
private List<User> userList = new List<User>();
/// <summary>使用的本机IP地址</summary>
IPAddress localAddress;
/// <summary>监听端口</summary>
private const int port = 51888;
private TcpListener myListener;
/// <summary>是否正常退出所有连接线程 </summary>
bool isNormalExit = false;
IPEndPoint iep;
IPHostEntry local;
User user;
TcpClient newClient;
public Server()
{
InitializeComponent();
UserNameList.HorizontalScrollbar = true;
local = Dns.GetHostByName(Dns.GetHostName());
iep = new IPEndPoint(local.AddressList[0], port);
this.iPTextBox.Text = local.AddressList[0].ToString();
this.portTextBox.Text = port.ToString();
}
private void start_button_Click(object sender, EventArgs e)
{
}
private void Server_Load(object sender, EventArgs e)
{
myListener = new TcpListener(iep);
myListener.Start();
AddItemToRichTextBox(string.Format("服务器已启动\n"));
//创建一个线程监听客户端连接请求
Thread myThread = new Thread(ListenClientConnect);
myThread.Start();
myThread.IsBackground = true;
iPTextBox.Enabled = false;
portTextBox.Enabled = false;
}
/// <summary>接收客户端连接</summary>
private void ListenClientConnect()
{
newClient = null;
while (true)
{
try
{
newClient = myListener.AcceptTcpClient();
}
catch
{
break;
}
//每接收一个客户端连接,就创建一个对应的线程循环接收该客户端发来的消息
user = new User(newClient);
Thread threadReceive = new Thread(ReceiveData);
threadReceive.Start(user);
userList.Add(user);
SendMessage(string.Format("Talk,Hello I am Server!"));
threadReceive.IsBackground = true;
}
}
/// <summary>
/// 发送信息给所有客户
/// </summary>
/// <param name="user">指定发给哪个用户</param>
/// <param name="message">信息内容</param>
private void SendToAllClient(User user, string message)
{
string command = message.Split(',')[0].ToLower();
if (command == "login")
{
for (int i = 0; i < userList.Count; i++)
{
SendToClient(userList[i],message);
if (userList[i].userName != user.userName)
{
SendToClient(user,"login,"+userList[i].userName+","+userList[i].userPort);
}
}
}
else if (command == "logout")
{
for (int i = 0; i < userList.Count; i++)
{
if (userList[i].userName != user.userName)
{
SendToClient(userList[i],message);
}
}
}
}
客户端:
private bool isExit = false;
//存放客户端名称及端口
private List<User> userUdpList = new List<User>();
private TcpClient client;
private BinaryReader br;
private BinaryWriter bw;
private UdpClient receiveUdpClient;
private UdpClient sendUdpClient;
IPAddress IP;
int random_port;
User user;
public Client()
{
InitializeComponent();
Random r = new Random((int)DateTime.Now.Ticks);
user_textBox.Text = "User" + r.Next(100,999);
user_listBox.HorizontalScrollbar = true;
}
private void Client_Load(object sender, EventArgs e)
{
}
private void login_button_Click(object sender, EventArgs e)
{
login_button.Enabled = false;
user_textBox.Enabled = false;
try
{
client = new TcpClient(Dns.GetHostName(),51888);
//随机产生一个端口,并在此端口进行UDP监听
Random random = new Random();
random_port = random.Next(60000);
IP=Dns.GetHostByName(Dns.GetHostName()).AddressList[0];
IPEndPoint local = new IPEndPoint(IP,random_port);
receiveUdpClient = new UdpClient(local);
}
catch
{
MessageBox.Show("连接失败!");
login_button.Enabled = true;
return;
}
NetworkStream networkStream = client.GetStream();
br = new BinaryReader(networkStream);
bw = new BinaryWriter(networkStream);
//把用户名及随机产生的端口发到服务器
SendMessageToServer("Login," + user_textBox.Text + "," + random_port);
//接收服务器消息的线程
Thread threadReceive = new Thread(new ThreadStart(ReceiveFromServer));
threadReceive.IsBackground = true;
threadReceive.Start();
}
四.程序运行效果图
1.Server支持多客户访问。
2.C与S之间使用TCP连接。
3.C与C之间直接通信(不是通过S传递)。
4.C与C之间直接通信既可以使用TCP,也可以使用UDP。
5.可以使用Socket,也可以使用TcpClient/UdpClient等。
二. 设计思路
1. 创建服务器端和客户端的主体框架:首先在服务器端的Windows窗体中添加5个Label控件,2个TextBox控件,2
个RichTextBox控件,1个ListBox控件和1个Button控件;然后在客户端的Windows窗体中添加4个Label控件,1
个TextBox控件,2个RichTextBox控件,1个ListBox控件,2个Button控件。建立程序的主要界面后,系统自动生
成界面的主要窗口生成代码。在服务器端和客户端的每个按钮的代码段中,分别添加事件触发的处理代码。其 中服务器端的2个TextBox用来存放IP地址和端口号,而ListBox用来显示当前连接服务器的客户端,RichTextBox
用来显示双方的通信信息。
2. 实现C/S的通信,使用的是TCP:
服务器端:
a)定义一个User类来存放客户的信息,包括名称与端口。
b)定义一个存放User类型的List集合,用于存放所有在线客户,获取本机的IP地址,并制定一个端口号,然
后创建一个TcpListener对象,并通过调用将对象的Start方法在指定的端口进行监听。
c)在单独的线程中,循环调用AcceptTcpClient方法接受客户端的连接请求,并根据该方法的返回结果得到 与该客户端对应的TcpClient对象,然后把该对象存放到List集合中。
d)每得到一个新的TcpClient对象,就创建一个与该客户对应的线程,在线程中与对应的客户进行通信;当 收到的是“Login”信息时,就把该客户的名称加到在线列表中,并把接收到的包括用户名及随机产生的
端口在内的信息转发给所有在线客户;若收到的是“Logout”信息,则把该用户从在线列表中移除;若 收到的是“Talk”信息,则彼此进行通信。
客户端:
a)定义一个User类来存放客户的信息,包括名称与端
4000
口。
b)利用TcpClient的构造函数创建一个TcpClient对象,与服务器端建立
连接。
c)利用TcpClient对象的GetStream方法得到网络流,然后利用该网络流
与服务器进行数据传输。
d)创建一个线程监听指定的端口,循环接收并处理服务器发送过来的消 息。
e)完成工作后,向服务器发送关闭消息,并关闭与服务器的连接。
3.实现C/C直接通信,使用的是UDP:
a)在客户端,用户一旦连接上服务器,就随机产生一个端口,并把用户和此端口发给服务器,服务器再将 各用户对应的名称及端口转发给所有在线用户,因此,服务器在C与C通信的过程中只是充当转发端口的
角色,一旦用户得知要与之通信的用户的IP地址及端口时,就可直接通信。
b)在每个用户随机产生的端口进行UDP监听,当用户接收到服务器转发的其他用户端口时,创建一个线程 循环接收并处理客户端发送过来的消息。
c)客户端与客户端发送消息时,指定想发送的那个客户端的IP地址和端口,就可以进行通信。
三.TCP+UDP通信核心代码:
服务器端:
/// <summary>保存连接的所有用户</summary>
private List<User> userList = new List<User>();
/// <summary>使用的本机IP地址</summary>
IPAddress localAddress;
/// <summary>监听端口</summary>
private const int port = 51888;
private TcpListener myListener;
/// <summary>是否正常退出所有连接线程 </summary>
bool isNormalExit = false;
IPEndPoint iep;
IPHostEntry local;
User user;
TcpClient newClient;
public Server()
{
InitializeComponent();
UserNameList.HorizontalScrollbar = true;
local = Dns.GetHostByName(Dns.GetHostName());
iep = new IPEndPoint(local.AddressList[0], port);
this.iPTextBox.Text = local.AddressList[0].ToString();
this.portTextBox.Text = port.ToString();
}
private void start_button_Click(object sender, EventArgs e)
{
}
private void Server_Load(object sender, EventArgs e)
{
myListener = new TcpListener(iep);
myListener.Start();
AddItemToRichTextBox(string.Format("服务器已启动\n"));
//创建一个线程监听客户端连接请求
Thread myThread = new Thread(ListenClientConnect);
myThread.Start();
myThread.IsBackground = true;
iPTextBox.Enabled = false;
portTextBox.Enabled = false;
}
/// <summary>接收客户端连接</summary>
private void ListenClientConnect()
{
newClient = null;
while (true)
{
try
{
newClient = myListener.AcceptTcpClient();
}
catch
{
break;
}
//每接收一个客户端连接,就创建一个对应的线程循环接收该客户端发来的消息
user = new User(newClient);
Thread threadReceive = new Thread(ReceiveData);
threadReceive.Start(user);
userList.Add(user);
SendMessage(string.Format("Talk,Hello I am Server!"));
threadReceive.IsBackground = true;
}
}
/// <summary>
/// 发送信息给所有客户
/// </summary>
/// <param name="user">指定发给哪个用户</param>
/// <param name="message">信息内容</param>
private void SendToAllClient(User user, string message)
{
string command = message.Split(',')[0].ToLower();
if (command == "login")
{
for (int i = 0; i < userList.Count; i++)
{
SendToClient(userList[i],message);
if (userList[i].userName != user.userName)
{
SendToClient(user,"login,"+userList[i].userName+","+userList[i].userPort);
}
}
}
else if (command == "logout")
{
for (int i = 0; i < userList.Count; i++)
{
if (userList[i].userName != user.userName)
{
SendToClient(userList[i],message);
}
}
}
}
客户端:
private bool isExit = false;
//存放客户端名称及端口
private List<User> userUdpList = new List<User>();
private TcpClient client;
private BinaryReader br;
private BinaryWriter bw;
private UdpClient receiveUdpClient;
private UdpClient sendUdpClient;
IPAddress IP;
int random_port;
User user;
public Client()
{
InitializeComponent();
Random r = new Random((int)DateTime.Now.Ticks);
user_textBox.Text = "User" + r.Next(100,999);
user_listBox.HorizontalScrollbar = true;
}
private void Client_Load(object sender, EventArgs e)
{
}
private void login_button_Click(object sender, EventArgs e)
{
login_button.Enabled = false;
user_textBox.Enabled = false;
try
{
client = new TcpClient(Dns.GetHostName(),51888);
//随机产生一个端口,并在此端口进行UDP监听
Random random = new Random();
random_port = random.Next(60000);
IP=Dns.GetHostByName(Dns.GetHostName()).AddressList[0];
IPEndPoint local = new IPEndPoint(IP,random_port);
receiveUdpClient = new UdpClient(local);
}
catch
{
MessageBox.Show("连接失败!");
login_button.Enabled = true;
return;
}
NetworkStream networkStream = client.GetStream();
br = new BinaryReader(networkStream);
bw = new BinaryWriter(networkStream);
//把用户名及随机产生的端口发到服务器
SendMessageToServer("Login," + user_textBox.Text + "," + random_port);
//接收服务器消息的线程
Thread threadReceive = new Thread(new ThreadStart(ReceiveFromServer));
threadReceive.IsBackground = true;
threadReceive.Start();
}
四.程序运行效果图
相关文章推荐
- Java基于UDP实现服务器和多客户端之间的通信
- Java基于TCP实现服务器和多客户端之间的通信
- c#中异步基于消息通信的完成端口的TCP-IP协议的组件实现(服务器端-源代码)
- Socket 通信原理(Android客户端和服务器以TCP&&UDP方式互通)
- Socket 通信原理(Android客户端和服务器以TCP&&UDP方式互通)
- Socket 通信原理(Android客户端和服务器以TCP&&UDP方式互通)
- Socket 通信原理(Android客户端和服务器以TCP&&UDP方式互通)
- 基于Tcp通信的聊天程序微风IM(c#开源) -技术分析(二) 消息转发
- Socket 通信原理 -- Android客户端和服务器以TCP&&UDP方式互通
- VB.NET C#实现基于UDP的免服务器局域网多客户端点对点通讯
- [c#源码分享]客户端程序通过TCP通信传送"小文件"到服务器
- c#中异步基于消息通信的完成端口的TCP/IP协议的组件实现(源代码) 客户端
- 基于Tcp通信的聊天程序微风IM(c#开源) -技术分析(二) 消息转发
- Socket 通信原理(Android客户端和服务器以TCP&&UDP方式互通)
- Socket 通信原理(Android客户端和服务器以TCP&&UDP方式互通)
- c#中异步基于消息通信的完成端口的TCP/IP协议的组件实现(客户端-源代码)
- 【转】Socket 通信原理(Android客户端和服务器以TCP&&UDP方式互通)
- Socket 通信原理(Android客户端和服务器以TCP&&UDP方式互通)
- 某实例+修改 Winsock实现基于TCP的客户端/服务器通信
- Socket 通信原理(Android客户端和服务器以TCP&&UDP方式互通)