C++一個網絡編程實例
學習C++已經有一段時間了,一直都是學習基礎的東西,每次寫的代碼都比較少,沒有明確的學習目標,基礎還是基礎,漫無邊際的,基本上都是做一道或者幾道算法題,連一個小小的實戰(zhàn)都沒有,也不知道自己學得怎么樣了,現(xiàn)在終于有一個小小的實戰(zhàn)了《C++ 一個網絡編程實例》。由于自己一直在做C#,只能業(yè)余時間學習C++,都說C++ 是那么的難,暫時還沒有感覺到有多難,畢竟寫代碼也有兩年多了。我要學習多久才能進一家做C++研發(fā)的公司呢?
相信在不遠處有一家C++研發(fā)公司在等著我。
這只是一個小小的實例,包括Socket編程、多線程、文件操作。
簡單介紹:他實現(xiàn)了點對點聊天,一個服務器,一個客戶端,主線程用來發(fā)送數(shù)據(jù),啟動一個子線程用來接收數(shù)據(jù),服務器記錄聊天內容。他只是用上了上面所說的三個技術,如果你對上面三個技術不是很熟,或許對你有點幫助,如果你很熟,既然來了希望你能指導一下我,如果你是高手希望你能指導一下我的編碼問題。我太渴望寫出高效簡潔的代碼。
廢話就少說了,程序里處處都是注釋,你可以選擇看看我的代碼,或是選擇直接運行看看。
服務器代碼:
- // 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")
- //多線程調用的方法只有一個指針型的參數(shù),有時候需要多個參數(shù),所以定義一個結構,參數(shù)作為結構的字段
- 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();//終止當前線程
- }
- 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);
- }
- //從主機名數(shù)據(jù)庫中得到對應的“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)
- {
- //創(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;
- //初始化指定的內存區(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);
- }
- //進入偵聽狀態(tài)
- 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<<"接受客戶端連接失?。?quot;;
- 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<<"服務器說:";
- gets_s(buf);
- if(send(clientSocket,buf,strlen(buf),0)==SOCKET_ERROR){
- cout<<"發(fā)送數(shù)據(jù)失??!"<<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結構被用來保存函數(shù)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);//啟動一個接收數(shù)據(jù)的線程
- SendMsg(clientSocket,fileLog);//發(fā)送數(shù)據(jù)
- fileLog.Close();//關閉文件
- closesocket(clientSocket);//關閉客戶端套接字(馬上發(fā)送FIN信號,所有沒有接收到或是發(fā)送完成的數(shù)據(jù)都會丟失)
- 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")
- //接收數(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<<"服務器說:"<<buf<<endl;
- }
- }
- //獲取服務器IP
- unsigned long GetServerIP(void)
- {
- //把字符串的IP地址轉化為u_long
- char ipStr[20];
- //用第二個參數(shù)填充***個參數(shù)所指的內存,填充的長度為第三個參數(shù)的大小
- 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();
- //把端口號轉化成整數(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);
- //建立和服務器的連接
- 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){
- //從控制臺讀取一行數(shù)據(jù)
- gets_s(buf);
- cout<<"我說:";
- //發(fā)送給服務器
- 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)建套接字失?。?quot;;
- exit(-1);
- }
- Connect(sock);//鏈接服務器
- _beginthread(Receive,0,&sock);//啟動接收數(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);// 進入臨界區(qū)
- }
- void UnLock()
- {
- LeaveCriticalSection(&cs);//離開臨界區(qū)
- }
- public:
- FileLog()
- {
- InitializeCriticalSection(&cs);//初始化臨界區(qū)
- fileHandle=INVALID_HANDLE_VALUE;//先初始化為錯誤的句柄
- }
- ~FileLog()
- {
- if(fileHandle!=INVALID_HANDLE_VALUE)
- {
- //CloseHandle的功能是關閉一個打開的對象句柄,該對象句柄可以是線程句柄,也可以是進程、信號量等其他內核對象的句柄
- CloseHandle(fileHandle);
- }
- DeleteCriticalSection(&cs);//刪除臨界區(qū)
- }
- 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;
- }
- //寫文件 返回當前對象的引用,實現(xiàn)連接操作
- 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/
【編輯推薦】