C語言小項目-基於TCP協議和SOCKET編程的網絡通信系統


1.1 功能結構圖  

網絡通信系統一共由4個模塊組成,分別是點對點客戶端、點對點服務端、服務器中轉服務端、服務器中轉客戶端。這4個模塊是成對使用的,點對點客戶端和點對點服務端一起使用,服務器中轉服務端和服務器中轉客戶端一起使用。

 功能結構體如下圖所示:

1.2 TCP、UDP編程流程

TCP_服務器端的一般步驟是:

 

  1、創建一個socket,用函數socket()。   2、socket綁定本機的IP地址和端口號,用函數bind()。   3、開啟監聽,用函數listen()。 4、接收客戶端上來的連接,用函數accept()。 5、通過accept()返回相應客戶端的socket建立專用的通信通道。   6、收發數據,用函數send()和recv(),或者read()和write()。   7、關閉網絡連接,關閉監聽。

 

TCP編程的客戶端的一般步驟是:

  1、創建一個socket,用函數socket()。   2、設置要連接的對方的IP地址和端口等屬性。   3、連接服務器,用函數connect()。   4、收發數據,用函數send()和recv(),或者read()和write()。   5、關閉網絡連接。 


UDP編程的服務器端一般步驟是:

  1、創建一個socket,用函數socket()。   2、綁定IP地址、端口等信息到socket上,用函數bind()。   3、循環接收數據,用函數recvfrom()。   4、關閉網絡連接。


UDP編程的客戶端一般步驟是:

  1、創建一個socket,用函數socket()。    2、設置對方的IP地址和端口等屬性。   3、發送數據,用函數sendto()。   4、關閉網絡連接。 

 

 1.3 編寫程序

網絡通信程序分為2個模塊實現,點對點模塊和服務器中轉模塊。

common.h

#ifndef __COMMON_H__
#define __COMMON_H__

#include "stdafx.h"
#include <stdlib.h>
#include <stdio.h>

#pragma comment (lib,"ws2_32.lib") //鏈接ws2_32.dll動態鏈接庫

//客戶端發送給服務端的消息類型
#define CLIENTSEND_EXIT 1
#define CLIENTSEND_TRAN 2
#define CLIENTSEND_LIST 3

//服務端發送給客戶端的消息類型
#define SERVERSEND_SELFID 1
#define SERVERSEND_NEWUSR 2
#define SERVERSEND_SHOWMSG 3
#define SERVERSEND_ONLINE 4


//定義記錄聊天消息的文件指針
extern FILE *server_fp; //記錄服務端聊天消息的文件指針
extern FILE *client_fp; //記錄客戶端聊天消息的文件指針

//服務端接收消息的結構體,客戶端使用這個結構發送消息(以客戶端為主體)
struct CReceivePackage
{
    int iType;            //存放消息類型
    int iToID;            //存放目標用戶ID
    int iFromID;        //存放原用戶ID
    char cBuffer[1024];    //存放消息內容
};

//以服務端發送消息的結構體,服務端使用這個結構體發送消息(以服務端為主體)
struct CSendPackage
{
    int iType;            //消息類型
    int iCurConn;        //當前在線用戶數量
    char cBuffer[512];    //存放消息內容 [VS內部限制了堆棧的大小,所以減少為512,避免堆棧溢出]
};

//服務端存儲在線用戶數據的結構體
struct CUserSocketInfo
{
    int ID;                //用戶的ID
    char cDstIP[64];    //用戶的IP地址,擴展使用
    int iPort;//用戶應用程序端口擴展使用
    SOCKET sUserSocket;    //網絡句柄
};

//客戶端存儲在線用戶列表的結構體
struct CUser
{
    int ID;                //用戶的ID
    char cDstIP[64];    //用戶的IP地址 擴展時使用
};

extern struct CUser usr[20]; //客戶端存儲用戶信息的對象
extern int bSend;//是否可以發送消息
extern int iMyself;//自己的id
extern int iNew;//在線用戶數

extern int CheckIP(char *cIP); //檢查IP地址
extern struct CUserSocketInfo usrinfo[20]; //服務端存儲用戶信息的對象


#endif

 

common.cpp

#include "stdafx.h"
#include <WinSock2.h> //包含socket套接字的API函數
#include "common.h"

//定義記錄聊天消息的文件指針
FILE *server_fp; //記錄服務端聊天消息的文件指針
FILE *client_fp; //記錄客戶端聊天消息的文件指針

struct CUser usr[20]; //客戶端存儲用戶信息的對象
int bSend=0;//是否可以發送消息
int iMyself;//自己的id
int iNew=0;//在線用戶數

struct CUserSocketInfo usrinfo[20]; //服務端存儲用戶信息的對象

/*
函數功能:檢查IP地址
詳細介紹:檢查IP地址中的點是否是3個,以及每段IP的數值是否超過255
*/
int CheckIP(char *cIP)
{
    char IPAddress[128];//IP地址字符串
    char IPNumber[4];//IP地址中每組的數值
    int iSubIP=0;//IP地址中4段之一
    int iDot=0;//IP地址中'.'的個數
    int iResult=0;
    int iIPResult=1;
    int i;//循環控制變量

    memset(IPNumber,0,4);
    strncpy(IPAddress,cIP,128);

    for(i=0;i<128;i++)
    {
        if(IPAddress[i]=='.')
        {
            iDot++;
            iSubIP=0;
            if(atoi(IPNumber)>255) //檢查每段IP的數值是否超過255
                iIPResult = 0;
            memset(IPNumber,0,4);
        }
        else
        {
            IPNumber[iSubIP++]=IPAddress[i];
        }
        if(iDot==3 && iIPResult!=0) //檢查IP地址中的點是否是3個
            iResult= 1;
    }

    return iResult;
}

 

pointToPointModule.h

#ifndef __pointToPointModule_H__ 
#define __pointToPointModule_H__

#include "stdafx.h"
#include "common.h"

extern void createServer(); //創建點對點服務端
extern void createClient(); //創建點對點客戶端

#endif

 

pointToPointModule.cpp  [點對點模塊]

#include "stdafx.h"
#include <WinSock2.h> //包含socket套接字的API函數
#include "pointToPointModule.h"

/*
函數功能:退出系統函數,並釋放文件指針和ws2_32.lib動態鏈接庫
*/
void ExitSystem()
{
    if(server_fp!=NULL)
        fclose(server_fp);
    if(client_fp!=NULL)
        fclose(client_fp);
    WSACleanup(); //釋放初始化ws2_32.lib動態鏈接庫所分配的資源
    exit(0);
}

/*
函數功能:創建客戶端接收消息的線程
*/
DWORD WINAPI threadproClient(LPVOID lpParam)
{
    SOCKET hsock=(SOCKET)lpParam;
    char cRecvBuffer[1024]; //接收消息緩存,接收數據保存在cRecvBuff[]
    char cShowBuffer[1024]; //顯示消息緩存
    int recCharNum = 0;

    if(hsock!=INVALID_SOCKET)
        printf("start:\n");

    while(1)
    {
        recCharNum = recv(hsock,cRecvBuffer,1024,0);
        if(recCharNum >= 0)
        {        
            cRecvBuffer[recCharNum]='\0';
            sprintf(cShowBuffer,"to me : %s\n",recCharNum);
            printf("%s",cShowBuffer);
            fwrite(cShowBuffer ,sizeof(char),strlen(cShowBuffer),client_fp);
            fflush(client_fp);
            if(strcmp("exit",cRecvBuffer)==0)
            {
                ExitSystem();
            }
        }
    }
    return 0;
}

/*
函數功能:創建服務端接收消息的線程
*/
DWORD WINAPI threadproServer(LPVOID lpParam) // LPVOID lpParameter為線程參數
{
    SOCKET hsock = (SOCKET)lpParam;
    char cRecvBuffer[1024]; //接收消息緩存,接收數據保存在cRecvBuff[]
    char cShowBuffer[1024]; //顯示消息緩存
    int iRecvResult = 0;

    if(hsock != INVALID_SOCKET)
    {
        printf("start:\n");
    }

    while(1)
    {
        iRecvResult = recv(hsock,cRecvBuffer,1024,0);
        if(iRecvResult >= 0)
        {
            cRecvBuffer[iRecvResult] = '\0'; //將cRecvBuff[]變為字符串
            sprintf(cShowBuffer,"to me:%s\n",cRecvBuffer); //sprintf: 格式化的數據寫入某個字符串中
            printf("%s",cShowBuffer); //顯示接收到的數據
            fwrite(cShowBuffer,1,strlen(cShowBuffer),server_fp); //將接收到的數據,寫入到服務端文件中
            fflush(server_fp); //刷新文件流 stream 的輸出緩沖區 (文件指針本質也是一種流stream)

            if(strcmp("exit",cRecvBuffer) == 0)
            {
                ExitSystem(); //退出系統函數,並釋放文件指針和ws2_32.lib動態鏈接庫
                //退出系統
            }
        }
    }

    return 0;
}

/*
函數功能:創建點對點服務端
詳細介紹:服務端監聽客服端發來的連接請求,當有客戶端發來連接請求時,啟動接收消息的線程並進入發送消息的循環中
*/
void createServer()
{
    SOCKET    server_listenSocket; //服務端的監聽套接字,socket()創建的,監聽客戶端是否發來連接請求
    SOCKET    server_communiSocket; //服務端的通信套接字,accept()返回的,與客戶端進行通信
    struct sockaddr_in server_sockAddr; //包含服務端的本地接口和端口號的sockaddr_in結構體
    struct sockaddr_in client_sockAddr; //包含所連接客服端的接口和端口號的sockaddr_in結構體
    struct hostent *localHost; //包含本地主機的主機名和地址信息的hostent結構體指針

    int iPort=4600; //設定為固定端口
    char* localIP; //本地主機的IP地址
    DWORD nThreadId = 0; //進程ID
    int iBindResult=-1; //綁定結果
    int ires;//發送的返回值
    int iWhileCount_bind = 10; //能夠重新輸入端口號綁定本地主機的機會次數
    int iWhileCount_listen = 10; //能夠重新監聽的機會次數
    char cWelcomBuffer[]="Welcome to you\0"; //歡迎消息的字符串
    char cSendBuffer[1024];//發送消息緩存
    char cShowBuffer[1024];//接收消息緩存
    int len=sizeof(struct sockaddr);

    server_fp= fopen("MessageServer.txt","a");//打開記錄消息的文件

    //創建一個服務端的本地連接套接字
    server_listenSocket = socket (AF_INET,SOCK_STREAM,0); //TCP方式,故type選擇SOCK_STREAM流式套接字

    printf("請輸入本機綁定的端口號(大於1024):");
    scanf("%d",&iPort);

    //獲取本地主機的IP地址
    localHost = gethostbyname(""); //獲取包含本地主機的主機名和地址信息的hostent結構體指針
    localIP = inet_ntoa (*(struct in_addr *)*localHost->h_addr_list); //獲取本地主機的IP地址

    //配置本地主機的網絡地址信息
    server_sockAddr.sin_family = AF_INET; //設置地址家族                    
    server_sockAddr.sin_port = htons(iPort); //設置本地主機的端口號        
    server_sockAddr.sin_addr.S_un.S_addr = inet_addr(localIP); //設置本地主機的IP地址

    //將套接字綁定在本地主機上
    iBindResult=bind(server_listenSocket,(struct sockaddr*)&server_sockAddr,sizeof(struct sockaddr));
    //如果端口不能被綁定,重新設置端口
    while(iBindResult!=0 && iWhileCount_bind > 0)
    {
        printf("綁定失敗,重新輸入:");
        scanf("%d",iPort);

        //配置本地主機的網絡地址信息
        server_sockAddr.sin_family = AF_INET; //設置地址家族                        
        server_sockAddr.sin_port = htons(iPort); //設置本地主機的端口號        
        server_sockAddr.sin_addr.S_un.S_addr = inet_addr(localIP); //設置本地主機的IP地址

        //將套接字綁定在本地主機上
        iBindResult = bind(server_listenSocket,(struct sockaddr*)&server_sockAddr,sizeof(struct sockaddr));

        iWhileCount_bind--;
        if(iWhileCount_bind<=0)
        {
            printf("端口綁定失敗,重新運行程序\n");
            exit(0);
        }
    }

    //重復監聽
    while(iWhileCount_listen>0)
    {
        printf("start listen\n");
        listen(server_listenSocket,0);//返回值判斷單個監聽是否超時

        server_communiSocket=accept(server_listenSocket,(struct sockaddr*)&client_sockAddr,&len);
        if(server_communiSocket!=INVALID_SOCKET)
        {
            //有連接成功,發送歡迎信息
            send(server_communiSocket,cWelcomBuffer,sizeof(cWelcomBuffer),0);
            //啟動接收消息的線程
            CreateThread(NULL,0,threadproServer,(LPVOID)server_communiSocket,0,&nThreadId );
            break;
        }
        printf(".");

        iWhileCount_listen--;
        if(iWhileCount_listen<=0)
        {
            printf("\n建立連接失敗\n");
            exit(0);
        }
    }

    while(1)
    {
        memset(cSendBuffer,0,1024);
        scanf("%s",cSendBuffer);//輸入消息
        if(strlen(cSendBuffer)>0)//輸入消息不能為空
        {
            ires = send(server_communiSocket,cSendBuffer,strlen(cSendBuffer),0);//發送消息
            if(ires<0)
            {
                printf("發送失敗");
            }
            else
            {
                sprintf(cShowBuffer,"Send to : %s\n",cSendBuffer);
                printf("%s",cShowBuffer);
                fwrite(cShowBuffer ,sizeof(char),strlen(cShowBuffer),server_fp);//將消息寫入日志
            }
            if(strcmp("exit",cSendBuffer)==0)
            {
                ExitSystem();
            }
        }
    }
}


/*
函數功能:創建點對點客戶端
詳細介紹:在客服端,輸入服務端主機的IP地址,向服務端發送連接請求
*/
void createClient()
{
    SOCKET m_SockClient;
    struct sockaddr_in clientaddr; //包含客戶端的本地接口和端口號的sockaddr_in結構體

    char cServerIP[128]; //服務端的輸入IP地址數組
    int iWhileIP=10; //循環次數
    int iCnnRes; //連接結果
    DWORD nThreadId = 0; //線程ID值
    char cSendBuffer[1024]; //發送消息緩存
    char cShowBuffer[1024]; //顯示消息緩存
    char cRecvBuffer[1024]; //接收消息緩存
    int recCharNum; //接收的字符個數
    int ires; //發送消息的結果
    int iIPRes; //檢測IP是否正確

    m_SockClient = socket ( AF_INET,SOCK_STREAM, 0 );
    printf("請輸入服務器地址:");
    scanf("%s",cServerIP);

    //IP地址判斷
    if(strlen(cServerIP)==0)
        strcpy(cServerIP,"127.0.0.1");//沒有輸入地址,使用回環地址
    else
    {
        iIPRes=CheckIP(cServerIP);
        while(!iIPRes && iWhileIP>0)
        {
            printf("請重新輸入服務器地址:\n");
            scanf("%s",cServerIP);//重新輸入IP地址
            iIPRes=CheckIP(cServerIP);//檢測IP的合法性
            iWhileIP--;
            if(iWhileIP<=0)
            {
                printf("輸入次數過多\n");
                exit(0);
            }
        }
    }

    client_fp= fopen("MessageServerClient.txt","a");//打開記錄消息的文件
    clientaddr.sin_family = AF_INET;
    //客戶端向服務端請求的端口好,應該和服務端綁定的一致            
    clientaddr.sin_port = htons(4600);
    clientaddr.sin_addr.S_un.S_addr = inet_addr(cServerIP);

    iCnnRes = connect(m_SockClient,(struct sockaddr*)&clientaddr,sizeof(struct sockaddr));
    if(iCnnRes==0)//連接成功
    {
        recCharNum = recv(m_SockClient,cRecvBuffer,1024,0);//接收消息
        if( recCharNum > 0 )
        {
            printf("Receive form server : %s\n",cRecvBuffer);
            //啟動接收消息的線程
            CreateThread(NULL,0,threadproClient,(LPVOID)m_SockClient,0,&nThreadId );
        }

        while(1)
        {
            memset(cSendBuffer,0,1024);
            scanf("%s",cSendBuffer);    
            if(strlen(cSendBuffer)>0)
            {
                ires=send(m_SockClient,cSendBuffer,strlen(cSendBuffer),0);
                if(ires<0)
                {
                    printf("發送失敗\n");
                }
                else
                {
                    sprintf(cShowBuffer,"Send to : %s\n",cSendBuffer);//整理要顯示的字符串
                    printf("%s",cShowBuffer);
                    fwrite(cShowBuffer ,sizeof(char),strlen(cShowBuffer),client_fp);//記錄發送消息
                    fflush(client_fp);
                }
                if(strcmp("exit",cSendBuffer)==0)
                {
                    ExitSystem();
                }
            }
        }

    }//iCnnRes
    else
    {
        //    printf("%s",inet_addr(cServerIP));
        printf("連接不正確\n");
    }
}

 

serverTranModule.h

#ifndef __pointToPointModule_H__ 
#define __pointToPointModule_H__

#include "stdafx.h"
#include "common.h"

extern void createServer(); //創建點對點服務端
extern void createClient(); //創建點對點客戶端

#endif

 

serverTranModule.cpp [服務器中轉模塊]

#include "stdafx.h"
#include <WinSock2.h> //包含socket套接字的API函數
#include "common.h"
#include "serverTranModule.h"

/*
函數功能:服務器中轉模塊的退出系統
詳細介紹:服務器中轉模塊的退出系統與點對點模塊有所不同,后者需要關閉文件,前者不需要
*/
void ExitTranSystem()
{
    WSACleanup();
    exit(0);
}

/*
函數功能:負責中轉服務端,用於中轉消息和發送在線用戶列表的線程
詳細介紹:
*/
DWORD WINAPI threadTranServer(LPVOID pParam) 
{
    SOCKET hsock=(SOCKET)pParam;//獲取SOCKET句柄
    SOCKET sTmp;//臨時存放用戶的SOCKET句柄

    char cRecvBuffer[1024];//接收消息的緩存
    int num=0;//發送的字符串
    int m,j;//循環控制變量
    //char cTmp[2];//臨時存放用戶ID
    int ires;
    struct CSendPackage sp;//發包
    struct CReceivePackage *p;

    if(hsock!=INVALID_SOCKET)
        printf("start:%d\n",hsock);

    while(1)
    {
        num=recv(hsock,cRecvBuffer,1024,0);//接收發送過來的信息
        if(num>=0)
        {        
            p = (struct CReceivePackage*)cRecvBuffer;
            switch(p->iType)
            {
            case CLIENTSEND_TRAN://對消息進行中轉
                for(m=0;m<2;m++)
                {
                    if(usrinfo[m].ID==p->iToID)
                    {
                        //組包
                        sTmp=usrinfo[m].sUserSocket;
                        memset(&sp,0,sizeof(sp));
                        sp.iType=SERVERSEND_SHOWMSG;
                        strcpy(sp.cBuffer,p->cBuffer);
                        ires = send(sTmp,(char*)&sp,sizeof(sp),0);//發送內容
                        if(ires<0)
                            printf("發送失敗\n");
                    }
                }
                break;
            case CLIENTSEND_LIST://發送在線用戶
                memset(&sp,0,sizeof(sp));
                for(j=0;j<2;j++)
                {
                    if(usrinfo[j].ID!=p->iFromID && usrinfo[j].ID!=0)
                    {
                        sp.cBuffer[j]=usrinfo[j].ID;
                    }
                }
                sp.iType=SERVERSEND_ONLINE;
                send(hsock,(char*)&sp,sizeof(sp),0);
                break;
            case CLIENTSEND_EXIT:
                printf("退出系統\n");
                return 0;//結束線程
                break;
            }
        }
    }
    return 0;
}

/*
函數功能:中轉服務端通知所有客戶端有新用戶登陸的線程
詳細介紹:
*/
DWORD WINAPI NotyifyProc(LPVOID pParam)
{
    struct CSendPackage sp;//發送包
    SOCKET sTemp;//連接用戶的socket句柄
    int *p;//接收主線程發送過來的ID值
    int j;//循環控制變量
    p=(int*)pParam;//新用戶ID

    for(j=0;j<2;j++)//去除新登錄的,已經連接的
    {
        if(usrinfo[j].ID !=  (*p))
        {
            sTemp=usrinfo[j].sUserSocket;
            sp.iType=SERVERSEND_NEWUSR;//新上線通知
            sprintf(sp.cBuffer,"%d\n",(*p));
            send(sTemp,(char*)&sp,sizeof(sp),0);//發送新用戶上線通知
        }
    }
    return 0;
}

/*
函數功能:創建創建服務器中轉服務端
詳細介紹:
*/
void createTranServer()
{
    SOCKET server_listenSocket;//開始監聽的SOCKET句柄
    struct sockaddr_in server_sockAddr;//用於綁定的地址信息
    struct sockaddr_in client_sockAddr;//接收到的連接的地址信息
    int iRes;//獲取綁定的結果
    SOCKET m_Server;//已建立連接的SOCKET句柄
    struct hostent* localHost;//主機環境指針
    char* localIP;//本地IP地址
    struct CSendPackage sp;//發送包
    int iMaxConnect=20;//允許的最大連接個數
    int iConnect=0;//建立連接的個數
    DWORD nThreadId = 0;//獲取線程的ID值
    char cWarnBuffer[]="It is voer Max connect\0";//警告字符串
    int len=sizeof(struct sockaddr);
    int id;//新分配的客戶ID
    localHost = gethostbyname("");
    localIP = inet_ntoa (*(struct in_addr *)*localHost->h_addr_list);//獲取本地IP
    server_sockAddr.sin_family = AF_INET;                    
    server_sockAddr.sin_port = htons(4600);//設置綁定的端口號            
    server_sockAddr.sin_addr.S_un.S_addr = inet_addr(localIP);//設置本地IP

    //創建套接字
    server_listenSocket = socket (AF_INET,SOCK_STREAM,0);
    if(server_listenSocket == INVALID_SOCKET)
    {
        printf("建立套接字失敗\n");
        exit(0);
    }
    //綁定本地IP地址
    iRes=bind(server_listenSocket,(struct sockaddr*)&server_sockAddr,sizeof(struct sockaddr));
    if(iRes < 0)
    {
        printf("建立套接字失敗\n");
        exit(0);
    }
    //程序主循環
    while(1)
    {
        listen(server_listenSocket,0);//開始監聽
        m_Server=accept(server_listenSocket,(struct sockaddr*)&client_sockAddr,&len);//接收連接
        if(m_Server != INVALID_SOCKET)
        {
            printf("有新用戶登錄");//對方已登錄 
            if(iConnect < iMaxConnect)
            {
                //啟動接收消息線程
                CreateThread(NULL,0,threadTranServer,(LPVOID)m_Server,0,&nThreadId );
                //構建連接用戶的信息
                usrinfo[iConnect].ID=iConnect+1;//存放用戶ID
                usrinfo[iConnect].sUserSocket=m_Server;
                usrinfo[iConnect].iPort=0;//存放端口,擴展用
                //構建發包信息
                sp.iType=SERVERSEND_SELFID;//獲取的ID值,返回信息
                sp.iCurConn=iConnect;//在線個數
                id=iConnect+1;
                sprintf(sp.cBuffer,"%d\0",id);
                send(m_Server,(char*)&sp,sizeof(sp),0);//發送客戶端的ID值
                //通知各個客戶端
                if(iConnect>0)
                    CreateThread(NULL,0,NotyifyProc,(LPVOID)&id,0,&nThreadId );
                iConnect++;
            }
            else
                send(m_Server,cWarnBuffer,sizeof(cWarnBuffer),0);//已超出最大連接數
        }
    }
    WSACleanup();
}

/*
函數功能:創建服務器中轉客戶端的線程
詳細介紹:
*/
DWORD WINAPI threadTranClient(LPVOID pParam) 
{
    SOCKET hsock=(SOCKET)pParam;
    int i;//循環控制變量
    char cRecvBuffer[2048];//接收消息的緩存
    int    num;//接收消息的字符數
    //char cTmp[2];//臨時存放在線用戶ID
    struct CReceivePackage sp;//服務端的接收包是,客戶端的發送包
    struct CSendPackage *p;//服務端的發送包是,客戶端的接收包
    int iTemp;//臨時存放接收到的ID值
    while(1)
    {
        num = recv(hsock,cRecvBuffer,2048,0);//接收消息
        if(num>=0) 
        {
            p = (struct CSendPackage*)cRecvBuffer;
            if(p->iType==SERVERSEND_SELFID)
            {
                iMyself=atoi(p->cBuffer);
                sp.iType=CLIENTSEND_LIST;//請求在線人員列表
                send(hsock,(char*)&sp,sizeof(sp),0);
            }
            if(p->iType==SERVERSEND_NEWUSR)//登錄用戶ID
            {
                iTemp = atoi(p->cBuffer);
                usr[iNew++].ID=iTemp;//iNew表示有多少個新用戶登錄 
                printf("有新用戶登錄,可以與其聊天\n");
                bSend=1;//可以發送消息聊天
            }
            if(p->iType==SERVERSEND_SHOWMSG)//顯示接受的消息
            {
                printf("rec:%s\n",p->cBuffer);
            }
            if(p->iType==SERVERSEND_ONLINE)//獲取在線列表
            {
                for(i=0;i<2;i++)
                {
                    if(p->cBuffer[i]!=iMyself && p->cBuffer[i]!=0)
                    {
                        usr[iNew++].ID=p->cBuffer[i];
                        printf("有用戶在線,可以與其聊天\n");
                        bSend=1;//可以發送消息聊天
                    }
                }
                if(!bSend)
                    printf("在線列表為空\n");
            }
        }
    }
    return 0;
}

/*
函數功能:創建服務器中轉客戶端
詳細介紹:
*/
void createTranClient()
{
    SOCKET m_SockClient;//建立連接的socket
    struct sockaddr_in clientaddr;//目標的地址信息
    int iRes;//函數執行情況
    char cSendBuffer[1024];//發送消息的緩存
    DWORD nThreadId = 0;//保存線程的ID值
    struct CReceivePackage sp;//發包結構
    char IPBuffer[128];
    printf("輸入服務器IP地址\n");
    scanf("%s",IPBuffer);

    clientaddr.sin_family = AF_INET;                
    clientaddr.sin_port = htons(4600);//連接的端口號
    clientaddr.sin_addr.S_un.S_addr = inet_addr(IPBuffer);
    m_SockClient = socket ( AF_INET,SOCK_STREAM, 0 );//創建socket
    //建立與服務端的連接
    iRes = connect(m_SockClient,(struct sockaddr*)&clientaddr,sizeof(struct sockaddr));
    if(iRes < 0)
    {
        printf("連接錯誤\n");
        exit(0);
    }
    //啟動接收消息的線程
    CreateThread(NULL,0,threadTranClient,(LPVOID)m_SockClient,0,&nThreadId );
    while(1)//接收到自己ID
    {
        memset(cSendBuffer,0,1024);
        scanf("%s",cSendBuffer);//輸入發送內容
        if(bSend)
        {
            if(sizeof(cSendBuffer)>0)
            {
                memset(&sp,0,sizeof(sp));
                strcpy(sp.cBuffer,cSendBuffer);
                sp.iToID=usr[0].ID;//聊天對象是固定的
                sp.iFromID=iMyself;//自己
                sp.iType=CLIENTSEND_TRAN;
                send(m_SockClient,(char*)&sp,sizeof(sp),0);//發送消息
            }
            if(strcmp("exit",cSendBuffer)==0)
            {
                memset(&sp,0,sizeof(sp));
                strcpy(sp.cBuffer,"退出");//設置發送消息的文本內容
                sp.iFromID=iMyself;
                sp.iType=CLIENTSEND_EXIT;//退出
                send(m_SockClient,(char*)&sp,sizeof(sp),0);//發送消息
                ExitTranSystem();
            }
        }
        else
            printf("沒有接收對象,發送失敗\n");
        Sleep(10);
    }
}

 

networkCommuniSys.cpp

#include "stdafx.h"
#include <WinSock2.h> //包含socket套接字的API函數
#include "common.h"
#include "pointToPointModule.h"
#include "serverTranModule.h"

//主函數
int _tmain(int argc, _TCHAR* argv[])
{
    int iSel=0;
    WSADATA wsd;                                    
    WSAStartup(MAKEWORD(2,2),&wsd);

    do
    {
        printf("選擇程序類型:\n");
        printf("點對點服務端: 1\n");
        printf("點對點客戶端: 2\n");
        printf("服務器中轉服務端: 3\n");
        printf("服務器中轉客戶端: 4\n");
        scanf("%d",&iSel);
    }while(iSel<0 || iSel >4);

    switch(iSel)
    {
        case 1:
            createServer(); //創建點對點服務端
        break;

        case 2:
            createClient(); //創建點對點客戶端
        break;

        case 3:
            createTranServer(); //創建服務器中轉服務端
        break;

        case 4:
            createTranClient(); //創建服務器中轉客戶端
        break;
    }
    printf("退出系統\n");

    return 0;
}

 

 啟動系統,根據提示菜單選擇1,就可以創建點對點服務端,輸入固定端口號4600(客戶端連接服務器使用的端口),輸入后進入監聽狀態,當連接上客服端后,點對點服務端發送消息"Im hostA"。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM