Socket网络编程学习一
这些天开始学习研究网络编程的东西,发现学习也是种乐趣,在不断的学习和探索中你能找到自己曾经缺乏的和新知识~
在学习这些内容的时候主要还是看了博客园里和一些有通俗易懂讲解的还蛮不错的文章,当然有的概念由于以前没有仔细深入接触那一块,所以还有待进一步深入学习~
感谢这些大牛博主分享的自己学习的经验和博文~
C#网络编程基础概念与基本的客户端服务器端通信,一步步按照他所讲解的东西,稍微修改了下里面原来有的东西,弄下面这个简单的客户端服务器端通信
博文地址如下:http://www.tracefact.net/CSharp-Programming/Network-Programming-Part1.aspx
还有另一个博客园中的地址 http://www.cnblogs.com/zhili/category/397082.html
处理消息类RequestHandler
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; namespace DavidProject.Common { public class RequestHandler { private string temp = string.Empty; public string[] GetActualString(string input) { return GetActualString(input, null); } public string[] GetActualString(string input, List<string> outputList) { if (outputList == null) outputList = new List<string>(); if (!string.IsNullOrEmpty(temp)) input = temp + input; string output = string.Empty; string pattern = @"(?<=^\[length=)(\d+)(?=\])"; int length; if (Regex.IsMatch(input, pattern)) { Match m = Regex.Match(input, pattern); length = Convert.ToInt32(m.Groups[0].Value); int startIndex = input.IndexOf("]") + 1; output = input.Substring(startIndex); if (output.Length == length) { // 如果output的长度与消息字符串的应有长度相等 // 说明刚好是完整的一条信息 outputList.Add(output); temp = string.Empty; } else if (output.Length < length) { // 如果之后的长度小于应有的长度, // 说明没有发完整,则应将整条信息,包括元数据,全部缓存 // 与下一条数据合并起来再进行处理 temp = input; // 此时程序应该退出,因为需要等待下一条数据到来才能继续处理 } else if (output.Length > length) { // 如果之后的长度大于应有的长度, // 说明消息发完整了,但是有多余的数据 // 多余的数据可能是截断消息,也可能是多条完整消息 output = output.Substring(0, length); outputList.Add(output); temp = string.Empty; input = input.Substring(startIndex + length); GetActualString(input, outputList); } } else { temp = input; } return outputList.ToArray(); } public static void Test() { RequestHandler handler = new RequestHandler(); string input; //第一种情况测试,一条完整消息发送 input = "[length=13]明天中秋,祝大家节日快乐!"; handler.PrintOutput(input); //第二种情况测试,两条完整消息一次发送 input = "明天中秋,祝大家节日快乐!"; input = string.Format("[length=13]{0}[length=13]{0}", input); handler.PrintOutput(input); //第三种情况测试,两条条不完整消息发送A input = "[length=13]明天中秋,祝大家节日快乐![length=13]明天中秋,"; handler.PrintOutput(input); input = "祝大家节日快乐!"; handler.PrintOutput(input); //第四种情况测试,两条条不完整消息发送B input = "[length=13]明天中秋,"; handler.PrintOutput(input); input = "祝大家节日快乐![length=13]明天中秋,祝大家节日快乐!"; handler.PrintOutput(input); //第五种情况,元数据不完整即自定义协议头不完整 input = "[leng"; handler.PrintOutput(input); //不输出 input = "th=13]明天中秋,祝大家节日快乐!"; handler.PrintOutput(input); } // 用于测试输出 private void PrintOutput(string input) { Console.WriteLine(input); string[] outputArray = GetActualString(input); foreach (string output in outputArray) { Console.WriteLine(output); } Console.WriteLine(); } } }
Server端
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; using System.Net; using DavidProject.Common; namespace DavidAdvancedServer { class Program { static void Main(string[] args) { //服务器启动监听 IPAddress serverIP = new IPAddress(new byte[] { 127, 0, 0, 1 }); TcpListener listener = new TcpListener(serverIP, 9999); Console.WriteLine("Server is running..."); listener.Start(); Console.WriteLine("Starting listening..."); while (true) { TcpClient client = listener.AcceptTcpClient(); ServerClient wrapper = new ServerClient(client); } } } public class ServerClient { #region 私有变量 private const int BufferSize = 8096; private TcpClient serverClient; private IList<TcpClient> serverClientLs; private string Msg = "Hello world, david test!"; private string Head_Msg = string.Empty; private byte[] buffer; private NetworkStream streamToClient; #endregion #region 构造方法 public ServerClient(TcpClient serverClient) { this.serverClient = serverClient; serverClientLs = new List<TcpClient>(); string.Format("[length={0}]{1}", Msg.Length, Msg); //显示当前连接到服务器上的客户端地址IP与端口 Console.WriteLine("Client Connected! {0}->{1}", serverClient.Client.LocalEndPoint, serverClient.Client.RemoteEndPoint); streamToClient = serverClient.GetStream(); buffer = new byte[BufferSize]; //构造函数中设置一个异步委托 AsyncCallback callBack = new AsyncCallback(ReadComplete); //开始一个异步调用 streamToClient.BeginRead(buffer, 0, BufferSize, callBack, null); } /// <summary> /// 异步调用结束后需要执行的回调函数 /// </summary> /// <param name="asynResult"></param> private void ReadComplete(IAsyncResult asynResult) { int bytesRead = 0; try { lock (streamToClient) { bytesRead = streamToClient.EndRead(asynResult); Console.WriteLine("From client->{0}, Read Data, {1} type...", this.serverClient.Client.RemoteEndPoint.ToString(),bytesRead); } if (bytesRead == 0) throw new Exception("Received 0 data!"); //注意点1 string receivedMsg = Encoding.Unicode.GetString(buffer, 0, bytesRead); Array.Clear(buffer, 0, buffer.Length); //处理请求过来的字符串因为有可能产生Helloworld!Helloworld!的情况,加入自定义的报文协议区别正确数据 string[] msgLs = new RequestHandler().GetActualString(receivedMsg); foreach (string msg in msgLs) { Console.WriteLine("From client->{0}, Received: {1}", this.serverClient.Client.RemoteEndPoint.ToString(), msg); string backStr = msg.ToUpper(); //注意点2,一定要重新new一个buffer用来写入流中 byte[] newBuffer = Encoding.Unicode.GetBytes(formatHeadStr(backStr)); streamToClient.Write(newBuffer, 0, newBuffer.Length); Console.WriteLine("Sent UpperString: {0}", backStr); streamToClient.Flush();//清除缓冲区上的所有数据 } lock (streamToClient) { //自己调用自己行程无限循环不需要在调用do-while,只要客户端一直有数据传送到服务器端则一直会运行这段 AsyncCallback callBack = new AsyncCallback(ReadComplete); streamToClient.BeginRead(buffer, 0, BufferSize, callBack, null); } } catch (Exception ex) { if (streamToClient != null) streamToClient.Dispose(); serverClient.Close(); Console.WriteLine(ex.Message);//捕获异常时退出 } } /// <summary> /// 得到带有头长度的文本 /// </summary> /// <param name="msg"></param> /// <returns></returns> private string formatHeadStr(string msg) { return string.Format("[length={0}]{1}", msg.Length, msg); } #endregion } }
Client端
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using System.Net.Sockets; using DavidProject.Common; using System.Threading; namespace DavidAdvancedClient { class Program { static void Main(string[] args) { ConsoleKey key; //运行3个客户端每个客户端进行发送3条消息 //for (int i = 0; i < 3; i++) //{ // Thread.Sleep(1000); // RemoteClient client = new RemoteClient(); // //client.SendMessage(); // client.SendMessageManually(); //} Thread.Sleep(1000); RemoteClient client = new RemoteClient(); //client.SendMessage(); client.SendMessageManually(); do { Console.WriteLine("按Q退出客户端"); key = Console.ReadKey(true).Key; } while (key != ConsoleKey.Q); } } public class RemoteClient { #region 私有变量 private const int BufferSize = 8096; private IPAddress serverIP = new IPAddress(new byte[] { 127, 0, 0, 1 }); private TcpClient client; private IList<TcpClient> clientLs; private string Msg = "Hello world, david test!"; private string Head_Msg = string.Empty; private byte[] buffer; private NetworkStream streamToServer; #endregion #region 构造方法 public RemoteClient() { try { Head_Msg = formatHeadStr(Msg); client = new TcpClient(); clientLs = new List<TcpClient>(); client.Connect(serverIP, 9999); streamToServer = client.GetStream(); } catch (Exception ex) { Console.WriteLine(!client.Connected ? string.Format("{0},stream流为null", ex.Message) : ex.Message); return; } buffer = new byte[BufferSize]; Console.WriteLine("Server Connected! {0}->{1}", client.Client.LocalEndPoint, client.Client.RemoteEndPoint); NetworkStream steamToServer = client.GetStream(); } /// <summary> /// 手动输入文本-按Q退出输入 /// </summary> public void SendMessageManually() { ConsoleKey enterKey; try { do { Console.WriteLine("请输入测试文本(按E退出输入):\n"); enterKey = Console.ReadKey(true).Key; string inputMsg = Console.ReadLine(); byte[] writeBytes = Encoding.Unicode.GetBytes(formatHeadStr(inputMsg)); lock (streamToServer) { streamToServer.Write(writeBytes, 0, writeBytes.Length); Console.WriteLine("Sent message \"{0}\" successfully!", inputMsg); } if (streamToServer != null) { lock (streamToServer) { AsyncCallback callback = new AsyncCallback(ReadComplete); streamToServer.BeginRead(buffer, 0, BufferSize, callback, null); } } } while (enterKey != ConsoleKey.E); } catch (Exception ex) { Console.WriteLine(!client.Connected ? string.Format("{0},stream流为null", ex.Message) : ex.Message); return; } } /// <summary> /// 测试方法 /// </summary> public void SendMessage() { SendMessage(this.Head_Msg); } /// <summary> /// 单个客户端发送3条消息 /// </summary> /// <param name="message"></param> public void SendMessage(string message) { for (int i = 0; i <= 2; i++) { byte[] temp = Encoding.Unicode.GetBytes(message); try { lock (streamToServer) { streamToServer.Write(temp, 0, temp.Length); Console.WriteLine("Sent: {0}", message); } } catch (Exception ex) { Console.WriteLine(!client.Connected ? string.Format("{0},stream流为null", ex.Message) : ex.Message); break; } } if (streamToServer != null) { lock (streamToServer) { AsyncCallback callback = new AsyncCallback(ReadComplete); streamToServer.BeginRead(buffer, 0, BufferSize, callback, null); } } } public void ReadComplete(IAsyncResult asynResult) { int bytesRead; try { lock (streamToServer) { bytesRead = streamToServer.EndRead(asynResult); } if (bytesRead == 0) throw new Exception("读取到0字节"); string result = Encoding.Unicode.GetString(buffer, 0, bytesRead); string[] msgLs = new RequestHandler().GetActualString(result); foreach (string msg in msgLs) { Console.WriteLine("Received: {0}", msg); } Array.Clear(buffer, 0, buffer.Length); lock (streamToServer) { AsyncCallback callback = new AsyncCallback(ReadComplete); streamToServer.BeginRead(buffer, 0, BufferSize, callback, null); } } catch (Exception ex) { if (streamToServer != null) streamToServer.Dispose(); client.Close(); Console.WriteLine(ex.Message); } } /// <summary> /// 得到带有头长度的文本 /// </summary> /// <param name="msg"></param> /// <returns></returns> private string formatHeadStr(string msg) { return string.Format("[length={0}]{1}", msg.Length, msg); } #endregion } }
完成的例子在资源中有下载 http://www.cnblogs.com/daviddai/admin/Files.aspx
示例效果图如下:
接下来会继续学习关于文件传输与把这写东西都应用到winform中做一个可视化类似QQ的建议对话程序,果然不断实践是学习新东西的最快途径~
如果大家有什么关于网络编程方面的好的网站博客或者书籍欢迎留在评论中~3Q~^0^
如果你觉得这篇文章对你有用,欢迎推荐[推荐]
如果你觉得文章内有错误欢迎指出^0^~
如果您想转载本博客,请注明出处
如果您对本文有意见或者建议,欢迎留言
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则作者保留追究法律责任的权利。