• C++网络编程(一)


    学习C++已经有一段时间了,一直都是学习基础的东西,每次写的代码都比较少,没有明确的学习目标,基础还是基础,漫无边际的,基本上都是做一道或者几道算法题,连一个小小的实战都没有,也不知道自己学得怎么样了,现在终于有一个小小的实战了《C++ 一个网络编程实例》。由于自己一直在做C#,只能业余时间学习C++,都说C++ 是那么的难,暂时还没有感觉到有多难,毕竟写代码也有两年多了。我要学习多久才能进一家做C++研发的公司呢?

    相信在不远处有一家C++研发公司在等着我。

    这只是一个小小的实例,包括Socket编程、多线程、文件操作。

    简单介绍:他实现了点对点聊天,一个服务器,一个客户端,主线程用来发送数据,启动一个子线程用来接收数据,服务器记录聊天内容。他只是用上了上面所说的三个技术,如果你对上面三个技术不是很熟,或许对你有点帮助,如果你很熟,既然来了希望你能指导一下我,如果你是高手希望你能指导一下我的编码问题。我太渴望写出高效简洁的代码。

     废话就少说了,程序里处处都是注释,你可以选择看看我的代码,或是选择直接运行看看,源码下载》。

     

    服务器代码:

       

    // Server.cpp : 定义控制台应用程序的入口点。

    #include
    "stdafx.h"
    #include
    <windows.h>
    #include
    <process.h>
    #include
    <iostream>
    #include
    "FileLog.h"
    #include
    "time.h"
    usingnamespace std;
    #pragma comment(lib,"ws2_32.lib")

    //多线程调用的方法只有一个指针型的参数,有时候需要多个参数,所以定义一个结构,参数作为结构的字段
    typedef struct _receiveStruct
    {
    SOCKET
    *Socket;
    FileLog
    *fileLog;
    _receiveStruct(SOCKET
    *_socket,FileLog *_fileLog):Socket(_socket),fileLog(_fileLog){}
    } ReceiveStruct;

    //获取今天日期的字符串
    string GetDate(constchar*format)
    {
    time_t tm;
    struct tm *now;
    char timebuf[20];
    time(
    &tm);
    now
    =localtime(&tm);
    strftime(timebuf,
    sizeof(timebuf)/sizeof(char),format,now);
    returnstring(timebuf);
    }

    //接收数据线程
    void receive(PVOID param)
    {
    ReceiveStruct
    * receiveStruct=(ReceiveStruct*)param;
    char buf[2048];
    int bytes;
    while(1)
    {
    //接收数据
    if((bytes=recv(*receiveStruct->Socket,buf,sizeof(buf),0))==SOCKET_ERROR){
    cout
    <<"接收数据失败!\n";
    _endthread();
    //终止当前线程
    }
    buf[bytes]
    ='\0';
    cout
    <<"客户端说:"<<buf<<endl;
    receiveStruct
    ->fileLog->Write("客户端 ").WriteLine(GetDate("%Y-%m-%d %H:%M:%S").c_str()).WriteLine(buf);//记录聊天内容
    }
    }


    //获取本机IP
    in_addr getHostName(void)
    {
    char host_name[255];
    //获取本地主机名称
    if (gethostname(host_name, sizeof(host_name)) == SOCKET_ERROR) {
    cout
    <<"Error %d when getting local host name."<<WSAGetLastError();
    Sleep(
    3000);
    exit(
    -1);
    }

    //从主机名数据库中得到对应的“IP”
    struct hostent *phe = gethostbyname(host_name);
    if (phe ==0) {
    cout
    <<"Yow! Bad host lookup.";
    Sleep(
    3000);
    exit(
    -1);
    }

    struct in_addr addr;
    memcpy(
    &addr, phe->h_addr_list[0], sizeof(struct in_addr));
    return addr;
    }


    //启动服务器
    SOCKET StartServer(void)
    {
    //创建套接字
    SOCKET serverSocket;
    if((serverSocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET){
    cout
    <<"创建套接字失败!";
    Sleep(
    3000);
    exit(
    -1);
    }
    short port=1986;
    struct sockaddr_in serverAddress;
    //初始化指定的内存区域
    memset(&serverAddress,0,sizeof(sockaddr_in));
    serverAddress.sin_family
    =AF_INET;
    serverAddress.sin_addr.S_un.S_addr
    = htonl(INADDR_ANY);
    serverAddress.sin_port
    = htons(port);

    //绑定
    if(bind(serverSocket,(sockaddr*)&serverAddress,sizeof(serverAddress))==SOCKET_ERROR){
    cout
    <<"套接字绑定到端口失败!端口:"<<port;
    Sleep(
    3000);
    exit(
    -1);
    }

    //进入侦听状态
    if(listen(serverSocket,SOMAXCONN)==SOCKET_ERROR){
    cout
    <<"侦听失败!";
    Sleep(
    3000);
    exit(
    -1);
    }

    //获取服务器IP
    struct in_addr addr = getHostName();
    cout
    <<"Server "<<inet_ntoa(addr)<<" : "<<port<<" is listening......"<<endl;
    return serverSocket;
    }


    //接收客户端连接
    SOCKET ReceiveConnect(SOCKET &serverSocket)
    {
    SOCKET clientSocket;
    //用来和客户端通信的套接字
    struct sockaddr_in clientAddress;//用来和客户端通信的套接字地址
    memset(&clientAddress,0,sizeof(clientAddress));//初始化存放客户端信息的内存
    int addrlen =sizeof(clientAddress);

    //接受连接
    if((clientSocket=accept(serverSocket,(sockaddr*)&clientAddress,&addrlen))==INVALID_SOCKET){
    cout
    <<"接受客户端连接失败!";
    Sleep(
    3000);
    exit(
    -1);
    }
    cout
    <<"Accept connection from "<<inet_ntoa(clientAddress.sin_addr)<<endl;
    return clientSocket;
    }


    //发送数据
    void SendMsg(SOCKET &clientSocket,FileLog &fileLog)
    {
    char buf[2048];
    while(1){
    cout
    <<"服务器说:";
    gets_s(buf);
    if(send(clientSocket,buf,strlen(buf),0)==SOCKET_ERROR){
    cout
    <<"发送数据失败!"<<endl;
    Sleep(
    3000);
    exit(
    -1);
    }
    fileLog.Write(
    "服务器 ").WriteLine(GetDate("%Y-%m-%d %H:%M:%S").c_str()).WriteLine(buf);//记录聊天内容
    }
    }


    int main(int argc, char* argv[]){
    WSADATA wsa;
    //WSADATA结构被用来保存函数WSAStartup返回的Windows Sockets初始化信息

    //MAKEWORD(a,b)是将两个byte型合并成一个word型,一个在高8位(b),一个在低8位(a)
    if(WSAStartup(MAKEWORD(2,2),&wsa)!=0){
    cout
    <<"套接字初始化失败!";
    Sleep(
    3000);
    exit(
    -1);
    }

    SOCKET serverSocket
    =StartServer();//启动服务器
    SOCKET clientSocket=ReceiveConnect(serverSocket);//接收客服端的链接

    FileLog fileLog;
    fileLog.Open(GetDate(
    "%Y%m%d").append(".log").c_str());//打开记录聊天内容文件

    ReceiveStruct receiveStruct(
    &clientSocket,&fileLog);
    _beginthread(receive,
    0,&receiveStruct);//启动一个接收数据的线程

    SendMsg(clientSocket,fileLog);
    //发送数据

    fileLog.Close();
    //关闭文件
    closesocket(clientSocket);//关闭客户端套接字(马上发送FIN信号,所有没有接收到或是发送完成的数据都会丢失)
    closesocket(serverSocket);//关闭服务器套接字

    //清理套接字占用的资源
    WSACleanup();
    return0;
    }

      客户端代码:

       

    // Client.cpp  
    #include "stdafx.h"
    #include
    <windows.h>
    #include
    <process.h>
    #include
    <iostream>
    usingnamespace std;
    #pragma comment(lib,"ws2_32.lib")

    //接收数据
    void Receive(PVOID param)
    {
    char buf[2096];
    while(1)
    {
    SOCKET
    * sock=(SOCKET*)param;
    int bytes;
    if((bytes=recv(*sock,buf,sizeof(buf),0))==SOCKET_ERROR){
    printf(
    "接收数据失败!\n");
    exit(
    -1);
    }
    buf[bytes]
    ='\0';
    cout
    <<"服务器说:"<<buf<<endl;
    }
    }

    //获取服务器IP
    unsigned long GetServerIP(void)
    {
    //把字符串的IP地址转化为u_long
    char ipStr[20];
    //用第二个参数填充第一个参数所指的内存,填充的长度为第三个参数的大小
    memset(ipStr,0,sizeof(ipStr));
    cout
    <<"请输入你要链接的服务器IP:";
    cin
    >>ipStr;
    unsigned
    long ip;
    if((ip=inet_addr(ipStr))==INADDR_NONE){
    cout
    <<"不合法的IP地址:";
    Sleep(
    3000);
    exit(
    -1);
    }
    return ip;
    }

    //链接服务器
    void Connect(SOCKET &sock)
    {
    unsigned
    long ip=GetServerIP();
    //把端口号转化成整数
    short port=1986;
    cout
    <<"Connecting to "<<inet_ntoa(*(in_addr*)&ip)<<" : "<<port<<endl;
    struct sockaddr_in serverAddress;
    memset(
    &serverAddress,0,sizeof(sockaddr_in));
    serverAddress.sin_family
    =AF_INET;
    serverAddress.sin_addr.S_un.S_addr
    = ip;
    serverAddress.sin_port
    = htons(port);
    //建立和服务器的连接
    if(connect(sock,(sockaddr*)&serverAddress,sizeof(serverAddress))==SOCKET_ERROR){
    cout
    <<"建立连接失败:"<<WSAGetLastError();
    Sleep(
    3000);
    exit(
    -1);
    }
    }

    //发送数据
    void SendMsg(SOCKET &sock)
    {
    char buf[2048];
    while(1){

    //从控制台读取一行数据
    gets_s(buf);
    cout
    <<"我说:";
    //发送给服务器
    if(send(sock,buf,strlen(buf),0)==SOCKET_ERROR){
    cout
    <<"发送数据失败!";
    exit(
    -1);
    }
    }
    }

    int main(int argc, char* argv[]){
    WSADATA wsa;
    //初始化套接字DLL
    if(WSAStartup(MAKEWORD(2,2),&wsa)!=0){
    cout
    <<"套接字初始化失败!";
    Sleep(
    3000);
    exit(
    -1);
    }

    //创建套接字
    SOCKET sock;
    if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET){
    cout
    <<"创建套接字失败!";
    exit(
    -1);
    }

    Connect(sock);
    //链接服务器

    _beginthread(Receive,
    0,&sock);//启动接收数据线程
    SendMsg(sock);//发送数据

    //清理套接字占用的资源
    WSACleanup();
    return0;
    }

      文件操作代码(FileLog.h):

       

    #include "iostream"
    #include
    "string.h"
    #include
    <windows.h>
    usingnamespace std;

    class FileLog
    {
    private:
    CRITICAL_SECTION cs;
    HANDLE fileHandle;
    void Lock()
    {
    EnterCriticalSection(
    &cs);// 进入临界区
    }

    void UnLock()
    {
    LeaveCriticalSection(
    &cs);//离开临界区
    }

    public:
    FileLog()
    {
    InitializeCriticalSection(
    &cs);//初始化临界区
    fileHandle=INVALID_HANDLE_VALUE;//先初始化为错误的句柄
    }

    ~FileLog()
    {
    if(fileHandle!=INVALID_HANDLE_VALUE)
    {
    //CloseHandle的功能是关闭一个打开的对象句柄,该对象句柄可以是线程句柄,也可以是进程、信号量等其他内核对象的句柄
    CloseHandle(fileHandle);
    }
    DeleteCriticalSection(
    &cs);//删除临界区
    }

    BOOL Open(
    constchar*fileName);//打开文件
    FileLog& Write(constchar*content);//向文件中写入内容
    FileLog& WriteLine(constchar*content);//向文件中写入内容
    BOOL Read(char*buf,int size);//读文件内容
    BOOL Close();//关闭文件
    };

      文件操作代码(FileLog.app):

    #include "stdafx.h"
    #include
    "FileLog.h"
    //打开文件
    BOOL FileLog::Open(constchar*fileName)
    {
    if(fileHandle==INVALID_HANDLE_VALUE)
    {
    fileHandle
    =CreateFile(fileName,GENERIC_WRITE|GENERIC_READ,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,
    OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
    if(fileHandle!=INVALID_HANDLE_VALUE)
    {
    SetFilePointer(fileHandle,
    0,NULL,FILE_END);
    return TRUE;
    }
    }
    return FALSE;
    }

    //写文件 返回当前对象的引用,实现连接操作
    FileLog& FileLog::Write(constchar*content)
    {
    Lock();
    if(fileHandle!=INVALID_HANDLE_VALUE)
    {
    DWORD dwSize
    =0;
    WriteFile(fileHandle,content,strlen(content),
    &dwSize,NULL);//
    }
    //开始的时候少写了这句,由于加的锁没有释放,一个线程占用之后,导致其他线程只能一直等待,好久都没有找到原因。
    UnLock();
    return*this;
    }

    //写入一行
    FileLog& FileLog::WriteLine(constchar*content)
    {
    Lock();
    if(fileHandle!=INVALID_HANDLE_VALUE)
    {
    DWORD dwSize
    =0;
    WriteFile(fileHandle,content,strlen(content),
    &dwSize,NULL);//
    }
    UnLock();
    return FileLog::Write("\r\n");
    }

    //读文件内容
    BOOL FileLog::Read(char*buf,int size)
    {
    BOOL isOK
    =FALSE;
    Lock();
    if(fileHandle!=INVALID_HANDLE_VALUE)
    {
    DWORD dwSize
    =0;
    isOK
    =ReadFile(fileHandle,buf,size,&dwSize,NULL);//
    }
    return isOK;
    }

    //关闭文件
    BOOL FileLog::Close()
    {
    BOOL isOK
    =FALSE;
    Lock();
    if(fileHandle!=INVALID_HANDLE_VALUE)
    {
    isOK
    =CloseHandle(fileHandle);
    fileHandle
    =INVALID_HANDLE_VALUE;
    }
    UnLock();
    return isOK;
    }

     

    作者:陈太汉

    博客:http://www.cnblogs.com/hlxs/

  • 相关阅读:
    使用MyBatis生成插件
    关联源码及日志
    下载依赖
    框架简介及基本使用
    注解的本质 及 反射读取属性值
    元注解
    广告系统学习笔记(一)
    Nginx学习笔记(三)
    Nginx学习笔记(二)
    Nginx学习笔记(一)
  • 原文地址:https://www.cnblogs.com/hlxs/p/2172351.html
Copyright © 2020-2023  润新知