简单的TCP服务器(含客户端)

简单的TCP服务器(含客户端),第1张

该服务器完成将客户端发送的内容转换成大写,并发送到客户端(注意:服务器只支持一个客户端连接,后面我们会使用多进程,多线程,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

}

首先需要在电脑上搭建服务器,关于网上好多按步骤安装apache,sql,phpmyadmin的方法复杂而且没有必要,这里推荐wamp这样的一个集成软件,安装非常简单,一路点击next,apache+sql+phpmyadmin的环境一键搭好。在百度中搜索Wamp下载,直接下载就行。下载完wamp进行安装,一路点击next,装在c:\wamp下,最后install完成.在浏览器输入localhost,出现以下页面说明安装成功了。现在服务器和php环境算是搭好了。此时在桌面右下角任务栏中会有wamp的图标,点击会有下图显示,www是网站页面根目录,localhost是网站默认主页,点击phpmyadmin可以进入数据库管理界面.现在网站只能在PC上显示,如何能把网站放到互联网上让朋友也欣赏一下。这时就需要一款叫花生壳的软件,提供动态的域名映射.下载网址如下:http://hsk.oray.com/download/#type=windows|lan下载完软件按步骤安装即可,这时候需要注册一下花生壳会员,注册时按网页提示的步骤即可,用身份证注册可以获得一个免费域名。用注册的账号密码登陆软件。会出现以下界面。这里的网址是申请的免费域名。在域名上右键,选择新花生壳管理,填写应用名称,这个可以随便填,内网主机为本机内网的ip地址,这时需要查看以下,win+R在cmd中输入,ipconfig/all,可以看到ipv4地址,填写即可。端口为80端口。同时,现在需要设置一下路由器。在浏览器输入192.168.1.1。输入路由器用户名和密码,一般都为admin。进入路由器界面。需要设置三个地方,首先是动态DNS,选择服务商为花生壳,填写用户名密码,验证保存。然后是转发规则中的虚拟服务器,添加新条目,端口80,ip地址为内网ip。最后是,DMZ主机,设置开启,ip为内网本机ip。现在设置基本完成,这时就可以测试了。注意,要把防火墙关闭,否则外网还是访问不了本机的网站。在控制面板中把防火墙关闭。现在可以写一段html代码,命名为index.html(注意此时先把index.php放到别的文件夹)放入wamp\www目录中,来测试访问。最后,在浏览器输入花生壳域名,看看是不是就能访问了。同时也可以把自域名发送给朋友,让他们帮测试一下。

用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))

}

}


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

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

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

发表评论

登录后才能评论

评论列表(0条)

    保存