C++網絡編程(一)

學習C++已經有一段時間了,一直都是學習基礎的東西,每次寫的代碼都比較少,沒有明確的學習目標,基礎仍是基礎,漫無邊際的,基本上都是作一道或者幾道算法題,連一個小小的實戰都沒有,也不知道本身學得怎麼樣了,如今終於有一個小小的實戰了《C++ 一個網絡編程實例》。因爲本身一直在作C#,只能業餘時間學習C++,都說C++ 是那麼的難,暫時尚未感受到有多難,畢竟寫代碼也有兩年多了。我要學習多久才能進一家作C++研發的公司呢?ios

相信在不遠處有一家C++研發公司在等着我。算法

這只是一個小小的實例,包括Socket編程、多線程、文件操做。數據庫

簡單介紹:他實現了點對點聊天,一個服務器,一個客戶端,主線程用來發送數據,啓動一個子線程用來接收數據,服務器記錄聊天內容。他只是用上了上面所說的三個技術,若是你對上面三個技術不是很熟,或許對你有點幫助,若是你很熟,既然來了但願你能指導一下我,若是你是高手但願你能指導一下個人編碼問題。我太渴望寫出高效簡潔的代碼。編程

 廢話就少說了,程序裏到處都是註釋,你能夠選擇看看個人代碼,或是選擇直接運行看看,《源碼下載》。windows

 

服務器代碼:服務器

   

 
// 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):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;} 
相關文章
相關標籤/搜索