c# Socket 线程 问题请教

衣舞晨风
博客专家认证
2014-03-17 10:38:06
最近在学习Secoket与线程,想做一个Form界面,客户端、服务端可以互发消息并显示,但遇到了这么一个问题,当启动服务端server.exe后发送消息,客户端可以接受到并显示,但客户端发送消息,服务端却不能显示。我单步调试,发现问题出在这里:
一开始启动server.exe的时候,服务端会停在vsClientSocket = vsServerSocket.Accept();这等待客户端接入,但但客户端发送消息过来后,服务端会往下执行但vsClientSocket.Receive(buffer, 0, bufLen, SocketFlags.None);函数中返回的bufLen长度为零,从而程序会再次到vsServerSocket.Accept();处进行等待。但再次从客户端发送信息的时候,服务端就没有反应了,单步调试,显示为跟按F10没有反应,也见不到光标在什么位置,但vs还是在debug模式。
所以,现在问题有下面两个:
1、bufLen长度为零,为什么会是0呢?
2、再次从客户端发送信息的时候,服务端就没有反应了,这是由什么原因造成的呢?
源代码如下:
客户端:
 public partial class Client : Form
{
Socket vsClientSocket;
Thread vsClientThread;
string strIP = "127.0.0.1";
public delegate void PassString(string strMsg);
int nPort = 9002;
public Client()
{
InitializeComponent();
}
public void SetSendData(string strMsg)
{
if (tBoxClientSend.InvokeRequired == true)
{
PassString d = new PassString(SetSendData);
this.Invoke(d, new object[] { strMsg });
}
else
{
tBoxClientSend.Text = strMsg;
}
}
public void SetRecvData(string strMsg)
{
if (tBoxClientReceive.InvokeRequired == true)
{
PassString d = new PassString(SetRecvData);
this.Invoke(d, new object[] { strMsg });
}
else
{
tBoxClientReceive.Text = strMsg;
}
}
private void ClientHandle()
{
string strRecv = string.Empty;
//IPEndPoint其实就是一个IP地址和端口的绑定,可以代表一个服务,用来Socket通讯。
//IPAddress类中有一个 Parse()方法,可以把点分的十进制IP表示转化成IPAddress类
IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(strIP), nPort);
//创建套接字实例
//这里创建的时候用ProtocolType.Tcp,表示建立一个面向连接(TCP)的Socket
vsClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
//将所创建的套接字与IPEndPoint绑定
vsClientSocket.Connect(ipep);
}
catch (SocketException ex)
{
MessageBox.Show("Connect Error: " + ex.Message);
return;
}
Byte[] buffer = new Byte[1024];
//循环监听
while (true)
{
//接收服务器信息
int bufLen = 0;
try
{
bufLen = vsClientSocket.Available;
vsClientSocket.Receive(buffer, 0, bufLen, SocketFlags.None);
if (bufLen == 0)
{
continue;
}
}
catch (Exception ex)
{
MessageBox.Show("Receive Error:" + ex.Message);
return;
}
strRecv = Encoding.ASCII.GetString(buffer, 0, bufLen);
SetRecvData(strRecv);
//vsClientSocket.Shutdown(SocketShutdown.Both);
//vsClientSocket.Close();
}
}

//发送数据
private void btnSend_Click(object sender, EventArgs e)
{
byte[] data = new byte[1024];
string ss = tBoxClientSend.Text;
data = Encoding.UTF8.GetBytes(ss);
vsClientSocket.Send(data, data.Length, SocketFlags.None);
}

private void Client_Load(object sender, EventArgs e)
{
//连接服务器
//通过ThreadStart委托告诉子线程讲执行什么方法
vsClientThread = new Thread(new ThreadStart(ClientHandle));
vsClientThread.Start();
}
private void KillProcess()
{
Process[] processes = Process.GetProcessesByName("SocketClient");
foreach (Process process in processes)
{
process.Kill();
break;
}
}

private void Client_FormClosing(object sender, FormClosingEventArgs e)
{
KillProcess();
}
}

服务端:
 public partial class Server : Form
{
Thread vsServerThread;
Socket vsServerSocket;
Socket vsClientSocket;
string strIP = "127.0.0.1";
public delegate void PassString(string strMsg);
public Server server;

int nPort = 9002;
public Server()
{
InitializeComponent();
}
private void SeverSendData(string strMsg)
{
//Control.InvokeRequired 属性,命名空间: System.Windows.Forms
//获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法,因为调用方位于创建控件所在的线程以外的线程中。
if (tBoxServerSend.InvokeRequired == true)
{
PassString d = new PassString(SeverSendData);
//Control.Invoke 方法 (Delegate)
//在拥有此控件的基础窗口句柄的线程上执行指定的委托。
this.Invoke(d, new object[] { strMsg });
}
else
{
tBoxServerSend.Text = strMsg;
}
}
private void SeverRecvData(string strMsg)
{
if (tBoxServerReceive.InvokeRequired == true)
{
PassString d = new PassString(SeverRecvData);
this.Invoke(d, new object[] { strMsg });
}
else
{
tBoxServerReceive.Text = strMsg;
}
}

private void ServerHandle()
{
string strRecv = string.Empty;
//创建IPEndPoint实例
//IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(strIP), nPort);
//IPAddress.Any可以用来表示本机上所有的IP地址,不用对每个IP进行侦听了
IPEndPoint ipep = new IPEndPoint(IPAddress.Any, nPort);
//创建一个套接字
vsServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//将所创建的套接字与IPEndPoint绑定
try
{
vsServerSocket.Bind(ipep);
}
catch (Exception ee)
{
MessageBox.Show("Bind出错:" + ee.ToString());
return;
}
//设置套接字为收听模式
vsServerSocket.Listen(10);
//循环监听
while (true)
{
try
{
//在套接字上接收接入的连接
vsClientSocket = vsServerSocket.Accept();
//获取连接上的远程主机的端口和IP地址
IPEndPoint clientep = (IPEndPoint)vsClientSocket.RemoteEndPoint;
Byte[] buffer = new Byte[1024];
//在套接字上接收客户端发送的信息
int bufLen = 0;
bufLen = vsClientSocket.Available;
vsClientSocket.Receive(buffer, 0, bufLen, SocketFlags.None);
if (bufLen == 0)
{
continue;
}
else
{
byte[] data = new byte[1024];
string ss = "Client:" + clientep.Address + "("+clientep.Port+")";
ss = ss + "所发信息已收到!";
data = Encoding.ASCII.GetBytes(ss);
//通过Socket发送信息,必须要先把发送的信息转化成二进字进行传输,
//收到信息后也要把收到的二进字信息转化成字符形式
vsClientSocket.Send(data, data.Length, SocketFlags.None);
//strRecv = Encoding.ASCII.GetString(buffer, 0, bufLen);
strRecv = Encoding.UTF8.GetString(buffer, 0, bufLen);
SeverRecvData(strRecv.ToString());
}
}
catch (Exception ex)
{
MessageBox.Show("Listening Error: " + ex.Message);
//vsClientSocket.Close();
//vsServerSocket.Close();
}
}
}
private void btnSend_Click(object sender, EventArgs e)
{
byte[] data = new byte[1024];
string ss = tBoxServerSend.Text;
data = Encoding.ASCII.GetBytes(ss);
vsClientSocket.Send(data, data.Length, SocketFlags.None);
}

private void Server_Load(object sender, EventArgs e)
{
//KillProcess();
vsServerThread = new Thread(new ThreadStart(ServerHandle));
vsServerThread.Start();
}

private void KillProcess()
{
Process[] processes = Process.GetProcessesByName("Server");
foreach (Process process in processes)
{
process.Kill();
break;
}
}

private void Server_FormClosing(object sender, FormClosingEventArgs e)
{
KillProcess();
}
}
...全文
297 18 打赏 收藏 转发到动态 举报
写回复
用AI写文章
18 条回复
切换为时间正序
请发表友善的回复…
发表回复
衣舞晨风 2014-03-19
  • 打赏
  • 举报
回复
引用 15 楼 xiaogui340 的回复:

        //
        // 摘要:
        //     使用指定的 System.Net.Sockets.SocketFlags,从绑定的 System.Net.Sockets.Socket 接收指定的字节数,存入接收缓冲区的指定偏移量位置。
        //
        // 参数:
        //   buffer:
        //     System.Byte 类型的数组,它是存储接收到的数据的位置。
        //
        //   offset:
        //     buffer 中存储所接收数据的位置。
        //
        //   size:
        //     要接收的字节数。
        //
        //   socketFlags:
        //     System.Net.Sockets.SocketFlags 值的按位组合。
        //
        // 返回结果:
        //     接收到的字节数。
        //
        // 异常:
        //   System.ArgumentNullException:
        //     buffer 为 null。
        //
        //   System.ArgumentOutOfRangeException:
        //     offset 小于 0。- 或 -offset 大于 buffer 的长度。- 或 -size 小于 0。- 或 -size 大于 buffer
        //     的长度减去 offset 参数的值。
        //
        //   System.Net.Sockets.SocketException:
        //     socketFlags 不是值的有效组合。- 或 -未设置 System.Net.Sockets.Socket.LocalEndPoint 属性。-
        //     或 -访问 System.Net.Sockets.Socket 时发生操作系统错误。
        //
        //   System.ObjectDisposedException:
        //     System.Net.Sockets.Socket 已关闭。
        //
        //   System.Security.SecurityException:
        //     调用堆栈中的调用方没有所需的权限。
        public int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags);
vsClientSocket.Receive(buffer, 0, bufLen, SocketFlags.None); 改为vsClientSocket.Receive(buffer, 0, buffer.Lenth, SocketFlags.None);
还是没有效果
衣舞晨风 2014-03-19
  • 打赏
  • 举报
回复
引用 14 楼 devilplus 的回复:
//在套接字上接收客户端发送的信息 bufLen = vsClientSocket.Available; try { vsClientSocket.Receive(buffer, 0, bufLen, SocketFlags.None); 改为 try { bufLen = vsClientSocket.Receive(buffer, 0, 1024, SocketFlags.None);
还是没有效果
xiaogui340 2014-03-19
  • 打赏
  • 举报
回复

        //
        // 摘要:
        //     使用指定的 System.Net.Sockets.SocketFlags,从绑定的 System.Net.Sockets.Socket 接收指定的字节数,存入接收缓冲区的指定偏移量位置。
        //
        // 参数:
        //   buffer:
        //     System.Byte 类型的数组,它是存储接收到的数据的位置。
        //
        //   offset:
        //     buffer 中存储所接收数据的位置。
        //
        //   size:
        //     要接收的字节数。
        //
        //   socketFlags:
        //     System.Net.Sockets.SocketFlags 值的按位组合。
        //
        // 返回结果:
        //     接收到的字节数。
        //
        // 异常:
        //   System.ArgumentNullException:
        //     buffer 为 null。
        //
        //   System.ArgumentOutOfRangeException:
        //     offset 小于 0。- 或 -offset 大于 buffer 的长度。- 或 -size 小于 0。- 或 -size 大于 buffer
        //     的长度减去 offset 参数的值。
        //
        //   System.Net.Sockets.SocketException:
        //     socketFlags 不是值的有效组合。- 或 -未设置 System.Net.Sockets.Socket.LocalEndPoint 属性。-
        //     或 -访问 System.Net.Sockets.Socket 时发生操作系统错误。
        //
        //   System.ObjectDisposedException:
        //     System.Net.Sockets.Socket 已关闭。
        //
        //   System.Security.SecurityException:
        //     调用堆栈中的调用方没有所需的权限。
        public int Receive(byte[] buffer, int offset, int size, SocketFlags socketFlags);
vsClientSocket.Receive(buffer, 0, bufLen, SocketFlags.None); 改为vsClientSocket.Receive(buffer, 0, buffer.Lenth, SocketFlags.None);
devilplus 2014-03-19
  • 打赏
  • 举报
回复
//在套接字上接收客户端发送的信息 bufLen = vsClientSocket.Available; try { vsClientSocket.Receive(buffer, 0, bufLen, SocketFlags.None); 改为 try { bufLen = vsClientSocket.Receive(buffer, 0, 1024, SocketFlags.None);
headgo 2014-03-17
  • 打赏
  • 举报
回复
思路有点问题,说一下服务器一般的处理思路: 侦听线程启动侦听,收到连接请求后Accept,然后启动另一个线程进行数据通讯处理,最好是两个线程,一个处理收,一个处理发,以免互相影响。如果是一问一答的通讯协议,用一个线程也可以。数据处理线程在socket异常后自动退出。 你的程序之所以会出现server在客户端发送第二次的时候服务器不会处理,是因为Accept和接收没有分开线程。客户端没有断开连接,可以继续发送数据,但服务器阻塞在Accept函数里,需要客户端发出新的连接请求才会退出,当然不会有反应。 至于Receive为何返回0,不知道,但应该是会的。出现返回为0,也不意味着socket出现问题,所以一般用异常判断socket是否故障。
衣舞晨风 2014-03-17
  • 打赏
  • 举报
回复
引用 1 楼 zhh0000zhh 的回复:
buflen=1024;//设置buffer长度为1024 buflen=vsClientSocket.Receive(buffer, 0, bufLen, SocketFlags.None);//返回实际获取到的字符串长度 详见http://technet.microsoft.com/zh-cn/library/w3xtz6a5(v=vs.90)
第一次buflen=0,第二次进入while还是会停在这里,然后就没反应了
//在套接字上接收接入的连接
                    vsClientSocket = vsServerSocket.Accept();
zhh0000zhh 2014-03-17
  • 打赏
  • 举报
回复
buflen=1024;//设置buffer长度为1024 buflen=vsClientSocket.Receive(buffer, 0, bufLen, SocketFlags.None);//返回实际获取到的字符串长度 详见http://technet.microsoft.com/zh-cn/library/w3xtz6a5(v=vs.90)
衣舞晨风 2014-03-17
  • 打赏
  • 举报
回复
引用 10 楼 headgo 的回复:
另外有时用console输出比打断点要好。
恩恩,我明天再调试一下
headgo 2014-03-17
  • 打赏
  • 举报
回复
另外有时用console输出比打断点要好。
headgo 2014-03-17
  • 打赏
  • 举报
回复
不要调试侦听线程,可以调试接收线程。
衣舞晨风 2014-03-17
  • 打赏
  • 举报
回复
引用 6 楼 headgo 的回复:
把你的程序稍微改了一下: private void ServerHandle() { string strRecv = string.Empty; //创建IPEndPoint实例 //IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(strIP), nPort); //IPAddress.Any可以用来表示本机上所有的IP地址,不用对每个IP进行侦听了 IPEndPoint ipep = new IPEndPoint(IPAddress.Any, nPort); //创建一个套接字 vsServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //将所创建的套接字与IPEndPoint绑定 try { vsServerSocket.Bind(ipep); } catch (Exception ee) { MessageBox.Show("Bind出错:" + ee.ToString()); return; } //设置套接字为收听模式 vsServerSocket.Listen(10); //循环监听 while (true) { try { vsClientSocket = vsServerSocket.Accept(); // 如果原来已经存在,杀死,保证只有一个接收线程。如果允许多个客户端,则不需要 if (vsRecvThread != null && vsRecvThread.IsAlive) { vsRecvThread.Abort(); } // 启动接收线程 vsRecvThread = new Thread(RecvData); } catch (Exception ex) { MessageBox.Show("Listening Error: " + ex.Message); //vsClientSocket.Close(); //vsServerSocket.Close(); } } } void RecvData() { //获取连接上的远程主机的端口和IP地址 IPEndPoint clientep = (IPEndPoint)vsClientSocket.RemoteEndPoint; Byte[] buffer = new Byte[1024]; int bufLen = 0; while (true) { //在套接字上接收客户端发送的信息 bufLen = vsClientSocket.Available; try { vsClientSocket.Receive(buffer, 0, bufLen, SocketFlags.None); if (bufLen <= 0) { continue; } else { byte[] data = new byte[1024]; string ss = "Client:" + clientep.Address + "(" + clientep.Port + ")"; ss = ss + "所发信息已收到!"; data = Encoding.ASCII.GetBytes(ss); //通过Socket发送信息,必须要先把发送的信息转化成二进字进行传输, //收到信息后也要把收到的二进字信息转化成字符形式 vsClientSocket.Send(data, data.Length, SocketFlags.None); //strRecv = Encoding.ASCII.GetString(buffer, 0, bufLen); strRecv = Encoding.UTF8.GetString(buffer, 0, bufLen); SeverRecvData(strRecv.ToString()); } } catch (System.Exception ex) {// 线程异常,退出 vsClientSocket.Close(); break; } } }
我把代码修改为下面这样:
 private void ServerHandle()
        {
            string strRecv = string.Empty;
            //创建IPEndPoint实例
            //IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(strIP), nPort);
            //IPAddress.Any可以用来表示本机上所有的IP地址,不用对每个IP进行侦听了
            IPEndPoint ipep = new IPEndPoint(IPAddress.Any, nPort);
            //创建一个套接字
            vsServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //将所创建的套接字与IPEndPoint绑定
            try
            {
                vsServerSocket.Bind(ipep);
            }
            catch (Exception ee)
            {
                MessageBox.Show("Bind出错:" + ee.ToString());
                return;
            }
            //设置套接字为收听模式
            vsServerSocket.Listen(10);
            //循环监听  
            while (true)
            {
                try
                {
                    vsClientSocket = vsServerSocket.Accept();
                    // 如果原来已经存在,杀死,保证只有一个接收线程。如果允许多个客户端,则不需要
                    if (vsRecvThread != null && vsRecvThread.IsAlive)
                    {
                        vsRecvThread.Abort();
                    }
                    // 启动接收线程
                    vsRecvThread = new Thread(RecvData);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Listening Error: " + ex.Message);
                    //vsClientSocket.Close();
                    //vsServerSocket.Close();
                }
            }
        }
        private void RecvData()
        {
            //获取连接上的远程主机的端口和IP地址
            IPEndPoint clientep = (IPEndPoint)vsClientSocket.RemoteEndPoint;
            Byte[] buffer = new Byte[1024];
            int bufLen = 0;
            string strRecv = string.Empty;
            while (true)
            {
                //在套接字上接收客户端发送的信息
                bufLen = vsClientSocket.Available;
                try
                {
                    vsClientSocket.Receive(buffer, 0, bufLen, SocketFlags.None);
                    if (bufLen <= 0)
                    {
                        continue;
                    }
                    else
                    {
                        byte[] data = new byte[1024];
                        string ss = "Client:" + clientep.Address + "(" + clientep.Port + ")";
                        ss = ss + "所发信息已收到!";
                        data = Encoding.ASCII.GetBytes(ss);
                        //通过Socket发送信息,必须要先把发送的信息转化成二进字进行传输,
                        //收到信息后也要把收到的二进字信息转化成字符形式
                        vsClientSocket.Send(data, data.Length, SocketFlags.None);                
                        strRecv = Encoding.UTF8.GetString(buffer, 0, bufLen);
                        SeverRecvData(strRecv.ToString());
                    }
                }
                catch (System.Exception ex)
                {// 线程异常,退出
                    MessageBox.Show(ex.ToString());
                    vsClientSocket.Close();
                    break;
                }
            }
        }
当点击debug后,光标会显示停在vsClientSocket = vsServerSocket.Accept();处。此时debug客户端,客户端会停在vsClientSocket.Receive(buffer, 0, bufLen, SocketFlags.None);处, 然后再Client界面上输入要发送的内容,点击发送,此时再看server端的代码已经没有光标,无法调试。
headgo 2014-03-17
  • 打赏
  • 举报
回复
把你的程序稍微改了一下: private void ServerHandle() { string strRecv = string.Empty; //创建IPEndPoint实例 //IPEndPoint ipep = new IPEndPoint(IPAddress.Parse(strIP), nPort); //IPAddress.Any可以用来表示本机上所有的IP地址,不用对每个IP进行侦听了 IPEndPoint ipep = new IPEndPoint(IPAddress.Any, nPort); //创建一个套接字 vsServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //将所创建的套接字与IPEndPoint绑定 try { vsServerSocket.Bind(ipep); } catch (Exception ee) { MessageBox.Show("Bind出错:" + ee.ToString()); return; } //设置套接字为收听模式 vsServerSocket.Listen(10); //循环监听 while (true) { try { vsClientSocket = vsServerSocket.Accept(); // 如果原来已经存在,杀死,保证只有一个接收线程。如果允许多个客户端,则不需要 if (vsRecvThread != null && vsRecvThread.IsAlive) { vsRecvThread.Abort(); } // 启动接收线程 vsRecvThread = new Thread(RecvData); } catch (Exception ex) { MessageBox.Show("Listening Error: " + ex.Message); //vsClientSocket.Close(); //vsServerSocket.Close(); } } } void RecvData() { //获取连接上的远程主机的端口和IP地址 IPEndPoint clientep = (IPEndPoint)vsClientSocket.RemoteEndPoint; Byte[] buffer = new Byte[1024]; int bufLen = 0; while (true) { //在套接字上接收客户端发送的信息 bufLen = vsClientSocket.Available; try { vsClientSocket.Receive(buffer, 0, bufLen, SocketFlags.None); if (bufLen <= 0) { continue; } else { byte[] data = new byte[1024]; string ss = "Client:" + clientep.Address + "(" + clientep.Port + ")"; ss = ss + "所发信息已收到!"; data = Encoding.ASCII.GetBytes(ss); //通过Socket发送信息,必须要先把发送的信息转化成二进字进行传输, //收到信息后也要把收到的二进字信息转化成字符形式 vsClientSocket.Send(data, data.Length, SocketFlags.None); //strRecv = Encoding.ASCII.GetString(buffer, 0, bufLen); strRecv = Encoding.UTF8.GetString(buffer, 0, bufLen); SeverRecvData(strRecv.ToString()); } } catch (System.Exception ex) {// 线程异常,退出 vsClientSocket.Close(); break; } } }
衣舞晨风 2014-03-17
  • 打赏
  • 举报
回复
引用 4 楼 headgo 的回复:
思路有点问题,说一下服务器一般的处理思路: 侦听线程启动侦听,收到连接请求后Accept,然后启动另一个线程进行数据通讯处理,最好是两个线程,一个处理收,一个处理发,以免互相影响。如果是一问一答的通讯协议,用一个线程也可以。数据处理线程在socket异常后自动退出。 你的程序之所以会出现server在客户端发送第二次的时候服务器不会处理,是因为Accept和接收没有分开线程。客户端没有断开连接,可以继续发送数据,但服务器阻塞在Accept函数里,需要客户端发出新的连接请求才会退出,当然不会有反应。 至于Receive为何返回0,不知道,但应该是会的。出现返回为0,也不意味着socket出现问题,所以一般用异常判断socket是否故障。
能给点代码参考不,第一次写这东西,没啥思路

110,571

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧