本人技术有限,可能代码很烂~~令各位看官忍不住想骂。。。我希望个为看官们可以原谅我这位菜鸟,有指教可以留言发表,但是请不要恶语伤害我幼小的心灵。
公共代码:
/// <summary>
/// 用户类
/// </summary>
public class User
{
/// <summary>
/// 用户名
/// </summary>
private string name = "";
/// <summary>
/// 用户的所在网络地址
/// </summary>
private EndPoint location = null;
/// <summary>
/// 用户名属性
/// </summary>
public string Name
{
get{ return name;}
}
/// <summary>
/// 用户地址属性
/// </summary>
public EndPoint Location
{
get{ return location;}
}
/// <summary>
/// 返回一个用户实例
/// </summary>
/// <param name="_name">用户名</param>
/// <param name="_location">用户地址</param>
public User(string _name, EndPoint _location)
{
name = _name;
location = _location;
}
}
}
客户端程序代码:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Collections;
namespace Client
{
/// <summary>
/// Class1 的摘要说明。
/// </summary>
class ClientApp
{
#region 参数...
/*客户端向服务器*/
const string LOGIN = "11";//客户端登录
const string LOGOUT = "12";//客户端下线
const string GETLIST = "13";//请求用户列表
const string P2PCON = "14";//请求P2P连接
/*服务器向客户端*/
const string NONUSER = "21";//请求连接的用户不存在
const string RENAME = "22";//登录用户名已经存在
const string USERLIST = "23";//在线用户列表
const string MAKHOLD = "24";//UDP打洞命令
const string LOGINOK = "25";//登陆成功
const string SERVCLS = "26";//服务器关闭
const string MSGEND = "27";//消息结束
/*客户端向客户端*/
const string P2PMSG = "31";//客户端消息
const string HOLDMG = "32";//UDP打洞消息
const string RECMSG = "33";//消息应答
const string PINGMG = "34";//ping客户端
/*网络参数*/
const int MaxTry = 5;//消息发送的重试次数
/*客户端命令行*/
const string GETUSERS = "getu";//获取在线用户列表
const string SENDMSG = "send";//发送消息给某个客户端
const string EXIT = "exit";//退出程序
const string PING = "ping";
const string USERS = "user";//打印在线用户
const string HELP = "help";//帮助
/*程序变量*/
static bool more = true;
static bool ok = true;
static string Username = "";//用户名
static ArrayList users = new ArrayList();
static Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
static IPEndPoint serverEP = new IPEndPoint(IPAddress.Parse("222.76.56.239"), 8888);
static Thread listenThr = new Thread(new ThreadStart(Listen));
#endregion
#region 方法...
static private void Listen()
{
while(true)
{
int recv = 0;//接收到的消息字节数
byte[] data = new byte[1024];//消息缓冲区
IPEndPoint sender = new IPEndPoint(IPAddress.Any,0);
EndPoint remote = (EndPoint)sender;//消息来源的远程地址
recv = sock.ReceiveFrom(data, ref remote);
string head = Encoding.Unicode.GetString(data,0,4);//获取消息头两个字节的标记
switch(head)
{
case RECMSG:
ok = true;
break;
case USERLIST:
GetUserList(data, recv);
break;
case MSGEND:
more = false;
break;
case SERVCLS:
Console.WriteLine("/n服务器已关闭!/n");
Console.Write("#Command:");
break;
case MAKHOLD:
string recever = Encoding.Unicode.GetString(data, 4,20);
int index = FindUser(recever);
if(index != -1)
{
sock.SendTo(Encoding.Unicode.GetBytes(HOLDMG),((User)users[index]).Location);
}
break;
case P2PMSG:
Console.WriteLine("/n收到来自" + Encoding.Unicode.GetString(data,4, 20) +
"的消息:" + Encoding.Unicode.GetString(data,24, recv - 24));
sock.SendTo(Encoding.Unicode.GetBytes(RECMSG), remote);
Console.Write("/n#Command:");
break;
case HOLDMG:
break;
case PINGMG:
break;
}
}
}
/*用户登录*/
static string UserLogIn()
{
IPEndPoint sender = new IPEndPoint(IPAddress.Any,0);
EndPoint remote = (EndPoint)sender;//消息来源的远程地址
Console.Write("请输入一个用户名:");
string username = Console.ReadLine();
byte[] temp = Encoding.Unicode.GetBytes(username);
if(temp.Length > 20)
{
Console.WriteLine("用户名不能超过20个字节!/n");
return "";
}
username = username.PadRight(10,' ');
byte[] data = Encoding.Unicode.GetBytes(LOGIN + username.ToUpper());
sock.SendTo(data,serverEP);
//TODO:记得在此添加超时处理
int recv = sock.Receive(data);
if(Encoding.Unicode.GetString(data,0, recv) == LOGINOK)
{
Console.WriteLine("登陆成功!/n");
ShowHelp();
Console.WriteLine();
return username;
}
else
{
Console.WriteLine("登陆失败,用户名已存在!/n");
return "";
}
}
/*发送消息*/
static private bool SendMsg(string name, string msg)
{
bool result = false;
name = name.PadRight(10,' ');
int index = FindUser(name);
if(index != -1)
{
byte[] toServ = Encoding.Unicode.GetBytes(P2PCON + name + Username);
byte[] toClient = Encoding.Unicode.GetBytes(P2PMSG + Username + msg);
ok = false;
for(int i=0; i<MaxTry; i++)
{
if(ok)
break;
sock.SendTo(toClient,((User)users[index]).Location);
Thread.Sleep(1000);
}
for(int i=0; i<MaxTry; i++)
{
if(ok)
break;
sock.SendTo(toServ, serverEP);
Thread.Sleep(1000);
sock.SendTo(toClient,((User)users[index]).Location);
}
if(!ok)
{
Console.WriteLine("消息发送失败。/n");
}
else
{
Console.WriteLine("消息发送成功。/n");
}
}
else
{
Console.WriteLine("用户不存在。/n");
}
return result;
}
/*获取用户列表*/
static private void GetUserList(byte[] data, int recv)
{
string userdata = Encoding.Unicode.GetString(data, 4, recv-4);
string username = userdata.Substring(0,10);
if(FindUser(username) == -1)
{
int portIndex = userdata.IndexOf(':');
string address = userdata.Substring(10, userdata.Length - (userdata.Length - portIndex) - 10);
IPEndPoint userEP = new IPEndPoint(IPAddress.Parse(address), int.Parse(userdata.Substring(portIndex+1)));
EndPoint location = (EndPoint)userEP;
users.Add(new User(username, location));
}
}
/*打印在线用户名单*/
static private void PrintfUsers()
{
if(users.Count != 0)
{
Console.WriteLine("在线用户:");
for(int i=0; i<users.Count; i++)
{
Console.WriteLine(((User)users[i]).Name + "/t/t" +((User)users[i]).Location.ToString());
}
Console.WriteLine("/n");
}
else
{
Console.WriteLine("目前没有在线用户/n");
}
}
/*查找用户*/
static private int FindUser(string _name)
{
int result = -1;
for(int i=0; i<users.Count; i++)
{
if(((User)users[i]).Name == _name)
{
result = i;
break;
}
}
return result;
}
static void WaitUserList()
{
byte[] data = Encoding.Unicode.GetBytes(GETLIST);
sock.SendTo(data, serverEP);
more = true;
for(int i=0; i<MaxTry; i++)
{
if(!more)
break;
Thread.Sleep(1000);
}
if(more)
{
Console.WriteLine("获取用户列表失败!服务器超时!/n");
}
else
{
PrintfUsers();
}
}
static void ShowHelp()
{
Console.WriteLine("/t/t---------------------------------");
Console.WriteLine("/t/t| GETU 获取在线用户列表 |");
Console.WriteLine("/t/t| SEND 发送消息给某个客户端 |");
Console.WriteLine("/t/t| USER 打印在线用户 |");
Console.WriteLine("/t/t| EXIT 退出程序 |");
Console.WriteLine("/t/t---------------------------------");
}
#endregion
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main(string[] args)
{
bool loop = true;
//byte[] data = new byte[1024];
while(loop)
{
Console.Write("请输入服务器IP:");
string ip = Console.ReadLine();
try
{
serverEP = new IPEndPoint(IPAddress.Parse(ip), 8888);
loop = false;
}
catch
{
Console.Write("请输入IP不正确。/n");
loop = true;
}
}
loop = true;
/*用户登录*/
while(Username == "")
{
Username = UserLogIn();
}
listenThr.Start();
while(loop)
{
Console.Write("#Command:");
string input = Console.ReadLine();
if(input.Length < 4)
{
Console.WriteLine("'" + input + "'" + "不是有效命令!/n");
continue;
}
switch(input.Substring(0,4).ToLower())
{
case GETUSERS:
if(input.Length == 4)
{
WaitUserList();
}
else
{
Console.WriteLine("'" +input+"'" +"不是有效命令!/n");
}
break;
case USERS:
if(input.Length == 4)
{
PrintfUsers();
}
else
{
Console.WriteLine("'" +input+"'" +"不是有效命令!/n");
}
break;
case SENDMSG:
if(input.Length > 5 && input[4] == ' ')
{
int Nindex = input.IndexOf(' ',5);
if(Nindex < 0 || Nindex == input.Length-1)
{
Console.WriteLine("Send命令格式应当如下:");
Console.WriteLine("SEND [用户名] [消息内容]/n");
break;
}
string name = input.Substring(5,Nindex-5).ToUpper();
string msg = input.Substring(Nindex+1);
SendMsg(name, msg);
}
else
{
Console.WriteLine("'" +input+"'" +"不是有效命令!/n");
}
break;
case EXIT:
if(input.Length==4)
{
loop = false;
byte[] temp = Encoding.Unicode.GetBytes(Username);
byte[] data = Encoding.Unicode.GetBytes(LOGOUT + temp.Length.ToString("0#") + Username.ToUpper());
listenThr.Abort();
sock.SendTo(data, serverEP);
sock.Close();
}
else
{
Console.WriteLine("'" +input+"'" +"不是有效命令!/n");
}
break;
case PING:
if(input.Length > 5 && input[4] == ' ')
{
Console.WriteLine("命令完成。/n");
}
else
{
Console.WriteLine("'" +input+"'" +"不是有效命令!/n");
}
break;
case HELP:
ShowHelp();
break;
default:
Console.WriteLine("'" +input+"'" +"不是有效命令!/n");
break;
}
}
}
}
服务器端程序代码:
using System;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace Server
{
/// <summary>
/// Class1 的摘要说明。
/// </summary>
class ServerApp
{
#region 参数...
/*客户端向服务器*/
const string LOGIN = "11";//客户端登录
const string LOGOUT = "12";//客户端下线
const string GETLIST = "13";//请求用户列表
const string P2PCON = "14";//请求P2P连接
/*服务器向客户端*/
const string NONUSER = "21";//请求连接的用户不存在
const string RENAME = "22";//登录用户名已经存在
const string USERLIST = "23";//在线用户列表
const string MAKHOLD = "24";//UDP打洞命令
const string LOGINOK = "25";//登陆成功
const string SERVCLS = "26";//服务器关闭
const string MSGEND = "27";//消息结束
/*服务器命令行*/
const string EXIT = "exit";//退出程序
const string USERS = "user";//打印在线用户
/*程序变量*/
static ArrayList users = new ArrayList();//用户列表
static Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
static IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 8888);
static Thread listenThr = new Thread(new ThreadStart(Listen));
#endregion
#region 方法...
static private void Listen()
{
while(true)
{
int recv = 0;//接收到的消息字节数
byte[] data = new byte[1024];//消息缓冲区
IPEndPoint sender = new IPEndPoint(IPAddress.Any,0);
EndPoint remote = (EndPoint)sender;//消息来源的远程地址
recv = sock.ReceiveFrom(data, ref remote);
string head = Encoding.Unicode.GetString(data,0,4);//获取消息头两个字节的标记
switch(head)
{
case LOGIN:
UserLogIn(data, remote);
break;
case LOGOUT:
UserLogOut(data);
break;
case GETLIST:
GetUserList(remote);
break;
case P2PCON:
PeerConnect(data, remote);
break;
}
}
}
/*查找用户*/
static private int FindUser(string _name)
{
int result = -1;
for(int i=0; i<users.Count; i++)
{
if(((User)users[i]).Name == _name)
{
result = i;
break;
}
}
return result;
}
/*用户登录*/
static private void UserLogIn(byte[] data, EndPoint _remote)
{
bool error = false;
/*新登记的用户*/
User newuser = new User(Encoding.Unicode.GetString(data,4,20), _remote);
/*判断用户是否重名*/
error = (FindUser(newuser.Name) != -1);
if(!error)
{
users.Add(newuser);
Console.WriteLine("/n用户 " + newuser.Name + " 登陆, 地址:"+ newuser.Location.ToString() + "/n");
Console.Write("#Command:");
sock.SendTo(Encoding.Unicode.GetBytes(LOGINOK), _remote);
for(int i=0; i< users.Count; i++)
{
GetUserList(((User)users[i]).Location);
}
}
else
{
sock.SendTo(Encoding.Unicode.GetBytes(RENAME), _remote);
}
}
/*用户下线*/
static private void UserLogOut(byte[] data)
{
/*获取户名并删除其记录*/
int lenght = int.Parse(Encoding.Unicode.GetString(data,4,4));//用户名长度
string username = Encoding.Unicode.GetString(data,8,lenght);//用户名
int index = FindUser(username);//用户在列表中的索引
if(index != -1)
{
users.RemoveAt(index);
Console.WriteLine("/n用户 " + username + " 退出。/n");
Console.Write("#Command:");
}
}
/*获取在线用户列表*/
static private void GetUserList(EndPoint remote)
{
for(int i=0; i<users.Count; i++)
{
byte[] userdata = Encoding.Unicode.GetBytes(USERLIST +((User)users[i]).Name + ((User)users[i]).Location.ToString());
sock.SendTo(userdata, remote);
}
sock.SendTo(Encoding.Unicode.GetBytes(MSGEND),remote);
}
/*命令进行端到端连接*/
static private void PeerConnect(byte[] data, EndPoint remote)
{
string recever = Encoding.Unicode.GetString(data, 4,20);
string sender = Encoding.Unicode.GetString(data, 20,20);
byte[] msg = Encoding.Unicode.GetBytes(MAKHOLD + sender);
int index = FindUser(recever);
if(index != -1)
{
sock.SendTo(msg, ((User)users[index]).Location);
}
else
{
sock.SendTo(Encoding.Unicode.GetBytes(NONUSER), remote);
}
}
/*打印在线用户名单*/
static private void PrintfUsers()
{
if(users.Count != 0)
{
Console.WriteLine("在线用户:");
for(int i=0; i<users.Count; i++)
{
Console.WriteLine(((User)users[i]).Name + "/t/t" +((User)users[i]).Location.ToString());
}
Console.WriteLine("/n");
}
else
{
Console.WriteLine("目前没有在线用户/n");
}
}
#endregion
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main(string[] args)
{
bool loop = true;
sock.Bind(ipep);
listenThr.Start();
Console.WriteLine("服务器已启动...../n");
while(loop)
{
Console.Write("#Command:");
string input = Console.ReadLine();
if(input.Length < 4)
{
Console.WriteLine("'" + input + "'" +"不是有效命令!/n");
continue;
}
switch(input.Substring(0,4).ToLower())
{
case USERS:
if(input.Length == 4)
{
PrintfUsers();
}
else
{
Console.WriteLine("'" +input+"'" +"不是有效命令!/n");
}
break;
case EXIT:
listenThr.Abort();
byte[] data = Encoding.Unicode.GetBytes(SERVCLS);
for(int i=0; i<users.Count; i++)
{
sock.SendTo(data, ((User)users[i]).Location);
}
sock.Close();
loop = false;
break;
default:
Console.WriteLine("'" +input+"'" +"不是有效命令!/n");
break;
}
}
}
}
用空再解释UDP打洞是怎么回事,代码写的很乱,请大家见谅!
这个试验我做了很多次都没成功,不知道是不是代码哪里错了~~~有空得检查下。
该博客给出了C#实现客户端与服务器通信的代码。定义了用户类,包含用户名和网络地址属性。客户端和服务器端代码中设置了多种通信命令,如登录、下线、请求用户列表等,还涉及UDP打洞相关操作,但作者表示试验多次未成功,需检查代码。
7547

被折叠的 条评论
为什么被折叠?



