C++一个网络编程实例

开发
学习C++已经有一段时间了,一直都是学习基础的东西,每次写的代码都比较少,没有明确的学习目标,基础还是基础,漫无边际的,基本上都是做一道或者几道算法题,连一个小小的实战都没有,也不知道自己学得怎么样了,现在终于有一个小小的实战了《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"  
using namespace 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(const char *format)  
{  
    time_t tm;  
    struct tm *now;  
    char timebuf[20];  
    time(&tm);  
    now=localtime(&tm);  
    strftime(timebuf,sizeof(timebuf)/sizeof(char),format,now);  
    return string(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();  
    return 0;  
}  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.
  • 106.
  • 107.
  • 108.
  • 109.
  • 110.
  • 111.
  • 112.
  • 113.
  • 114.
  • 115.
  • 116.
  • 117.
  • 118.
  • 119.
  • 120.
  • 121.
  • 122.
  • 123.
  • 124.
  • 125.
  • 126.
  • 127.
  • 128.
  • 129.
  • 130.
  • 131.
  • 132.
  • 133.
  • 134.
  • 135.
  • 136.
  • 137.
  • 138.
  • 139.
  • 140.
  • 141.
  • 142.
  • 143.
  • 144.
  • 145.
  • 146.
  • 147.
  • 148.
  • 149.
  • 150.
  • 151.
  • 152.
  • 153.
  • 154.
  • 155.
  • 156.
  • 157.
  • 158.
  • 159.
  • 160.
  • 161.
  • 162.
  • 163.
  • 164.
  • 165.
  • 166.
  • 167.
  • 168.
  • 169.
  • 170.
  • 171.
  • 172.
  • 173.
  • 174.
  • 175.
  • 176.
  • 177.
  • 178.
  • 179.
  • 180.

 

 客户端代码:

// Client.cpp    
#include "stdafx.h"  
#include <windows.h>  
#include <process.h>  
#include <iostream>  
using namespace 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();  
    return 0;  
}  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.
  • 105.

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

#include "iostream"  
#include "string.h"  
#include <windows.h>  
using namespace 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(const char *fileName);//打开文件  
        FileLog& Write(const char *content);//向文件中写入内容  
        FileLog& WriteLine(const char *content);//向文件中写入内容  
        BOOL Read(char *buf,int size);//读文件内容  
        BOOL Close();//关闭文件  
}; 
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.

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

#include "stdafx.h"  
#include "FileLog.h"  
//打开文件  
BOOL FileLog::Open(const char *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(const char *content)  
{  
    Lock();  
    if(fileHandle!=INVALID_HANDLE_VALUE)  
    {  
        DWORD dwSize=0;  
        WriteFile(fileHandle,content,strlen(content),&dwSize,NULL);//写  
    }  
    //开始的时候少写了这句,由于加的锁没有释放,一个线程占用之后,导致其他线程只能一直等待,好久都没有找到原因。  
    UnLock();     
    return *this;  
}   
 
//写入一行  
FileLog& FileLog::WriteLine(const char *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;  
}  
  
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.

作者:陈太汉
博客:http://www.cnblogs.com/hlxs/

【编辑推荐】

  1. 原生代码卷土重来 C++欲东山再起
  2. 深入理解gtest C/C++单元测试经验谈
  3. C++程序运行时的异常处理
  4. C++程序员必读:让你的代码更强大
  5. 详解C++用户自定义转换过程
责任编辑:陈贻新 来源: 陈太汉的博客
相关推荐

2010-01-11 09:56:07

C++编程实例

2021-01-05 12:38:53

C++编程语言软件开发

2009-07-30 18:18:27

C#时间计算

2009-08-18 17:19:33

C#事件模型

2021-05-28 18:12:51

C++设计

2023-01-02 18:15:42

PythonC++模块

2011-07-20 13:57:06

C++STL

2009-08-19 09:38:34

C++编程

2011-05-30 15:29:32

C++

2009-06-23 14:08:00

Java Socket

2013-07-18 09:58:18

C++程序员

2009-07-15 13:41:00

JDBC实例

2011-07-10 15:26:54

C++

2022-06-27 09:54:38

编程语言JavaC++

2009-08-31 14:01:50

C#创建一个文件

2009-09-01 16:14:06

C#窗口抖动

2022-05-19 14:49:19

Nick网络开源社区专有网络

2024-03-13 13:53:10

C++程序开发

2021-10-27 11:29:32

框架Web开发

2010-01-26 17:11:13

C++编程
点赞
收藏

51CTO技术栈公众号