C++一個(gè)網(wǎng)絡(luò)編程實(shí)例
學(xué)習(xí)C++已經(jīng)有一段時(shí)間了,一直都是學(xué)習(xí)基礎(chǔ)的東西,每次寫的代碼都比較少,沒(méi)有明確的學(xué)習(xí)目標(biāo),基礎(chǔ)還是基礎(chǔ),漫無(wú)邊際的,基本上都是做一道或者幾道算法題,連一個(gè)小小的實(shí)戰(zhàn)都沒(méi)有,也不知道自己學(xué)得怎么樣了,現(xiàn)在終于有一個(gè)小小的實(shí)戰(zhàn)了《C++ 一個(gè)網(wǎng)絡(luò)編程實(shí)例》。由于自己一直在做C#,只能業(yè)余時(shí)間學(xué)習(xí)C++,都說(shuō)C++ 是那么的難,暫時(shí)還沒(méi)有感覺(jué)到有多難,畢竟寫代碼也有兩年多了。我要學(xué)習(xí)多久才能進(jìn)一家做C++研發(fā)的公司呢?
相信在不遠(yuǎn)處有一家C++研發(fā)公司在等著我。
這只是一個(gè)小小的實(shí)例,包括Socket編程、多線程、文件操作。
簡(jiǎn)單介紹:他實(shí)現(xiàn)了點(diǎn)對(duì)點(diǎn)聊天,一個(gè)服務(wù)器,一個(gè)客戶端,主線程用來(lái)發(fā)送數(shù)據(jù),啟動(dòng)一個(gè)子線程用來(lái)接收數(shù)據(jù),服務(wù)器記錄聊天內(nèi)容。他只是用上了上面所說(shuō)的三個(gè)技術(shù),如果你對(duì)上面三個(gè)技術(shù)不是很熟,或許對(duì)你有點(diǎn)幫助,如果你很熟,既然來(lái)了希望你能指導(dǎo)一下我,如果你是高手希望你能指導(dǎo)一下我的編碼問(wèn)題。我太渴望寫出高效簡(jiǎn)潔的代碼。
廢話就少說(shuō)了,程序里處處都是注釋,你可以選擇看看我的代碼,或是選擇直接運(yùn)行看看。
服務(wù)器代碼:
- // Server.cpp : 定義控制臺(tái)應(yīng)用程序的入口點(diǎn)。
- #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")
- //多線程調(diào)用的方法只有一個(gè)指針型的參數(shù),有時(shí)候需要多個(gè)參數(shù),所以定義一個(gè)結(jié)構(gòu),參數(shù)作為結(jié)構(gòu)的字段
- 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);
- }
- //接收數(shù)據(jù)線程
- void receive(PVOID param)
- {
- ReceiveStruct* receiveStruct=(ReceiveStruct*)param;
- char buf[2048];
- int bytes;
- while(1)
- {
- //接收數(shù)據(jù)
- if((bytes=recv(*receiveStruct->Socket,buf,sizeof(buf),0))==SOCKET_ERROR){
- cout<<"接收數(shù)據(jù)失敗!\n";
- _endthread();//終止當(dāng)前線程
- }
- buf[bytes]='\0';
- cout<<"客戶端說(shuō):"<<buf<<endl;
- receiveStruct->fileLog->Write("客戶端 ").WriteLine(GetDate("%Y-%m-%d %H:%M:%S").c_str()).WriteLine(buf);//記錄聊天內(nèi)容
- }
- }
- //獲取本機(jī)IP
- in_addr getHostName(void)
- {
- char host_name[255];
- //獲取本地主機(jī)名稱
- if (gethostname(host_name, sizeof(host_name)) == SOCKET_ERROR) {
- cout<<"Error %d when getting local host name."<<WSAGetLastError();
- Sleep(3000);
- exit(-1);
- }
- //從主機(jī)名數(shù)據(jù)庫(kù)中得到對(duì)應(yīng)的“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;
- }
- //啟動(dòng)服務(wù)器
- SOCKET StartServer(void)
- {
- //創(chuàng)建套接字
- SOCKET serverSocket;
- if((serverSocket=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET){
- cout<<"創(chuàng)建套接字失敗!";
- Sleep(3000);
- exit(-1);
- }
- short port=1986;
- struct sockaddr_in serverAddress;
- //初始化指定的內(nèi)存區(qū)域
- 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);
- }
- //進(jìn)入偵聽(tīng)狀態(tài)
- if(listen(serverSocket,SOMAXCONN)==SOCKET_ERROR){
- cout<<"偵聽(tīng)失敗!";
- Sleep(3000);
- exit(-1);
- }
- //獲取服務(wù)器IP
- struct in_addr addr = getHostName();
- cout<<"Server "<<inet_ntoa(addr)<<" : "<<port<<" is listening......"<<endl;
- return serverSocket;
- }
- //接收客戶端連接
- SOCKET ReceiveConnect(SOCKET &serverSocket)
- {
- SOCKET clientSocket;//用來(lái)和客戶端通信的套接字
- struct sockaddr_in clientAddress;//用來(lái)和客戶端通信的套接字地址
- memset(&clientAddress,0,sizeof(clientAddress));//初始化存放客戶端信息的內(nèi)存
- 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;
- }
- //發(fā)送數(shù)據(jù)
- void SendMsg(SOCKET &clientSocket,FileLog &fileLog)
- {
- char buf[2048];
- while(1){
- cout<<"服務(wù)器說(shuō):";
- gets_s(buf);
- if(send(clientSocket,buf,strlen(buf),0)==SOCKET_ERROR){
- cout<<"發(fā)送數(shù)據(jù)失敗!"<<endl;
- Sleep(3000);
- exit(-1);
- }
- fileLog.Write("服務(wù)器 ").WriteLine(GetDate("%Y-%m-%d %H:%M:%S").c_str()).WriteLine(buf);//記錄聊天內(nèi)容
- }
- }
- int main(int argc, char* argv[]){
- WSADATA wsa;//WSADATA結(jié)構(gòu)被用來(lái)保存函數(shù)WSAStartup返回的Windows Sockets初始化信息
- //MAKEWORD(a,b)是將兩個(gè)byte型合并成一個(gè)word型,一個(gè)在高8位(b),一個(gè)在低8位(a)
- if(WSAStartup(MAKEWORD(2,2),&wsa)!=0){
- cout<<"套接字初始化失敗!";
- Sleep(3000);
- exit(-1);
- }
- SOCKET serverSocket=StartServer();//啟動(dòng)服務(wù)器
- SOCKET clientSocket=ReceiveConnect(serverSocket);//接收客服端的鏈接
- FileLog fileLog;
- fileLog.Open(GetDate("%Y%m%d").append(".log").c_str());//打開(kāi)記錄聊天內(nèi)容文件
- ReceiveStruct receiveStruct(&clientSocket,&fileLog);
- _beginthread(receive,0,&receiveStruct);//啟動(dòng)一個(gè)接收數(shù)據(jù)的線程
- SendMsg(clientSocket,fileLog);//發(fā)送數(shù)據(jù)
- fileLog.Close();//關(guān)閉文件
- closesocket(clientSocket);//關(guān)閉客戶端套接字(馬上發(fā)送FIN信號(hào),所有沒(méi)有接收到或是發(fā)送完成的數(shù)據(jù)都會(huì)丟失)
- closesocket(serverSocket);//關(guān)閉服務(wù)器套接字
- //清理套接字占用的資源
- 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")
- //接收數(shù)據(jù)
- 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("接收數(shù)據(jù)失敗!\n");
- exit(-1);
- }
- buf[bytes]='\0';
- cout<<"服務(wù)器說(shuō):"<<buf<<endl;
- }
- }
- //獲取服務(wù)器IP
- unsigned long GetServerIP(void)
- {
- //把字符串的IP地址轉(zhuǎn)化為u_long
- char ipStr[20];
- //用第二個(gè)參數(shù)填充***個(gè)參數(shù)所指的內(nèi)存,填充的長(zhǎng)度為第三個(gè)參數(shù)的大小
- memset(ipStr,0,sizeof(ipStr));
- cout<<"請(qǐng)輸入你要鏈接的服務(wù)器IP:";
- cin>>ipStr;
- unsigned long ip;
- if((ip=inet_addr(ipStr))==INADDR_NONE){
- cout<<"不合法的IP地址:";
- Sleep(3000);
- exit(-1);
- }
- return ip;
- }
- //鏈接服務(wù)器
- void Connect(SOCKET &sock)
- {
- unsigned long ip=GetServerIP();
- //把端口號(hào)轉(zhuǎn)化成整數(shù)
- 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);
- //建立和服務(wù)器的連接
- if(connect(sock,(sockaddr*)&serverAddress,sizeof(serverAddress))==SOCKET_ERROR){
- cout<<"建立連接失敗:"<<WSAGetLastError();
- Sleep(3000);
- exit(-1);
- }
- }
- //發(fā)送數(shù)據(jù)
- void SendMsg(SOCKET &sock)
- {
- char buf[2048];
- while(1){
- //從控制臺(tái)讀取一行數(shù)據(jù)
- gets_s(buf);
- cout<<"我說(shuō):";
- //發(fā)送給服務(wù)器
- if(send(sock,buf,strlen(buf),0)==SOCKET_ERROR){
- cout<<"發(fā)送數(shù)據(jù)失敗!";
- exit(-1);
- }
- }
- }
- int main(int argc, char* argv[]){
- WSADATA wsa;
- //初始化套接字DLL
- if(WSAStartup(MAKEWORD(2,2),&wsa)!=0){
- cout<<"套接字初始化失敗!";
- Sleep(3000);
- exit(-1);
- }
- //創(chuàng)建套接字
- SOCKET sock;
- if((sock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))==INVALID_SOCKET){
- cout<<"創(chuàng)建套接字失敗!";
- exit(-1);
- }
- Connect(sock);//鏈接服務(wù)器
- _beginthread(Receive,0,&sock);//啟動(dòng)接收數(shù)據(jù)線程
- SendMsg(sock);//發(fā)送數(shù)據(jù)
- //清理套接字占用的資源
- 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);// 進(jìn)入臨界區(qū)
- }
- void UnLock()
- {
- LeaveCriticalSection(&cs);//離開(kāi)臨界區(qū)
- }
- public:
- FileLog()
- {
- InitializeCriticalSection(&cs);//初始化臨界區(qū)
- fileHandle=INVALID_HANDLE_VALUE;//先初始化為錯(cuò)誤的句柄
- }
- ~FileLog()
- {
- if(fileHandle!=INVALID_HANDLE_VALUE)
- {
- //CloseHandle的功能是關(guān)閉一個(gè)打開(kāi)的對(duì)象句柄,該對(duì)象句柄可以是線程句柄,也可以是進(jìn)程、信號(hào)量等其他內(nèi)核對(duì)象的句柄
- CloseHandle(fileHandle);
- }
- DeleteCriticalSection(&cs);//刪除臨界區(qū)
- }
- BOOL Open(const char *fileName);//打開(kāi)文件
- FileLog& Write(const char *content);//向文件中寫入內(nèi)容
- FileLog& WriteLine(const char *content);//向文件中寫入內(nèi)容
- BOOL Read(char *buf,int size);//讀文件內(nèi)容
- BOOL Close();//關(guān)閉文件
- };
文件操作代碼(FileLog.app):
- #include "stdafx.h"
- #include "FileLog.h"
- //打開(kāi)文件
- 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;
- }
- //寫文件 返回當(dāng)前對(duì)象的引用,實(shí)現(xiàn)連接操作
- FileLog& FileLog::Write(const char *content)
- {
- Lock();
- if(fileHandle!=INVALID_HANDLE_VALUE)
- {
- DWORD dwSize=0;
- WriteFile(fileHandle,content,strlen(content),&dwSize,NULL);//寫
- }
- //開(kāi)始的時(shí)候少寫了這句,由于加的鎖沒(méi)有釋放,一個(gè)線程占用之后,導(dǎo)致其他線程只能一直等待,好久都沒(méi)有找到原因。
- 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");
- }
- //讀文件內(nèi)容
- 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;
- }
- //關(guān)閉文件
- 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/
【編輯推薦】