学习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;
- }
客户端代码:
- // 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;
- }
文件操作代码(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();//关闭文件
- };
文件操作代码(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;
- }
作者:陈太汉
博客:http://www.cnblogs.com/hlxs/
【编辑推荐】