长时间没有摸这两个协议,写个代码温习下

下面是界面

【服务器界面】

【登陆界面】

【好友列表界面(我登陆了2个)】

【聊天界面】

下面大致讲解下用到的内容

1、用户登陆于服务器通信用到的tcp协议,服务器接收到用户登陆信息(包括ip,端口,用户名等)后,返回已经登陆的用户列表信息(包括ip,端口,用户名等)给这个用户,同时服务器使用Udp协议向已经登陆的用户发送最新用户列表(包括ip,端口,用户名等)用于更新用户列表

2、用户登陆成功后展示好友列表,并启动udp协议的监听(叫监听似乎不太合适,暂且这么叫吧  形象),用以接收好友发来的消息和服务器返回的好友信息(1中提到的发送用户列表信息)

3、关于聊天有被动接收到消息和主动发送消息

先说主动发送消息吧:双击列表的某个好友打开聊天窗口,然后发送内容,通过udp协议向好友发送信息

被动接收消息:当2中提到的udp监听器接收到消息,则打开聊天窗口,并显示信息

4、用户退出时想服务器发送数据退出,用到的tcp协议,服务器接到到信息,更新在线用户列表并向其他用户发送用户最新列表进行更新(用到udp协议)

口才不行,写的有点乱

下面上代码解释下

先来服务器代码,服务器我使用了控制台程序

 using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.IO; namespace QQServer
{
class Program
{
public static List<string> userlist = new List<string>();
static TcpListener tl;
static NetworkStream ns;
static void Main(string[] args)
{
//声明监听对象 //声明网络流 //IPEndPoint ip=new IPEndPoint(
tl = new TcpListener();
tl.Start();
Console.WriteLine("TcpListener Star");
//开启线程
Thread th = new Thread(new ThreadStart(listen));
th.IsBackground = true;
th.Start();
while (true)
{
string index = Console.ReadLine();
if (index == "exit")
break; } }
private static void listen()
{
Console.WriteLine("TcpListenering...");
while (true)
{
//获得响应的Socket
Socket sock = tl.AcceptSocket();
//通过该Socket实例化网络流
ns = new NetworkStream(sock);
//ClientTcp是添加的类,下面会做说明
ClientTcp ct = new ClientTcp(ns);
//ct_MyEvent方法注册ClientTcp类的MyEvent事件
ct.MyEvent += new MyDelegate(ct_MyEvent);
//开启线程
Thread th = new Thread(new ThreadStart(ct.TcpThread));
th.IsBackground = true;
th.Start();
}
} static void ct_MyEvent(string temp)
{
if (temp.StartsWith("login:"))
{
temp = temp.Replace("login:", "");
Console.WriteLine("UserLogin:" + temp);
string[] index = temp.Split(';');
if (!ContainsList(index[]))
{
userlist.Add(temp);
}
SendUsersToUser(index[]);
}
else if (temp.StartsWith("out:"))
{
temp = temp.Replace("out:", "");
Console.WriteLine("UserLoginOut:" + temp);
if (ContainsList(temp))
{
RemoveList(temp);
}
SendUsersToUser(temp);
}
} static void SendUsersToUser(string outuser)
{
string message = GetUserStr();
UdpClient uc;
foreach (string s in userlist)
{
string[] _userstrindex=s.Split(';');
if (_userstrindex[] == outuser)
continue;
string _ipsindex = _userstrindex[];
string[] _ipindex = _ipsindex.Split(':');
byte[] b = System.Text.Encoding.UTF8.GetBytes("users" + message);
//向本机的8888端口发送数据
uc = new UdpClient();
uc.Send(b, b.Length, _ipindex[], int.Parse(_ipindex[]));
}
} static string GetUserStr()
{
StringBuilder sb = new StringBuilder();
foreach (string s in userlist)
{
if (sb.Length > )
sb.Append("#");
sb.Append(s);
}
return sb.ToString();
} static bool ContainsList(string str)
{
foreach (string s in userlist)
{
if (s.Split(';')[] == str)
{
return true;
}
}
return false;
} static void RemoveList(string str)
{
for (int i = userlist.Count - ; i >= ; i--)
{
string s = userlist[i];
if (s.Split(';')[] == str)
{
userlist.Remove(s);
}
}
}
} public delegate void MyDelegate(string temp);
class ClientTcp
{
//设置网络流局部对象
private NetworkStream ns;
//声明类型为MyDelegate的事件MyEvent
public event MyDelegate MyEvent;
//构造函数中接收参数以初始化
public ClientTcp(NetworkStream ns)
{
this.ns = ns;
}
//服务器端线程所调用的方法
public void TcpThread()
{
//获得相关的封装流
StreamReader sr = new StreamReader(ns);
string temp = sr.ReadLine();
//接收到客户端消息后触发事件将消息回传
if (!temp.StartsWith("getuser"))
{
MyEvent(temp);
}
StringBuilder sb = new StringBuilder();
foreach (string s in Program.userlist)
{
if (sb.Length > )
sb.Append("#");
sb.Append(s);
}
StreamWriter sw = new StreamWriter(ns);
//转换为大写后发送消息给客户端
sw.WriteLine(sb.ToString());
sw.Flush();
sw.Close();
sr.Close();
}
}
}

需要注意的地方:

tl = new TcpListener(12345);这个地方使用了固定端口12345,所有客户端跟服务器进行通信必须使用这个端口

Thread th = new Thread(new ThreadStart(ct.TcpThread));
th.IsBackground = true;
th.Start();

这个地方为什么使用一个线程呢???

当接收到一个信息后需要进行处理,如果同时有好多信息进来的话容易堵塞,所有用线程,并且接收到一个信息马上将信息放到 ClientTcp ct = new ClientTcp(ns);这里,然后慢慢进行处理吧

服务器接收到的消息有多种,怎么区分呢???

有登陆的信息,有退出的信息,有获取列表的信息,我们可以在发送的消息内用一些字段进行标记,例如在头部加上“getuser”等等的

=======================================================

下面是客户端的

登陆

 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.IO;
using System.Net; namespace QQClient
{
public partial class Login : Form
{
private TcpClient tc;
//声明网络流
private NetworkStream ns;
public Login()
{
InitializeComponent();
} private void button1_Click(object sender, EventArgs e)
{
string username = textBox1.Text;
string ipstr = textBox2.Text;
string poitstr = textBox3.Text; IPHostEntry ipe = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipa = null;
foreach (IPAddress ip in ipe.AddressList)
{
if (ip.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
continue;
ipa = ip;
break;
} StringBuilder sb = new StringBuilder();
sb.Append("login:");
sb.Append(username + ";");
sb.Append(ipa.ToString() + ":");
Random r = new Random();
int port = r.Next(, );
sb.Append(port.ToString()); try
{
tc = new TcpClient(ipstr, int.Parse(poitstr));
}
catch
{
MessageBox.Show("无法连接到主机");
}
//实例化网络流对象
ns = tc.GetStream();
StreamWriter sw = new StreamWriter(ns);
StreamReader sr = new StreamReader(ns);
//将TextBox1的值传给服务器端
sw.WriteLine(sb.ToString());
sw.Flush();
//接收服务器端回传的字符串
string users = sr.ReadLine(); sr.Close();
sw.Close(); Main main=new Main();
main.Username=username;
main.Users=users;
main.Port = port;
main.ThisIP = ipa.ToString();
main.ServerIP = textBox2.Text;
main.ServerPort = textBox3.Text;
this.Hide();
main.ShowDialog();
} private void button2_Click(object sender, EventArgs e)
{
Application.Exit();
}
}
}

列表界面

 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Net; namespace QQClient
{
public partial class Main : Form
{
public string Username { get; set; }
public string Users { get; set; }
public int Port { get; set; }
public string ServerIP;
public string ServerPort;
public string ThisIP { get; set; }
public static List<Talking> TalkList = new List<Talking>();
public List<User> userList = new List<User>();
public Main()
{
InitializeComponent();
} private void Main_Load(object sender, EventArgs e)
{
//Control.CheckForIllegalCrossThreadCalls = false;
this.Text = Username;
LoadUser();
StartListen();
} private void LoadUser()
{
if (string.IsNullOrEmpty(Users))
return;
this.listView1.Items.Clear();
userList.Clear();
string[] _userindex = Users.Split('#');
foreach (string s in _userindex)
{
string[] _index = s.Split(';');
string _username = _index[];
//string[] _ipinex = _index[1].Split(':');
//string ip = _ipinex[0];
//string port = _ipinex[1];
if (_username != Username)
{
//TreeNode tn = new TreeNode();
//tn.Text = _username;
//tn.Tag = _index[1];
//this.treeView1.Nodes.Add(tn); ListViewItem lvitem = new ListViewItem(); lvitem.ImageIndex = ;
lvitem.Text = _username;
lvitem.Tag = _index[];
this.listView1.Items.Add(lvitem);
userList.Add(new User() { UserName = _username, Ips = _index[] });
}
}
} private void button2_Click(object sender, EventArgs e)
{
Application.Exit();
} private void button1_Click(object sender, EventArgs e)
{
try
{
TcpClient tc = new TcpClient(ServerIP, int.Parse(ServerPort));
//实例化网络流对象
NetworkStream ns = tc.GetStream();
StreamWriter sw = new StreamWriter(ns);
StreamReader sr = new StreamReader(ns);
//将TextBox1的值传给服务器端
sw.WriteLine("getuser");
sw.Flush();
//接收服务器端回传的字符串
Users = sr.ReadLine();
sr.Close();
sw.Close();
LoadUser();
}
catch
{ }
} private void Main_FormClosed(object sender, FormClosedEventArgs e)
{
try
{
TcpClient tc = new TcpClient(ServerIP, int.Parse(ServerPort));
//实例化网络流对象
NetworkStream ns = tc.GetStream();
StreamWriter sw = new StreamWriter(ns);
//将TextBox1的值传给服务器端
sw.WriteLine("out:" + Username);
sw.Flush();
sw.Close();
iswork = false;
}
catch
{ }
Application.Exit();
} private void listView1_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (this.listView1.SelectedItems.Count > )
{
ListViewItem lvitem = this.listView1.SelectedItems[];
string toname = lvitem.Text;
string toips = lvitem.Tag.ToString();
Talking t = isHaveTalk(toname);
if (t != null)
{
t.Focus();
}
else
{
Talking talk = new Talking();
talk.UserName = Username;
talk.ToName = toname;
talk.ToIP = toips;
TalkList.Add(talk);
talk.Show();
}
}
} private Talking isHaveTalk(string toname)
{
foreach (Talking tk in TalkList)
{
if (tk.ToName == toname)
return tk;
}
return null;
} public static void RemoveTalking(Talking _talk)
{
foreach (Talking tk in TalkList)
{
if (tk.ToName == _talk.ToName)
{
TalkList.Remove(_talk);
return;
}
}
} bool iswork = false;
UdpClient uc = null;
private void StartListen()
{ iswork = true;
Thread th = new Thread(new ThreadStart(listen));
//设置为后台
th.IsBackground = true;
th.Start();
}
private void listen()
{
uc = new UdpClient(Port);
IPEndPoint iep = new IPEndPoint(IPAddress.Any, );
while (iswork)
{
//获得Form1发送过来的数据包
string text = System.Text.Encoding.UTF8.GetString(uc.Receive(ref iep));
if (text.StartsWith("message"))
{
text = text.Substring();
int indexof = text.IndexOf("#");
string fromuser = text.Substring(, indexof);
string message = text.Substring(indexof + );
Talking _tk = isHaveTalk(fromuser);
if (_tk != null)
{
this.BeginInvoke(new MethodInvoker(delegate()
{
_tk.Focus();
_tk.AddMessage(message, true);
}));
}
else
{
//Talking talk = new Talking(message);
//talk.UserName = Username;
//talk.ToName = fromuser;
//talk.ToIP = GetIP(fromuser);
//TalkList.Add(talk);
//talk.Show();
this.BeginInvoke(new MethodInvoker(delegate()
{
this.CreatTalking(text);
}));
//Thread th = new Thread(new ParameterizedThreadStart(CreatTalking));
//th.IsBackground = true;
//th.Start(text);
}
//加入ListBox
//this.listBox1.Items.Add(text);
}
else if (text.StartsWith("users"))
{
text = text.Substring();
Users = text;
LoadUser();
}
}
} public void CreatTalking(object _text)
{
string text = _text.ToString();
int indexof = text.IndexOf("#");
string fromuser = text.Substring(, indexof);
string message = text.Substring(indexof + );
Talking talk = new Talking(message);
talk.UserName = Username;
talk.ToName = fromuser;
talk.ToIP = GetIP(fromuser);
TalkList.Add(talk);
talk.Show();
} private string GetIP(string toname)
{
foreach (User user in userList)
{
if (user.UserName == toname)
return user.Ips;
}
return "";
}
}
public class User
{
private string userName; public string UserName
{
get { return userName; }
set { userName = value; }
}
private string ips; public string Ips
{
get { return ips; }
set { ips = value; }
}
}
}

聊天界面

 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Threading; namespace QQClient
{
public partial class Talking : Form
{
public string UserName { get; set; }
public string ToName { get; set; }
public string ToIP { get; set; } UdpClient uc;
public Talking()
{
InitializeComponent();
} string getmessage = string.Empty;
public Talking(string message)
{
getmessage = message;
InitializeComponent();
} private void Talking_Load(object sender, EventArgs e)
{
uc = new UdpClient();
this.Text = "和" + ToName + "聊天中";
if (!string.IsNullOrEmpty(getmessage))
{
ShowTalking();
AddMessage(getmessage, true);
}
} private void button1_Click(object sender, EventArgs e)
{
string temp = this.textBox1.Text; //保存TextBox文本
//将该文本转化为字节数组
byte[] b = System.Text.Encoding.UTF8.GetBytes("message" + UserName + "#" + temp);
//向本机的8888端口发送数据
string[] _ip = ToIP.Split(':');
uc.Send(b, b.Length, _ip[], int.Parse(_ip[]));
AddMessage(temp, false);
this.textBox1.Clear();
}
public void AddMessage(string str, bool isuser)
{
int startindex = this.richTextBox1.Text.Length; string message = string.Empty; if (isuser)
message = "【" + ToName + "】 " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\n" + str + "\n";
else
message = "【" + UserName + "】 " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "\n" + str + "\n";
this.richTextBox1.AppendText(message);
this.richTextBox1.Select(startindex, message.Length);
if (isuser)
{
this.richTextBox1.SelectionAlignment = HorizontalAlignment.Left;
}
else
{
this.richTextBox1.SelectionAlignment = HorizontalAlignment.Right;
}
this.richTextBox1.Select(this.richTextBox1.Text.Length, );
} [System.Runtime.InteropServices.DllImport("user32")]
private static extern long FlashWindow(IntPtr hwnd, bool bInvert); private static void FlashWindow(object _handle)
{
IntPtr handle = (IntPtr)_handle;
int flashindex = ;
while (true)
{
if (flashindex > )
break;
FlashWindow(handle, true);
flashindex++;
Thread.Sleep();
}
} public void ShowTalking()
{
Thread _thread = new Thread(FlashWindow);
_thread.IsBackground = true;
_thread.Start(this.Handle);
} private void Talking_FormClosed(object sender, FormClosedEventArgs e)
{
Main.RemoveTalking(this);
} private void button2_Click(object sender, EventArgs e)
{
this.Close();
}
}
}

大致总结下:

tcp必须建立连接才可以进行通信

udp不需要建立通信

但是两者都需要一个监听来接收消息

c# UDP/TCP协议简单实现(简单聊天工具)的更多相关文章

  1. C++开发的基于TCP协议的内网聊天工具

    项目相关地址 源码:https://github.com/easonjim/TCPChat bug提交:https://github.com/easonjim/TCPChat/issues

  2. Java基础之UDP协议和TCP协议简介及简单案例的实现

    写在前面的废话:马上要找工作了,做了一年的.net ,到要找工作了发现没几个大公司招聘.net工程师,真是坑爹呀.哎,java就java吧,咱从头开始学呗,啥也不说了,玩命撸吧,我真可怜啊. 摘要: ...

  3. 关于Http协议与TCP协议的一些简单理解

    TCP协议对应于传输层,而HTTP协议对应于应用层,从本质上来说,二者没有可比性.Http协议是建立在TCP协议基础之上的,当浏览器需要从服务器获取网页数据的时候,会发出一次Http请求.Http会通 ...

  4. 基于TCP 协议的socket 简单通信

    DNS 服务器:域名解析 socket 套接字 : ​ socket 是处于应用层与传输层之间的抽象层,也是一组操作起来非常简单的接口(接受数据),此接口接受数据之后,交由操作系统 为什么存在 soc ...

  5. 网络编程应用:基于TCP协议【实现一个聊天程序】

    要求: 基于TCP协议实现一个聊天程序,客户端发送一条数据,服务器端发送一条数据 客户端代码: package Homework1; import java.io.IOException; impor ...

  6. JS简单仿QQ聊天工具的制作

    刚接触JS,对其充满了好奇,利用刚学到的一点知识,写了一个简单的仿QQ聊天的东西,其中还有很多的不足之处,有待慢慢提高. 功能:1.在输入框中输入内容,点击发送,即可在上方显示所输入内容. 2.点击‘ ...

  7. 基于UDP/TCP协议的套接字

    1.UDP UDP的数据报协议特点是不粘包,非可靠传输 服务端 import socket server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) ...

  8. node实现简单的群体聊天工具

    一.使用的node模块 1.express当做服务器 2.socket.io 前后通信的桥梁 3.opn默认打开浏览器的模块(本质上用不到) 难点:前后通信 源码地址:https://github.c ...

  9. Python网络编程02 /基于TCP、UDP协议的socket简单的通信、字符串转bytes类型

    Python网络编程02 /基于TCP.UDP协议的socket简单的通信.字符串转bytes类型 目录 Python网络编程02 /基于TCP.UDP协议的socket简单的通信.字符串转bytes ...

随机推荐

  1. 经典算法题每日演练——第八题 AC自动机

    原文:经典算法题每日演练--第八题 AC自动机 上一篇我们说了单模式匹配算法KMP,现在我们有需求了,我要检查一篇文章中是否有某些敏感词,这其实就是多模式匹配的问题. 当然你也可以用KMP算法求出,那 ...

  2. HTML5分析实战Web存储机制(Web Storage)

    Web Storage它是Key-Value在持久性数据存储的形式.Web Storage为了克服cookie把所引起的一些限制.当数据需要严格格控制client准时,没有必要不断地发回数据serve ...

  3. Drop dual

    一些互联网用户删除dual表还有一个问题: 删除dual时间表hang直播,然后直接shutdown abort.话又说回来,当您启动数据库.发现open时间已经hang直播.但该数据库是真正开放的另 ...

  4. java 字符串反转

    描述:给我一个字符串,例如I love java,输出: java love I   方法一 public class StringReverse { public void swap(char[] ...

  5. iOS8自适应布局视频教程

    联系:http://www.elsyy.com/course/6480 这是颐和园最近录制iOS8视频课程.简介iOS8出现在自适应布局. 本教程的书,颐和园<ios8 swift编程指南> ...

  6. 拷贝构造函数,深拷贝,大约delete和default相关业务,explicit,给定初始类,构造函数和析构函数,成员函数和内联函数,关于记忆储存,默认参数,静态功能和正常功能,const功能,朋友

     1.拷贝构造 //拷贝构造的规则,有两种方式实现初始化. //1.一个是通过在后面:a(x),b(y)的方式实现初始化. //2.另外一种初始化的方式是直接在构造方法里面实现初始化. 案比例如以 ...

  7. 启用IIS7报错功能

    进入:控制面板 - 卸载程序 - 打开或关闭Windows功能 如果访问任何不存在页面或页面出错时空白: Internet 信息服务 - 万维网服务 - 常见 HTTP 功能 - HTTP 错误 打勾 ...

  8. c++中&amp;和&amp;&amp;有什么差别

    他们不同点在于&&相当一个开关语句,就是说假设&&前面值为false那么他就不继续运行后面的表达式:而&无论前面的值为什么,总是运行其后面的语句. &能 ...

  9. unity3d插件Daikon Forge GUI 中文教程-1-Daikon Forge介绍

    (游戏蛮牛首发)大家好我是孙广东官网提供了专业的视频教程http://www.daikonforge.com/dfgui/tutorials/,只是是在youtube上,要观看是须要FQ的. 只是教程 ...

  10. itext之pdf导出添加水印Java工具类

    import java.io.IOException; import com.itextpdf.text.Document; import com.itextpdf.text.DocumentExce ...