C# Socket TCP 编程,客户端与服务端连接,发送字符串,文件
来源:互联网 发布:如何快速学完网络课程 编辑:程序博客网 时间:2024/06/05 10:44
做了个winform的Socket服务端和客户端,能实现发送消息&文字
目前客户端还有几个bug(一些异常没处理好).客户端在接受一个文件之后才能正常接受文字消息,找了很久也不知道怎么改
以下是图片和一些关键点,最后是代码,注释不多,请多多指教
这部分是服务端
服务端创建一个socket对象
第一个参数 :寻址方案,ip版本4 第二个参数:套接字类型,字节流 第三个参数:协议,TCP
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
这时候可以在外边创建new一个空的socket对象比如serverSocket.来保存上面创建的对象,这样其他地方就能使用这个socket
例外,在外部创建一个List<Socket> list 来保存连客户端socket对象.后面要用
socket需要ip地址和port,注意参数转换类型
socket.Bind(new IPEndPoint(IPAddress.Parse(txtIP.Text.ToString()), int.Parse(txtPort.Text)));
设置连接等待队列的数量
socket.Listen(10);
开始服务,等待客户端连接,这时候需要开另外一个线程 来做这些事情,AcceptClientConnect是自己写的一个方法,将socket传给它
ThreadPool.QueueUserWorkItem(new WaitCallback(this.AcceptClientConnect), socket);
下面是AcceptClientConnect的代码, 当有客户端连接时,创建一个Socket对象.变量名为proxSocket,并且保存到list中,方便以后使用,另外,需要在创建一个线程来处理该客户端socket发送过来的数据.
public void AcceptClientConnect(object socket) { var serverSocket = socket as Socket; this.AppendTextToTxtLog("服务器端开始接受客户端的链接"); while (true) { try { var proxSocket = serverSocket.Accept(); this.AppendTextToTxtLog(string.Format("客户端{0}连接上了", proxSocket.RemoteEndPoint.ToString())); ClientProxClentList.Add(proxSocket); //接受消息 ThreadPool.QueueUserWorkItem(new WaitCallback(this.ReceiveData), proxSocket); } catch (Exception) { } } }
其中AppendTextToTxtLog方法是给主窗口中传递文本信息,因为txtLog这个控件不是监听服务端连接的线程创建的.所以要用异步来让它把新消息复制到txtLog中,代码中if来判断是否是txtLog当前线程的控件
public void AppendTextToTxtLog(string txt) { if (txtLog.InvokeRequired) { txtLog.BeginInvoke(new Action<string>(s => { this.txtLog.Text = string.Format("{0}\r\n{1}", s, txtLog.Text); }), txt); } else { this.txtLog.Text = string.Format("{0}\r\n{1}", txt, txtLog.Text); } }下面是处理客户端发送过来的数据的代码,new一个byte[]数组,变量名为data,代销为1024*1024,用来保存客户端发来的数据. int readlen是实际接收到的数据的长度(data中没用的到部分会自动为null),服务端和客户端通信的时候,数据长度是大于0的.当数据长度等于0 的时候,说明客户端要断开连接,代码中有这个判断,还有try,catch捕获异常(这里处理的不怎么好),最后.当复合条件.发送过来的数据转换成字符串,这里用到了有效长度,截取,转换为字符串,添加到消息文本框中.
public void ReceiveData(object obj) { Socket proxSocket = obj as Socket; byte[] data = new byte[1024 * 1024]; while (true) { int readLen = 0; try { readLen = proxSocket.Receive(data, 0, data.Length, SocketFlags.None); } catch (Exception ex) { //异常退出时 AppendTextToTxtLog(string.Format("客户端{0}非正常退出", proxSocket.RemoteEndPoint.ToString())); ClientProxClentList.Remove(proxSocket); StopConnetct(proxSocket); return; } if (readLen<=0) { //客户端正常退出 AppendTextToTxtLog(string.Format("客户端:{0}正常退出", proxSocket.RemoteEndPoint.ToString())); ClientProxClentList.Remove(proxSocket); StopConnetct(proxSocket); return;//方法结束->终结当前接受客户端数据的异步线程 } string txt = Encoding.Default.GetString(data, 0, readLen); AppendTextToTxtLog(string.Format("接收到客户端{0}的消息{1}",proxSocket.RemoteEndPoint.ToString(),txt)); } }一下是发送消息的代码 ,这个客户端能发送字符串,文件和窗口晃动,在原本发送的byte[] data前面在增加一位,为1 的时候是发送字符串,2发送晃动.3发送文件,data数组前面加一位,new用一个新数组result,长度是data.length+1,之后用到了Buffer.BlockCopy()方法,参数分别是(原数组,原数组的开始位置,目标数组,目标数组的开始位置,原数组中取多长数据)
发送数据,客户端对象.Send(要发送的byte数组,开始位置,长度,发送接收行为),一般发送接收行为用SocketFlags.None就可以了.发送的时候遍历一下list中的客户端对象.判断一下是否连接.
private void btnSend_Click(object sender, EventArgs e) { foreach (var proxSocket in ClientProxClentList) { if (proxSocket.Connected) { //原始的字符串转换成的字节数组 byte[] data = Encoding.Default.GetBytes(txtSendMsg.Text); //在头部加上标记字节 byte[] result = new byte[data.Length + 1]; //头部协议字节 1:代表字符串 result[0] = 1; Buffer.BlockCopy(data, 0, result, 1, data.Length); proxSocket.Send(result, 0, result.Length,SocketFlags.None); } } }发送文件的方法,new一个新的byte数组,第一位设置为3,然后读取文件,转换为byte[]data,还是用Buffer.BlockCopy(),整合到变量名为result的byte数组中,遍历list中的客户端对象,如果是连接的,那么发送
private void button2_Click(object sender, EventArgs e) { using (OpenFileDialog ofd =new OpenFileDialog()) { if (ofd.ShowDialog()!=DialogResult.OK) { return; } byte[] data = File.ReadAllBytes(ofd.FileName); byte[] result = new byte[data.Length + 1]; result[0] = 3; Buffer.BlockCopy(data, 0, result, 1,data.Length); foreach (var proxSocket in ClientProxClentList) { if (!proxSocket.Connected) { continue; } proxSocket.Send(result, SocketFlags.None); } } }最后一个是晃动的,只要发一个第一位是2的byte数组过去就好
private void button1_Click(object sender, EventArgs e) { foreach (var proxSocket in ClientProxClentList) { if (proxSocket.Connected) { proxSocket.Send(new byte[] { 2 }, SocketFlags.None); } } }
下面是服务端的整体代码
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.IO;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 ChatDemo{ public partial class Mainfrm : Form { public Mainfrm() { InitializeComponent(); } private Socket serverSocket; List<Socket> ClientProxClentList = new List<Socket>(); private bool state = false; private void btnStart_Click(object sender, EventArgs e) { if (!state) { Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); serverSocket = socket; //ip port socket.Bind(new IPEndPoint(IPAddress.Parse(txtIP.Text.ToString()), int.Parse(txtPort.Text))); //listen socket.Listen(10);//连接等待队列 ThreadPool.QueueUserWorkItem(new WaitCallback(this.AcceptClientConnect), socket); state = true; btnStart.Text = "停止服务"; } else { try { serverSocket.Close(); serverSocket.Shutdown(SocketShutdown.Both); } catch (Exception) { state = false; btnStart.Text = "开始服务"; } } //对象 } //日志文本框追加数据 public void AppendTextToTxtLog(string txt) { if (txtLog.InvokeRequired) { txtLog.BeginInvoke(new Action<string>(s => { this.txtLog.Text = string.Format("{0}\r\n{1}", s, txtLog.Text); }), txt); } else { this.txtLog.Text = string.Format("{0}\r\n{1}", txt, txtLog.Text); } } public void AcceptClientConnect(object socket) { var serverSocket = socket as Socket; this.AppendTextToTxtLog("服务器端开始接受客户端的链接"); while (true) { try { var proxSocket = serverSocket.Accept(); this.AppendTextToTxtLog(string.Format("客户端{0}连接上了", proxSocket.RemoteEndPoint.ToString())); ClientProxClentList.Add(proxSocket); //接受消息 ThreadPool.QueueUserWorkItem(new WaitCallback(this.ReceiveData), proxSocket); } catch (Exception) { } } } public void ReceiveData(object obj) { Socket proxSocket = obj as Socket; byte[] data = new byte[1024 * 1024]; while (true) { int readLen = 0; try { readLen = proxSocket.Receive(data, 0, data.Length, SocketFlags.None); } catch (Exception ex) { //异常退出时 AppendTextToTxtLog(string.Format("客户端{0}非正常退出", proxSocket.RemoteEndPoint.ToString())); ClientProxClentList.Remove(proxSocket); StopConnetct(proxSocket); return; } if (readLen<=0) { //客户端正常退出 AppendTextToTxtLog(string.Format("客户端:{0}正常退出", proxSocket.RemoteEndPoint.ToString())); ClientProxClentList.Remove(proxSocket); StopConnetct(proxSocket); return;//方法结束->终结当前接受客户端数据的异步线程 } string txt = Encoding.Default.GetString(data, 0, readLen); AppendTextToTxtLog(string.Format("接收到客户端{0}的消息{1}",proxSocket.RemoteEndPoint.ToString(),txt)); } } private void btnSend_Click(object sender, EventArgs e) { foreach (var proxSocket in ClientProxClentList) { if (proxSocket.Connected) { //原始的字符串转换成的字节数组 byte[] data = Encoding.Default.GetBytes(txtSendMsg.Text); //在头部加上标记字节 byte[] result = new byte[data.Length + 1]; //头部协议字节 1:代表字符串 result[0] = 1; Buffer.BlockCopy(data, 0, result, 1, data.Length); proxSocket.Send(result, 0, result.Length,SocketFlags.None); } } } private void StopConnetct(Socket proxSocket) { try { if (proxSocket.Connected) { proxSocket.Shutdown(SocketShutdown.Both); proxSocket.Close(100); } } catch (Exception ex) { } } private void button2_Click(object sender, EventArgs e) { using (OpenFileDialog ofd =new OpenFileDialog()) { if (ofd.ShowDialog()!=DialogResult.OK) { return; } byte[] data = File.ReadAllBytes(ofd.FileName); byte[] result = new byte[data.Length + 1]; result[0] = 3; Buffer.BlockCopy(data, 0, result, 1,data.Length); foreach (var proxSocket in ClientProxClentList) { if (!proxSocket.Connected) { continue; } proxSocket.Send(result, SocketFlags.None); } } } private void button1_Click(object sender, EventArgs e) { foreach (var proxSocket in ClientProxClentList) { if (proxSocket.Connected) { proxSocket.Send(new byte[] { 2 }, SocketFlags.None); } } } }}
现在开始客户端的代码
点击按钮连接Server,在外面创建一个socket对象名称为ClientSocket.方便其他地方调用,连接成功后new一个线程名称thread来处理接受到的消息,注意.thread设置为后台线程thread.IsBackground = true,线程开始的时候把当前的socket对象作为参数给它,thread.Start(ClientSocket);
private void btnConnect_Click(object sender, EventArgs e) { Socket socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); ClientSocket = socket; try { socket.Connect(IPAddress.Parse(txtIP.Text), int.Parse(txtPort.Text)); } catch (Exception ex) { MessageBox.Show("Bad"); return; } AppendTextToTxtLog(string.Format("服务端{0}已连接",ClientSocket.RemoteEndPoint.ToString())); Thread thread = new Thread(new ParameterizedThreadStart(ReceiveData)); thread.IsBackground = true; thread.Start(ClientSocket); }
消息接受方法ReceiveData(), socket对象.Receive(data, 0, data.Length, SocketFlags.None);返回值是实际接受的字节流长度,变量名readLen,后面要用.
public void ReceiveData(object obj) { Socket proxSocket = obj as Socket; byte[] data = new byte[1024 * 1024]; while (true) { int readLen = 0; try { readLen = proxSocket.Receive(data, 0, data.Length, SocketFlags.None); } catch (Exception) { //异常退出时 AppendTextToTxtLog(string.Format("服务端{0}非正常退出", proxSocket.RemoteEndPoint.ToString())); StopConnetct(); return; } if (readLen <= 0) { //客户端正常退出 AppendTextToTxtLog(string.Format("服务端:{0}正常退出", proxSocket.RemoteEndPoint.ToString())); StopConnetct(); return;//方法结束->终结当前接受客户端数据的异步线程 } //接受的数据中第一个字节如果是1,那么是字符串.2是闪屏.3是文件 if (data[0]==1) { string strMsg = ProcessRecieveString(data, readLen); AppendTextToTxtLog(string.Format("接收到服务端{0}的消息{1}", proxSocket.RemoteEndPoint.ToString(), strMsg)); } else if (data[0] == 2) { ProcessRecieveShake(); } else if (data[0]==3) { ProcessRecieveFile(data, readLen); } } }
下面是对字节流首位分别是1,2,3的处理方法
public string ProcessRecieveString(byte[] data,int readLen) { string str = Encoding.Default.GetString(data, 1, readLen); return str; } public void ProcessRecieveShake() { Point oldLocation = this.Location; Random r = new Random(); if (this.InvokeRequired) { txtLog.BeginInvoke(new Action<Point,Random>(Shake),oldLocation,r); } else { Shake(oldLocation, r); } } private void Shake(Point oldLocation, Random r) { for (int i = 0; i < 50; i++) { this.Location = new Point(r.Next(oldLocation.X - 5, oldLocation.X + 5), r.Next(oldLocation.Y - 5, oldLocation.Y + 5)); Thread.Sleep(50); this.Location = oldLocation; } } public void ProcessRecieveFile(byte[] data,int readLen) { using (SaveFileDialog sfd = new SaveFileDialog()) { sfd.DefaultExt = "txt"; sfd.Filter = "文本文件(*.txt)|*.txt|所有文件(*.*)|*.*"; if (sfd.ShowDialog(this) != DialogResult.OK) { return; } byte[] fileData = new byte[readLen - 1]; Buffer.BlockCopy(data, 1, fileData, 0, readLen - 1); File.WriteAllBytes(sfd.FileName, fileData); AppendTextToTxtLog(string.Format("接收到文件,已保存到{0}", sfd.FileName)); } }下面是发送消息的代码(和服务端的基本差不多)
private void btnSend_Click(object sender, EventArgs e) { // AppendTextToTxtLog("123"); if (ClientSocket==null) { return; } if (ClientSocket.Connected) { byte[] data = Encoding.Default.GetBytes(txtSendMsg.Text); ClientSocket.Send(data, 0, data.Length, SocketFlags.None); } }
下面是客户端的整体代码
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.IO;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 SocketClient{ public partial class Mianfrm : Form { public Mianfrm() { InitializeComponent(); //Control.CheckForIllegalCrossThreadCalls = false; } public Socket ClientSocket { get; set; } private void btnConnect_Click(object sender, EventArgs e) { Socket socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); ClientSocket = socket; try { socket.Connect(IPAddress.Parse(txtIP.Text), int.Parse(txtPort.Text)); } catch (Exception ex) { MessageBox.Show("Bad"); return; } AppendTextToTxtLog(string.Format("服务端{0}已连接",ClientSocket.RemoteEndPoint.ToString())); Thread thread = new Thread(new ParameterizedThreadStart(ReceiveData)); thread.IsBackground = true; thread.Start(ClientSocket); } public void ReceiveData(object obj) { Socket proxSocket = obj as Socket; byte[] data = new byte[1024 * 1024]; while (true) { int readLen = 0; try { readLen = proxSocket.Receive(data, 0, data.Length, SocketFlags.None); } catch (Exception) { //异常退出时 AppendTextToTxtLog(string.Format("服务端{0}非正常退出", proxSocket.RemoteEndPoint.ToString())); StopConnetct(); return; } if (readLen <= 0) { //客户端正常退出 AppendTextToTxtLog(string.Format("服务端:{0}正常退出", proxSocket.RemoteEndPoint.ToString())); StopConnetct(); return;//方法结束->终结当前接受客户端数据的异步线程 } //接受的数据中第一个字节如果是1,那么是字符串.2是闪屏.3是文件 if (data[0]==1) { string strMsg = ProcessRecieveString(data, readLen); AppendTextToTxtLog(string.Format("接收到服务端{0}的消息{1}", proxSocket.RemoteEndPoint.ToString(), strMsg)); } else if (data[0] == 2) { ProcessRecieveShake(); } else if (data[0]==3) { ProcessRecieveFile(data, readLen); } } } public string ProcessRecieveString(byte[] data,int readLen) { string str = Encoding.Default.GetString(data, 1, readLen); return str; } public void ProcessRecieveShake() { Point oldLocation = this.Location; Random r = new Random(); if (this.InvokeRequired) { txtLog.BeginInvoke(new Action<Point,Random>(Shake),oldLocation,r); } else { Shake(oldLocation, r); } } private void Shake(Point oldLocation, Random r) { for (int i = 0; i < 50; i++) { this.Location = new Point(r.Next(oldLocation.X - 5, oldLocation.X + 5), r.Next(oldLocation.Y - 5, oldLocation.Y + 5)); Thread.Sleep(50); this.Location = oldLocation; } } public void ProcessRecieveFile(byte[] data,int readLen) { using (SaveFileDialog sfd = new SaveFileDialog()) { sfd.DefaultExt = "txt"; sfd.Filter = "文本文件(*.txt)|*.txt|所有文件(*.*)|*.*"; if (sfd.ShowDialog(this) != DialogResult.OK) { return; } byte[] fileData = new byte[readLen - 1]; Buffer.BlockCopy(data, 1, fileData, 0, readLen - 1); File.WriteAllBytes(sfd.FileName, fileData); AppendTextToTxtLog(string.Format("接收到文件,已保存到{0}", sfd.FileName)); } } public void AppendTextToTxtLog(string txt) { if (txtLog.InvokeRequired) { txtLog.BeginInvoke(new Action<string>(s =>{this.txtLog.Text = String.Format("{0}\r\n{1}", s, txtLog.Text);}), txt); } else { this.txtLog.Text = string.Format("{0}\r\n{1}", txt, txtLog.Text); } } private void btnSend_Click(object sender, EventArgs e) { // AppendTextToTxtLog("123"); if (ClientSocket==null) { return; } if (ClientSocket.Connected) { byte[] data = Encoding.Default.GetBytes(txtSendMsg.Text); ClientSocket.Send(data, 0, data.Length, SocketFlags.None); } } private void StopConnetct() { try { if (ClientSocket.Connected) { ClientSocket.Shutdown(SocketShutdown.Both); ClientSocket.Close(100); } } catch (Exception ex) { } } private void Mianfrm_FormClosing(object sender, FormClosingEventArgs e) { StopConnetct(); } private void Mianfrm_Load(object sender, EventArgs e) { AppendTextToTxtLog("请单击连接到服务器"); } }}
最后是软件的界面,控件命名看名字基本就知道了,欢迎讨论
- C# Socket TCP 编程,客户端与服务端连接,发送字符串,文件
- C# Socket TCP 编程,客户端与服务端连接,发送字符串,文件
- socket编程--客户端发送文件至服务端
- socket 编程之TCP服务端与客户端
- C# Socket编程 服务端与客户端(一)
- C# Socket编程 服务端与客户端(二)
- C# Socket编程 服务端与客户端(四) 异步服务端
- windows Socket编程之TCP服务端与客户端
- Socket编程简单实现服务端客户端连接
- C# socket编程 异步服务端 同步客户端
- Socket TCP通信 客户端给服务端发送数据
- Python 服务端与客户端 TCP连接
- TCP-客户端连接服务端
- C# IP/TCP 客户端与服务端
- Socket通讯-C#客户端与Java服务端通讯(发送消息和文件)
- Untiy中用C#实现TCP通讯(Socket通讯)服务端与客户端皆可
- 【转】TCP Socket编程-客户端和服务端双向通信
- TCP服务端与客户端
- 【网络编程】利用流式套接字实现文件传输实验
- 如何实现基于微信小程序的人脸识别
- 回归评价指标
- swift3.0
- 自主学习报告第二周S_Part
- C# Socket TCP 编程,客户端与服务端连接,发送字符串,文件
- Redis高可用(sentinel机制)--Windows下实现
- java 出力log到linux下面的系统log
- 正则表达式在Java和JS中的应用
- 判断一棵二叉树是否平衡
- MySQL数据库liunx 下的自动备份
- eclipse 代码格式自定义
- Android 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
- 如何锻造出让用户欲罢不能的鸦片式文章?