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"。

 

posted @ 2018-10-14 19:17  fengMisaka  阅读(5786)  评论(0编辑  收藏  举报