前言

项目中要实现一个简单的socket服务器端,采用了TcpListener这个类。除了基本的功能之外,有几处需要注意的点。

  1. 要能同时接收多个客户端的连接,当然,不需要几千个那么多。
  2. 要能探测到客户端的断开。
  3. 要能关闭服务器端的监听。

这几个点之间,2和3我没有找到很好的方法,是通过捕获异常的方法解决的。

重点功能

要能同时接收多个客户端的连接

MSDN上面的代码例子是连接一个客户端的情况,我需要可以连接多个客户端,采用了多线程的方式,即连接一个客户端之后,把处理客户端消息的部分用一个线程处理,这样可以继续新的监听,核心代码如下:

 private void ThreadListen()
{
while (this.StatusOn)
{
try
{
TcpClient client = this.serverListener.AcceptTcpClient();
this.clientList.Add(client);
this.ShowMsg(string.Format("客户端连接成功! ip = {0} port = {1}", ((IPEndPoint)client.Client.RemoteEndPoint).Address, ((IPEndPoint)client.Client.RemoteEndPoint).Port));
Thread t1 = new Thread(() => ThreadHandleMsg(client));
t1.Start();
}
catch (Exception ex)
{
Log.Error("", ex);
}
}
}

要能探测到客户端的断开

在如下代码之处,while循环的条件会阻塞掉,等待客户端的输入(i = stream.Read(bytes, 0, bytes.Length)) != 0,这个不知道如何判断客户端断开,所以用了try catch比较low的办法。

Byte[] bytes = new Byte[1024];
String data = null;
NetworkStream stream = client.GetStream();
int i;
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
if (client == null || !client.Connected)
{
break;
}
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
this.ShowMsg(string.Format("收到消息: {0}", data));
data = data.ToUpper();
byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);
stream.Write(msg, 0, msg.Length);
this.ShowMsg(string.Format("返回消息: {0}", data));
}

要能关闭服务器端的监听

另外也没有发现如何关闭服务器端的监听,我看了一下stackoverflow,也没发现特别好的办法。代码如下:

public void Close()
{
this.StatusOn = false;
foreach (var client in this.clientList)
{
if (client != null && client.Connected)
{
client.Close();
}
}
this.clientList.Clear();
this.serverListener.Server.Close();
this.serverListener.Stop();
this.ShowMsg("停止监视,关闭连接");
}

全部代码

using log4net;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Text;
using System.Threading;
namespace srtc_attools.Bll
{
public class TcpServer
{
private static TcpServer inst;
private static readonly ILog Log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private List<TcpClient> clientList;
private TcpListener serverListener;
public Action<string> ShowMsg { get; set; }
public bool StatusOn { get; set; } private TcpServer() { }
public static TcpServer GetInst()
{
if (inst == null)
{
inst = new TcpServer();
}
return inst;
}
private Thread threadListen;
public void Open(string ip, int port)
{
this.clientList = new List<TcpClient>();
this.StatusOn = true;
this.ShowMsg("开始监视,等待连接");
this.serverListener = new TcpListener(IPAddress.Parse(ip), port);
this.serverListener.Start();
threadListen = new Thread(() => ThreadListen());
threadListen.Start();
} private void ThreadListen()
{
while (this.StatusOn)
{
try
{
TcpClient client = this.serverListener.AcceptTcpClient();
this.clientList.Add(client);
this.ShowMsg(string.Format("客户端连接成功! ip = {0} port = {1}", ((IPEndPoint)client.Client.RemoteEndPoint).Address, ((IPEndPoint)client.Client.RemoteEndPoint).Port));
Thread t1 = new Thread(() => ThreadHandleMsg(client));
t1.Start();
}
catch (Exception ex)
{
Log.Error("", ex);
}
}
}
private void ThreadHandleMsg(TcpClient client)
{
if (client.Connected)
{
try
{
Byte[] bytes = new Byte[1024];
String data = null;
NetworkStream stream = client.GetStream();
int i;
while ((i = stream.Read(bytes, 0, bytes.Length)) != 0)
{
if (client == null || !client.Connected)
{
break;
}
data = System.Text.Encoding.ASCII.GetString(bytes, 0, i);
this.ShowMsg(string.Format("收到消息: {0}", data));
data = data.ToUpper();
byte[] msg = System.Text.Encoding.ASCII.GetBytes(data);
stream.Write(msg, 0, msg.Length);
this.ShowMsg(string.Format("返回消息: {0}", data));
}
}
catch (Exception ex)
{
Log.Error("客户端发生错误\r\n", ex);
if(this.clientList.Contains(client))
{
this.clientList.Remove(client);
this.ShowMsg(string.Format("客户端断开连接! ip = {0} port = {1}", ((IPEndPoint)client.Client.RemoteEndPoint).Address, ((IPEndPoint)client.Client.RemoteEndPoint).Port));
}
}
}
}
public void Close()
{
this.StatusOn = false;
foreach (var client in this.clientList)
{
if (client != null && client.Connected)
{
client.Close();
}
}
this.clientList.Clear();
this.serverListener.Server.Close();
this.serverListener.Stop();
this.ShowMsg("停止监视,关闭连接");
}
}
}

应用TcpListener实现的socket服务器端的更多相关文章

  1. C# Socket服务器端如何判断客户端断开

    使用Socket类中的Poll方法,就可以. Socket client //假如已经创建好了,连接到服务器端得Socket的客户端对象. 我们只要client.Poll(10,SelectMode. ...

  2. C# Socket服务器端如何判断客户端断开求解

    Socket client //假如已经创建好了,连接到服务器端得Socket的客户端对象. 我们只要client.Poll(10,SelectMode.SelectRead)判断就行了.只要返回Tr ...

  3. linux下一对多socket服务器端多线程泄露问题

    线程创建多了,没有释放.导致内存泄露... int main() { int len; int on=1; // pMachList = CreateEmptyLinklist(); DataBase ...

  4. Socket编程——客户端,服务器端的读写操作

    URL网络编程,最大的特征就是一对一的响应! 1:客户端“写”,服务器端用于“读” package coreBookSocket2; import java.io.InputStreamReader; ...

  5. 【socket】一分钟理清 socket udpsocket tcpsocket tcplistener TCPClient和 UDPClient

    socket 套接字接口是各种语言tcp udp的网络操作的基础. 直接用socket 对象开发 可以选择 udpsocket  或者 tcpsocket ,两者在使用上仅一些方法和参数不同,所有的底 ...

  6. Python socket 客户端和服务器端

    connection, address = socket.accept() 调 用accept方法时,socket会时入“waiting”状态.客户请求连接时,方法建立连接并返回服务器.accept方 ...

  7. php socket客户端及服务器端应用实例

    经常有朋友会对php的socket应用充满疑惑,本文就以实例代码作一讲解,希望能对初学php的朋友起到一点帮助作用 具体代码如下: 1.服务器端代码: <?php class SocketSer ...

  8. Socket 进行发送

    最灵活的通信方式还是Socket ,TcpClient和Tcplistener只是对Socket进行了一些包装,从而使他们使用起来更简单一些 给出同步的服务器端 static void Main(st ...

  9. (45)C#网络3 socket

    一.TCP传输 using System.Net.Sockets; 1.最基本客户端连服务器 服务端运行后一直处于监听状态,客户端每启动一次服务端就接收一次连接并打印客户端的ip地址和端口号.(服务端 ...

随机推荐

  1. You can't specify target table 'charge' for update in FROM clause

    mysql中不能这么用. (等待mysql升级吧)错误提示就是说,不能先select出同一表中的某些值,再update这个表(在同一语句中) 替 换方 案: create table tmp as s ...

  2. R语言实现数据集某一列的频数统计——with和table

    with(priority.train, table(From.EMail)) 统计priority.train中From.EMail的频数

  3. poj 2479 (DP)

    求一个区间内连续两段不相交区间最大和. // File Name: 2479.cpp // Author: Missa_Chen // Created Time: 2013年06月22日 星期六 16 ...

  4. XML中对特殊字符的处置

    str = str.replaceAll("‘", "‘"); str = str.replaceAll("’", "‘" ...

  5. CI在CentOS中的部署与实践LNMP

    1. 平台:lnmp CentOS6.4 (64bit) nginx1.2.4+php5.5.7配置过程中遇到的问题与处理方式: 1. 404错误: 原因:nginx中的配置请求路径的问题 2. 40 ...

  6. SAS Config文件 处理流程

    Processing Options Specified by Additional CONFIG Options You can also specify additional –CONFIG op ...

  7. errno 与 perror()/strerror()函数

    errno是个全局的int型变量,当调用一个系统函数时不管成功不成功都有可能改变errno的值.但只有不成功时errno的值才是有意义的.如果要真的想用errno进行多次打印,刚可以先将其缓存一下.下 ...

  8. [转]glew, glee与 gl glu glut glx glext的区别和关系

    原文地址:http://blog.csdn.net/delacroix_xu/article/details/5881942 因为也是初接触,所以就当了解,等深入学习后再回顾这篇文章观点. GLEW是 ...

  9. 【转】Bootloader之uBoot简介(转)

    原文网址:http://blog.csdn.net/sadamoo/article/details/8139946 来自http://blog.ednchina.com/hhuwxf/1915416/ ...

  10. redo和undo的区别

    转摘:http://blog.163.com/jing_playboy/blog/static/757362222012520104521864/   redo--> undo-->dat ...