C语言小项目-基于TCP协议和SOCKET编程的网络通信系统

1.1 功能结构图  

网络通信系统一共由4个模块组成,分别是点对点客户端、点对点服务端、服务器中转服务端、服务器中转客户端。这4个模块是成对使用的,点对点客户端和点对点服务端一起使用,服务器中转服务端和服务器中转客户端一起使用。

功能结构体如下图所示:

 1.2 编写程序

网络通信程序分为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"。

原文地址:https://www.cnblogs.com/linuxAndMcu/p/9787473.html

时间: 2024-10-12 03:58:05

C语言小项目-基于TCP协议和SOCKET编程的网络通信系统的相关文章

TCP协议和socket API 学习笔记

本文转载至 http://blog.chinaunix.net/uid-16979052-id-3350958.html 分类: 原文地址:TCP协议和socket API 学习笔记 作者:gilbertjuly  • TCP包头 ACK为1时,确认序号有效,表示期望收到的下一个序号,是上次成功收到的字节序加1. SYN, FIN都占用一个序号. • TCP连接的建立 client通过connect()来建立TCP连接,connect()会发送SYN报文: server通过bind().list

网络编程——基于TCP协议的Socket编程,基于UDP协议的Socket编程

Socket编程 目前较为流行的网络编程模型是客户机/服务器通信模式 客户进程向服务器进程发出要求某种服务的请求,服务器进程响应该请求.如图所示,通常,一个服务器进程会同时为多个客户端进程服务,图中服务器进程B1同时为客户进程A1.A2和B2提供服务. Socket概述 ①   所谓Socket通常也称作“套接字”,用于描述IP地址和端口,是一个通信链的句柄.应用程序通常通过“套接字”向网络发出请求或者应答网络请求. ②   Socket是连接运行在网络上的两个程序间的双向通信的端点. ③  

基于TCP/UDP的socket编程

基于TCP(面向连接)的socket编程服务器端顺序: 1. 创建套接字(socket) 2. 将套接字绑定到一个本地地址和端口上(bind) 3. 将套接字设为监听模式,准备接收客户请求(listen) 4. 等待客户请求的到来:当请求带来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept) 5. 用返回的套接字和客户端进行通信(send/recv) 6. 返回,等待另一个客户请求 7. 关闭套接字(closesocket)客户端程序: 1. 创建套接字(socket) 2.

Java网络编程基础(二)-- 基于TCP/IP的Socket编程

本节讲点: 客户端套接字:Socket:Socket的创建和使用方法,以及Socket选项和异常. 服务端套接字:ServerSocket:SeverSocket的创建和使用方法,以及ServerSocket选项 简单的Client/Server对话程序 支持多客户端的Client/Server服务响应程序 在学习JDK的网络编程之前先要了解一下网络基础知识和网络协议. TCP(传输控制协议)一种基于连接的通信协议.可靠传输 UDP(用户数据包协议)不稳定连接的通信协议 TCP和UDP的端口如下

基于TCP(面向连接)的Socket编程

一.客户端: 1.打开一个套接字(Socket); 2.发起连接请求(connect); 3.如果连接成功,则进行数据交换(read.write.send.recv): 4.数据交换完成,关闭连接(shutdown.close): 二.服务器端: 1.打开一个套接字(Socket); 2.将套接字绑定到服务器地址上(bind): 3.指定套接字为服务器套接字(listen),做好连接请求准备: 4.等待连接请求(connect); 5.如果连接请求到,则连接建立,进行数据交换(read.writ

基于TCP协议之——socket编程

一. 套接字(socket) socket英文为插座的意思,也就是为用户提供了一个接入某个链路的接口.而在计算机网络中,一个IP地址标识唯一一台主机,而一个端口号标识着主机中唯一一个应用进程,因此"IP+端口号"就可以称之为socket. 两个主机的进程之间要通信,就可以各自建立一个socket,其实可以看做各自提供出来一个"插座",然后通过连接上"插座"的两头也就是由这两个socket组成的socket pair就标识唯一一个连接,以此来表示网

基于TCP协议的socket编程

一.什么是socket socket本身有"插座"的意思,在TCP/IP协议中,"IP地址+TCP或UDP端口号"唯一标识网络通讯中的个进程,"IP地址+端口号"就称为socket. 在TCP协议中,建立连接的两个进程各自有一个socket来标识,那么这两个socket组成 的socketpair就唯一标识一个连接. 套接字是一种进程间的通信的方法,不同于以往介绍的进程间通信方法的是,它并不局限于同一台计算机的资源,例如文件系统空间,共享内存或者

基于UDP协议之——socket编程

一. socket API 前面一篇<基于TCP协议之--socket编程>http://2627lounuo.blog.51cto.com/10696599/1775559已经花了大量的篇幅讲述了socket和使用基本的socket API所需要注意的问题,这里就不再赘述了.下面主要谈论的是UDP和TCP在socket编程中的不同之处: 1. 创建sock 和TCP面向连接的可靠的字节流传输服务不同的是,UDP是无连接的不可靠的数据报传输服务:虽然有所不同,但同样在进程间通信的时候需要提供出

(1)网络编程的常识 (2)基于tcp协议的编程模型 (3)tcp协议和udp协议的比较 (4)基于udp协议的编程模型

1.网络编程的常识 目前主流的网络通讯软件有:微信.QQ.YY.陌陌.探探.飞信.阿里旺旺.... 在吗? 1.1 七层网络模型(熟悉) 为了保证数据传递的可靠安全等等,ISO(国际标准委员会组织)将数据的传递从逻辑上划分为以下七层: 应用层.表示层.会话层.传输层.网络层.数据链路层.物理层. 在发送数据之前要按照上述七层协议从上到下一层一层进行加包处理,再发送出去; 在接收数据之后要按照上述七层协议从下到上一层一层进行拆包处理,再解析出来: 1.2 常用的协议(熟悉) http协议 - 超文