如何用C#实现多线程TCP协议的服务器端程序

如何用C#实现多线程TCP协议的服务器端程序,第1张

用C#实现多线程TCP协议的服务器端程序:

// <summary>

/// Tcp客户线程类(服务端),ThreadServerProcessor 线程产生的客户连接,用该线程读写

/// </summary>

public class ThreadClientProcessor

{

//Tcp连接实例

private TcpClient tcpClient

//消息框,本来想写日志用

private System.Windows.Forms.ListBox MessageList

private string Password//该连接登陆密码

private string Cmd1Echo

private string Cmd2Echo

private bool ClientLogOn//客户是否登陆

private bool TcpClose

public ThreadClientProcessor(){}

//构造函数,参数解释:Tcp客户,消息框,该服务密码(password命令后的参数) ,命令回应串 1,2 ******************

public ThreadClientProcessor(TcpClient client , ListBox listBox,string LogonText ,string cmd1echo,string cmd2echo)

{

ClientList.Add(this) //把当前实例加入一个列表中,方便以后控制

this.tcpClient=client

this.MessageList=listBox

this.Password=LogonText

this.Cmd1Echo=cmd1echo

this.Cmd2Echo=cmd2echo

this.ClientLogOn=false

this.TcpClose=false

}

public static char[] CmdSplit={' '}//读来的串由' ' 进行分离,命名+' '+参数

//public const string[] Cmd=new string[] { "password","cmd1","cmd2","echo","bye"}

//该函数由你自己写,这个只是给一个例子,

//功能:命令处理器,给个命令串,返回该命令处理结果,把命令和处理结果放在一个文本文件里,便于系统升级

public string TcpCmd(string s)

{

string result

try

{

string cmdarg=s.Trim()

string[] args=cmdarg.Split(CmdSplit)

string cmd=args[0].ToLower()

switch (cmd )

{

case "password" :

if (args.Length>1)

{

ClientLogOn= Password.Equals(args[1].Trim())

result=ClientLogOn? "登陆成功":"密码不正确,未登陆"

}

else result= "登陆时候,没有输入密码"

break

case "cmd1":

result=ClientLogOn?this.Cmd1Echo:"该命令无权执行,请先登陆"

break

case "cmd2":

result=ClientLogOn?this.Cmd2Echo:"该命令无权执行,请先登陆"

break

case "echo":

result=string.Format("服务器回应:\n {0}",s)

break

case "bye":

this.TcpClose=true

result="DisConnected"

break

default:

result="不可识别的命令"

break

}

}

catch

{

result="解析命令发生错误,你输入的是狗屁命令,TMD *^* "

}

return result

} //end cmd

//定义一个线程,该线程对应的函数是 void start()(不是Start())********************************

//一下程序主要是操作该线程

public System.Threading.Thread tcpClientThread

//启动客户连接线程 *************************************************************

public void Start()

{

tcpClientThread=new Thread(new ThreadStart(start))

tcpClientThread.Priority=ThreadPriority.BelowNormal

tcpClientThread.Start()

}

//断开该当前实例连接,终止线程 **************************************************************

public void Abort()

{

if (this.tcpClientThread!=null)

{

//tcpClientThread.Interrupt()

tcpClientThread.Abort()

//一定要等一会儿,以为后边tcpClient.Close()时候,会影响NetWorkStream的操作

Thread.Sleep(TimeSpan.FromMilliseconds(100))

tcpClient.Close()

}

}

//静态列表,包含了每个连接实例(在构造实例时候使用了 ArrayList.Add( object))

private static System.Collections.ArrayList ClientList=new ArrayList()

//断开所有的Tcp客户连接,静态方法*************************************************************

public static void AbortAllClient()

{

for(int j=0 j<ClientList.Countj++)

{

//从实例列表中取一个对象,转化为ThreadClientProcessor对象

ThreadClientProcessor o=(ThreadClientProcessor ) ClientList[j]

//调用ThreadClientProcessor 对象的停止方法

o.Abort()

}

//清除连接列表

ClientList.Clear()

}

//读写连接的函数,用于线程//*******************************************************************

private void start()

{

byte[] buf=new byte[1024*1024]//预先定义1MB的缓冲

int Len=0//流的实际长度

NetworkStream networkStream=tcpClient.GetStream()//建立读写Tcp的流

try

{

byte[] p=Encoding.UTF8.GetBytes(" 欢迎光临,请输入密码" )

//向Tcp连接写 欢迎消息

if (!this.ClientLogOn )

networkStream.Write(p,0,p.Length)

//开始循环读写tcp流

while (!TcpClose)

{

//如果当前线程是在其它状态,(等待挂起,等待终止.....)就结束该循环

if (Thread.CurrentThread.ThreadState!=ThreadState.Running)

break

//判断Tcp流是否有可读的东西

if ( networkStream.DataAvailable)

{

//从流中读取缓冲字节数组

Len=networkStream.Read(buf,0,buf.Length)

//转化缓冲数组为串

string cmd=Encoding.UTF8.GetString(buf,0,Len)

this.MessageList.Items.Add("客户机:"+cmd)

//处理该缓冲的串(分析命令),分析结果为res串

string res=TcpCmd(cmd)

//把命令的返回结果res 转化为字节数组

byte[] result=Encoding.UTF8.GetBytes(res)

//发送结果缓冲数组给客户端

networkStream.Write(result,0,result.Length)

this.MessageList.Items.Add("服务器回应:"+res)

}

else

{

//Thread.Sleep(TimeSpan.FromMilliseconds(200d))

//this.MessageList.Items.Add("客户机无命令")

//如果当前Tcp连接空闲,客户端没有写入,则当前线程停止200毫秒

Thread.Sleep(TimeSpan.FromMilliseconds(200d))

}

}

1.建立一个服务器socket并开始监听.

2.使用accept()方法取得新的连接.

3.建立输入和输出流.

4.在已有的协议上产生会话.

5.关闭客户端流和socket.

6.回到第二步或者到第七步.

7.关闭服务器socket.

该服务器完成将客户端发送的内容转换成大写,并发送到客户端(注意:服务器只支持一个客户端连接,后面我们会使用多进程,多线程,select,poll,epoll来完善我们的服务器,使之支持多个客户端连接)

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <errno.h>

#include <ctype.h>

#define SERV_PORT 7000

//running command: "./server_tcp -p 7001"

int main(int argc, char** argv)

{

        int opt

        int port = SERV_PORT

        while((opt = getopt(argc, argv, "p:")) != -1){

            switch (opt){

              case 'p': port = atoi(optarg)

                      break

              default: break

            }

        }

        struct sockaddr_in serv_addr, clnt_addr

        socklen_t clnt_len

        int lfd, cfd

        int n, i

        char buf[BUFSIZ]

        lfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)

        if(lfd <0){

                printf("%s\n", "socket error")

                exit(1)

        }

        serv_addr.sin_family = AF_INET

        serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1")

        serv_addr.sin_port = htons(port)

        bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr))

        listen(lfd, 128)

        clnt_len = sizeof(serv_addr)

        cfd = accept(lfd, (struct sockaddr*)&clnt_addr, &clnt_len)

        if(cfd == -1){

            printf("accept error\n")

            if(errno == EAGAIN || errno == EWOULDBLOCK){

                    printf("normal error")

            }else{

                printf("innormal error\n")

                return -1

            }

        }

        while(1){

          n = read(cfd, buf, BUFSIZ)

          if(n >0){

                printf("receive centent: %s\n", buf)

                for(i = 0i <ni++){

                        buf[i] = toupper(buf[i])

                }

          }

          write(cfd, buf, n)

        }

        close(cfd)

        close(lfd)

        return 0

}

客户端可以省略,使用nc命令可以模拟客户端连接: nc 127.1 7001

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <string.h>

int main()

{

struct sockaddr_in serv_addr

int lfd

lfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)

serv_addr.sin_family = AF_INET

serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1")//服务器和客户端IP相同

serv_addr.sin_port = htons(6666)

connect(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr))

write(lfd, "hello socket", strlen("hello socket"))

char rbuf[BUFSIZ]

read(lfd, rbuf, BUFSIZ)

printf("rbuf=%s\n", rbuf)

close(lfd)

return 0

}


欢迎分享,转载请注明来源:夏雨云

原文地址:https://www.xiayuyun.com/zonghe/194339.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-03-31
下一篇2023-03-31

发表评论

登录后才能评论

评论列表(0条)

    保存