C# Socket TCP 编程,客户端与服务端连接,发送字符串,文件

标签:c# /socket /winform /发送文件 1690
做了个winform的Socket服务端和客户端,能实现发送消息&文字

以下是图片和一些关键点,最后是代码,注释不多

服务端
服务端创建一个socket对象
参数 :寻址方案,ip版本4 ;套接字类型,字节流 ;协议,TCP
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
这时候可以在外边创建new一个空的socket对象比如serverSocket.来保存上面创建的对象,这样其他地方就能使用这个socket
例外,在外部创建一个List 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发送过来的数据.
[csharp] view plain copy
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当前线程的控件
[csharp] view plain copy
public void AppendTextToTxtLog(string txt)
{
if (txtLog.InvokeRequired)
{
txtLog.BeginInvoke(new Action(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捕获异常(这里处理的不怎么好),最后.当复合条件.发送过来的数据转换成字符串,这里用到了有效长度,截取,转换为字符串,添加到消息文本框中.
[csharp] view plain copy
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中的客户端对象.判断一下是否连接.
[csharp] view plain copy
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中的客户端对象,如果是连接的,那么发送

[csharp] view plain copy
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数组过去就好
[csharp] view plain copy
private void button1_Click(object sender, EventArgs e)
{
foreach (var proxSocket in ClientProxClentList)
{
if (proxSocket.Connected)
{
proxSocket.Send(new byte[] { 2 }, SocketFlags.None);
}
}
}

下面是服务端的整体代码
[csharp] view plain copy
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 ClientProxClentList = new List();
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);
[csharp] view plain copy
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,后面要用.
[csharp] view plain copy
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的处理方法
[csharp] view plain copy
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));  
       }  
   }  

下面是发送消息的代码(和服务端的基本差不多)
[csharp] view plain copy
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);
}

   }  

下面是客户端的整体代码
[csharp] view plain copy
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("请单击连接到服务器");  
    }  
}  

}

最后是软件的界面,

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值