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

【服務(wù)端】基于TCP,多線程的聊天框架代碼
評注:非常完整
*****************************************************
*/

#include 
"stdafx.h"
#include 
<stdio.h>
#include 
<winsock2.h>
#pragma comment(lib,
"ws2_32.lib")

/*****************定義客戶端連接上后的聊天線程函數(shù)************/

DWORD WINAPI ClientThread(LPVOID lpParam)
{
    SOCKET  sock 
= (SOCKET)lpParam;//定義連接客戶端的套接字
    char  szBuff[2048];//定義接收緩沖區(qū)
    char  szMessage[2048];//定義發(fā)送的消息
    int  ret,
        nLeft,
        idx;
//nLeft,idx用以控制寫緩沖的數(shù)據(jù),以保證數(shù)據(jù)寫入正確
    
//提示輸入exit退出聊天
    puts("輸入\"exit\"可退出聊天\n");
    
//進入數(shù)據(jù)傳輸循環(huá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';
        
//判斷對方發(fā)過來的是否為exit退出命令,若是則退出聊天繼續(xù)監(jiān)聽
        if(!strcmp(szBuff,"exit")) 
        
{
            printf(
"對方已經(jīng)停止聊天!\n");
            printf(
"服務(wù)器正在監(jiān)聽");
            
break;
        }

        printf(
"客戶:%s\n",szBuff);//在控制臺打印客戶的聊天語句
        ////////////////////////// 回復  //////////////////
        printf("發(fā)送消息:"); 
        
//服務(wù)器輸入數(shù)據(jù)回答客戶
        gets(szMessage);
        
//若發(fā)送為空,則傳送‘不說話’三字,并提示
        if(strlen(szMessage)==0)
        
{
            printf(
"發(fā)送不能為空哦\n");
            strcpy(szMessage,
"不說話!");
        }

        
//傳送數(shù)據(jù)
        nLeft = strlen(szMessage);
        idx 
= 0;
        
//確保寫進所有數(shù)據(jù)
        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命令,若是則退出聊天繼續(xù)監(jiān)聽
        if(!strcmp(szMessage,"exit")) 
        
{
            printf(
"連接正在斷開!\n");
            printf(
"服務(wù)器繼續(xù)監(jiān)聽\n");
            
break;
        }

    }

    
return 0;
}


//主函數(shù)
int main(int argc, char* argv[])
{
    WSADATA  wsd;
//定義WINSOCK32消息結(jié)構(gòu)體
    SOCKET  sServSock;//服務(wù)器端的套接字
    SOCKET  sConns;//服務(wù)器的各連接
    HANDLE  hThread;//定義處理客戶連接的縣城
    DWORD  dwThreadId;//定義線程ID
    char  szAddress[128];//監(jiān)聽的地址
    struct hostent *host = NULL;//定義本地地址指針
    sockaddr_in local,
        client;
//分別定義本地,客戶端的地址結(jié)構(gòu)
    int nSockErr;//定義出錯信息
    printf("請你輸入監(jiān)聽地址(格式如202.204.118.138):");
    gets(szAddress);
    
int nAddrLen = sizeof(client);//得到地址結(jié)構(gòu)長度
    
    
    
//初始化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分配端口地址監(jiān)聽
    local.sin_family = AF_INET;
    local.sin_port 
= htons(5150);//監(jiān)聽端口
    
    
//若地址出錯則監(jiān)聽本機地址
    if((local.sin_addr.s_addr = inet_addr(szAddress))
        
==INADDR_NONE)
    
{
        puts(
"所輸入的地址不正確,本服務(wù)將使用本機地址!");
        
//得到主機名
        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;
    }

    
//監(jiān)聽客戶連接請求
    if(listen(sServSock,5)==SOCKET_ERROR)
    
{
        nSockErr 
=WSAGetLastError();
        printf(
"listen error:%d\n", nSockErr);
        
return 1;
    }

    
//提示狀態(tài)
    printf("服務(wù)器啟動成功!\n");
    printf(
"服務(wù)器正在監(jiān)聽\n");
    
//進入處理連接循環(huán)
    while(1)
    
{
        
//若有客戶連接,則接受連接
        sConns = accept(sServSock,(struct sockaddr *)&client,&nAddrLen);
        
if(sConns == INVALID_SOCKET)
        
{
            nSockErr 
= WSAGetLastError();
            printf(
"accept error %d\n",nSockErr);
            
break;
        }

        
//連接正確則提示可以開始聊天
        printf("%s:%d連接到了本服務(wù),現(xiàn)在可以聊天了.\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
        
//創(chuàng)建一個線程用于聊天
        hThread = CreateThread(NULL,0,ClientThread,    (LPVOID)sConns,0,&dwThreadId);
        
if(hThread == NULL)
        
{
            printf(
"CreateThread() failed %d\n",GetLastError());
            
break;
        }

        
//聊天結(jié)束關(guān)閉聊天線程,繼續(xù)監(jiān)聽
        CloseHandle(hThread);
    }

    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信息結(jié)構(gòu)
    SOCKET  sClient;//定義本地套接字
    char  szBuffer[2048];//定義接受的緩沖
    char  szMessage[2048];//發(fā)送的消息
    char  szServer[128];//連接的服務(wù)器地址,IP地址
    int   ret;
    
    
struct  sockaddr_in server;//定義連接的服務(wù)器地址
    struct  hostent    *host =NULL;//定義地址
    
    
//提示輸入連接的服務(wù)器地址
    printf("請輸入連接的服務(wù)器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;
    }

    
    
//定義服務(wù)器地址以發(fā)送信息
    server.sin_family = AF_INET;
    server.sin_port 
= htons(5150);//端口
    
    server.sin_addr.s_addr 
= inet_addr(szServer);
    
    
//若沒有地址,則將地址設(shè)置為本機地址
    if(server.sin_addr.s_addr == INADDR_NONE)
    
{
        puts(
"所輸入服務(wù)器地址不正確,本將使用本機地址!");
        
//得到主機名
        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;
    }

    
//提示當前狀態(tài)
    puts("連接成功,現(xiàn)在可以聊天了!\n");
    puts(
"輸入\"exit\"可退出聊天\n");
    
    
    
//進入聊天狀態(tài)
    while(1)
    
{
        
////////發(fā)送
        printf("發(fā)送消息:");
        
//寫入發(fā)送信息
        gets(szMessage);
        
if(strlen(szMessage)==0)
        
{
            printf(
"發(fā)送不能為空哦\n");
            strcpy(szMessage,
"不說話!");
        }

        
//發(fā)送信息
        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(
"你已經(jīng)退出了聊天!");
            
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());
            
        }

        
        
//設(shè)置接收得到的字符串,并打印
        szBuffer[ret] = '\0';
        
if(!strcmp(szBuffer,"exit"))
        
{
            printf(
"服務(wù)器已經(jīng)停止聊天!");
            
break;
        }

        printf(
"服務(wù)器:%s\n",szBuffer);
    }
 
    
    
//關(guān)閉套接字
    closesocket(sClient);
    
//清空winsock環(huán)境
    WSACleanup();
    
return 0;
    
}