准多线程TCP服务器,处理客户端连接请求。

    上TCP服务器课的时候,老师讲了关于服务器设计的思想,用来处理多个客户端连接,

基本上也就是两种。

(1)每当有一个客户端连接请求的时候,服务器检测到连接请求,就创建一个工作线程

用来处理这个工作请求,这样的好处处理速度比较快,当客户端请求比较少的时候,性能非常优越,这种方式也是

阻塞的方式,只不过是每一个线程都在阻塞。缺点是:如果开启的线程太多,那么系统资源就不够用,处理速度就会变慢。


(2)采用I/O多路复用技术,也就是非阻塞的方式,这种方式是事件驱动方式也是回掉的方式,典型的就是

ACE中的 reactor模式,通过注册回掉事件,避免了系统阻塞。


之前在网上翻了很多文章,关于第一种模式,实际上说来一个连接请求,新建一个线程的方式,并不知道具体的实现细节。

就是说,监听到了一个连接请求,这个是怎么实现的?

后来询问同学才知道,按照如下方法:

1.首先建立一个套接字socket,这个socket是一个对象,只是一个数据类型,而不是通过一个函数返回的形式

2.将该套接字绑定到本机的端口,ip地址

3.使用listen函数监听这个socekt,

然后:

while(1)

{

          sConns = accept(sServSock,(struct sockaddr *)&client,&nAddrLen); //在while(1)循环里面使用listen插卡是否有连接请求

                         //如果有连接请求,那么listen函数返回的将是一个有效的的套接字,否则返回SOCKET_INVALID;表示无效套接字

                         //      这样就可以根据上面返回的套接字,新建一个处理线程,把申请到的套接字,当做参数传递给申请的线程

}

从上面中总结出的:

     (1)服务器首先需要创建一个socket,绑定端口和iP地址,并且进行监听,监听成功其实就说明一直在监听,不需要再重复监听

      (2)需要不断的通过while(1),来调用accept()函数,判断是否有连接,accept饭hiu的套接字,用于建立新线程。

下面是服务器端的代码:

/****************************************************

【服务端】基于TCP,多线程的聊天框架代码
评注:非常完整
******************************************************/
#include "stdafx.h"
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")

/*****************定义客户端连接上后的聊天线程函数************/

DWORD WINAPI ClientThread1(LPVOID lpParam)
{
    SOCKET  sock = (SOCKET)lpParam;//定义连接客户端的套接字
    char  szBuff[2048];//定义接收缓冲区
    char  szMessage[2048];//定义发送的消息
    int  ret,
        nLeft,
        idx;//nLeft,idx用以控制写缓冲的数据,以保证数据写入正确
    //提示输入exit退出聊天
    puts("输入\"exit\"可退出聊天\n");
    //进入数据传输循环,即聊天
    //缺陷是只能一人一句来回对话
    while(1)
    {
        
        / 接收  /
        ret = recv(sock,szBuff,2048,0);
        if(ret == 0) 
            break;
        else if(ret == SOCKET_ERROR)
        {
            printf("recv() failed:%d\n",WSAGetLastError());
            break;
        }
        
        szBuff[ret] = '\0';
        //判断对方发过来的是否为exit退出命令,若是则退出聊天继续监听
        if(!strcmp(szBuff,"exit")) 
        {
            printf("对方已经停止聊天!\n");
            printf("服务器正在监听");
            break;
        }
        printf("客户:%s\n",szBuff);//在控制台打印客户的聊天语句
        // 回复  //
        printf("发送消息:"); 
        //服务器输入数据回答客户
        gets(szMessage);
        //若发送为空,则传送‘不说话’三字,并提示
        if(strlen(szMessage)==0)
        {
            printf("发送不能为空哦\n");
            strcpy(szMessage,"不说话!");
        }
        //传送数据
        nLeft = strlen(szMessage);
        idx = 0;
        //确保写进所有数据
        while(nLeft>0)
        {
            ret = send(sock,&szMessage[idx],nLeft,0);
            if(ret == 0)
                break;
            else if(ret == SOCKET_ERROR)
            {
                printf("send error!%d\n",WSAGetLastError());
                break;
            }
            nLeft-=ret;
            idx +=ret;
        }
        //判断szMessage是否为exit命令,若是则退出聊天继续监听
        if(!strcmp(szMessage,"exit")) 
        {
            printf("连接正在断开!\n");
            printf("服务器继续监听\n");
            break;
        }
    }
    return 0;
}


//主函数
int main(int argc, char* argv[])
{
    WSADATA  wsd;//定义WINSOCK32消息结构体
    SOCKET  sServSock;//服务器端的套接字

    SOCKET  MysConns[5];//服务器的各连接
	SOCKET  sConns;
    HANDLE  hThread;//定义处理客户连接的县城
    DWORD  dwThreadId;//定义线程ID
    char  szAddress[128];//监听的地址
    struct hostent *host = NULL;//定义本地地址指针
    sockaddr_in local,
                      client;//分别定义本地,客户端的地址结构
    int nSockErr;//定义出错信息
    printf("请你输入监听地址(格式如202.204.118.138):");
    gets(szAddress);
    int nAddrLen = sizeof(client);//得到地址结构长度
	//定义一个线程数组
    ClientListen[0]=(DWORD WINAPI )ClientThread1;
	ClientListen[1]=(DWORD WINAPI )ClientThread2;

    //初始化Winsock32库
    if(WSAStartup(MAKEWORD(2,2),&wsd) != 0)
    {
        printf("failed to load winsock!\n");
        return 1;
    }
    //建立socket对象
    sServSock = socket(AF_INET,SOCK_STREAM,IPPROTO_IP);//流套接字,面向连接
    
    //为socket分配端口地址监听
    local.sin_family = AF_INET;
    local.sin_port = htons(5150);//监听端口
    
    //若地址出错则监听本机地址
    if((local.sin_addr.s_addr = inet_addr(szAddress))
        ==INADDR_NONE)
    {
        puts("所输入的地址不正确,本服务将使用本机地址!");
        //得到主机名
        if(gethostname(szAddress,sizeof(szAddress))==SOCKET_ERROR)//得到本机的域名,名称
        {   
            puts("Can't getting local host name.");
            
        }
        //通过主机名得到主机IP地址
        host = gethostbyname(szAddress);//得到本地ip
        if(host)
            CopyMemory(&local.sin_addr,host->h_addr_list[0],
            host->h_length);
        else
        {
            printf("gethostbyname() failed:%d\n",WSAGetLastError());
            Sleep(5000);
            return 1;
        }
    }
    
    //将套接字绑定到本机地址local上
    if(bind(sServSock,(LPSOCKADDR)&local,sizeof(local))==SOCKET_ERROR)
    {
        nSockErr = WSAGetLastError();
        printf("bind error:%d!\n", nSockErr);
        return 1;
    }
    //监听客户连接请求
    if(listen(sServSock,5)==SOCKET_ERROR)
    {
        nSockErr =WSAGetLastError();
        printf("listen error:%d\n", nSockErr);
        return 1;
    }
    //提示状态
    printf("服务器启动成功!\n");
    printf("服务器正在监听\n");
	int i=0;
	HANDLE hthread[5];
    //进入处理连接循环
    while(1)
    {
        //若有客户连接,则接受连接
        sConns = accept(sServSock,(struct sockaddr *)&client,&nAddrLen);
        if(sConns == INVALID_SOCKET)
        {
            nSockErr = WSAGetLastError();
            printf("accept error %d\n",nSockErr);
            break;
        }
		MysConns[i]=sConns;
        //连接正确则提示可以开始聊天
        printf("%s:%d连接到了本服务,现在可以聊天了.\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
        //创建一个线程用于聊天
	
	
        hthread[i] = CreateThread(NULL,0,ClientThread1,    (LPVOID)MysConns[i],0,&dwThreadId);
		
        if(hthread[i] == NULL)
        {
            printf("CreateThread() failed %d\n",GetLastError());
            break;
        }
		i++;
        //聊天结束关闭聊天线程,继续监听
        CloseHandle(hthread[i-1]);
    }
    closesocket(sServSock);
    WSACleanup();
    return 0;
}







下面是客户端的代码:

/***********************************************************
客户端
************************************************************/
#include "stdafx.h"
#include <stdio.h>
#include <winsock2.h>
#pragma comment(lib,"ws2_32.lib")


int main(int argc,char **argv)
{
    WSADATA  wsd;//定义winsock32信息结构
    SOCKET  sClient;//定义本地套接字
    char  szBuffer[2048];//定义接受的缓冲
    char  szMessage[2048];//发送的消息
    char  szServer[128];//连接的服务器地址,IP地址
    int   ret;
    
    struct  sockaddr_in server;//定义连接的服务器地址
    struct  hostent    *host =NULL;//定义地址
    
    //提示输入连接的服务器地址
    printf("请输入连接的服务器IP地址(如:202.204.118.138):");
    gets(szServer);
    //初始winsock库
    if(WSAStartup(MAKEWORD(2,2),&wsd)!=0)
    {
        printf("Failed to load Winsock library!\n");Sleep(5000);
        return 1;
    }
    
    // strcpy(szMessage,"我是***");
    
    
    //建立socket对象
    sClient =  socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    if(sClient == INVALID_SOCKET)
    {
        printf("socket() failed :%d\n",WSAGetLastError());Sleep(5000);
        return 1;
    }
    
    //定义服务器地址以发送信息
    server.sin_family = AF_INET;
    server.sin_port = htons(5150);//端口
    
    server.sin_addr.s_addr = inet_addr(szServer);
    
    //若没有地址,则将地址设置为本机地址
    if(server.sin_addr.s_addr == INADDR_NONE)
    {
        puts("所输入服务器地址不正确,本将使用本机地址!");
        //得到主机名
        if(gethostname(szServer,sizeof(szServer))==SOCKET_ERROR)//得到本机的域名,名称
        {   
            puts("Can't getting local host name.");
            
        }
        //通过主机名得到IP
        host = gethostbyname(szServer);
        if(host == NULL)
        {
            printf("Unable to resolve server:%s\n",szServer);Sleep(5000);
            return 1;
        }
        CopyMemory(&server.sin_addr,host->h_addr_list[0],host->h_length);
    }
    //建立连接
    if(connect(sClient,(struct sockaddr *)&server,sizeof(server))==SOCKET_ERROR)
    {
        printf("connect() failed:%d\n",WSAGetLastError());
        Sleep(5000);
        return 1;
    }
    //提示当前状态
    puts("连接成功,现在可以聊天了!\n");
    puts("输入\"exit\"可退出聊天\n");
    
    
    //进入聊天状态
    while(1)
    {
        发送
        printf("发送消息:");
        //写入发送信息
        gets(szMessage);
        if(strlen(szMessage)==0)
        {
            printf("发送不能为空哦\n");
            strcpy(szMessage,"不说话!");
        }
        //发送信息
        ret = send(sClient,szMessage,strlen(szMessage),0);
        if(ret == 0)
        {
        }
        else if(ret == SOCKET_ERROR)
        {
            printf("send() failed: %d\n",WSAGetLastError());
            Sleep(5000);
        }
        //判断输入信息是否为exit命令,若是则退出
        if(!strcmp(szMessage,"exit"))
        {
            printf("你已经退出了聊天!");
            break;
        }
        // printf("send %d byte\n",ret);
        
        /接收回复的信息
        ret = recv(sClient,szBuffer,2048,0);
        if(ret == 0) 
            ;
        else if(ret == SOCKET_ERROR)
        {
            printf("recv()failed:%d\n",WSAGetLastError());
            
        }
        
        //设置接收得到的字符串,并打印
        szBuffer[ret] = '\0';
        if(!strcmp(szBuffer,"exit"))
        {
            printf("服务器已经停止聊天!");
            break;
        }
        printf("服务器:%s\n",szBuffer);
    } 
    
    //关闭套接字
    closesocket(sClient);
    //清空winsock环境
    WSACleanup();
    return 0;
    
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值