這只是一個小小的實例,包括Socket編程、多線程、文件操作。
簡單介紹:他實現了點對點聊天,一個服務器,一個客戶端,主線程用來發送數據,啟動一個子線程用來接收數據,服務器記錄聊天內容。他只是用上了上面所說的三個技術,如果你對上面三個技術不是很熟,或許對你有點幫助,如果你很熟,既然來了希望你能指導一下我,如果你是高手希望你能指導一下我的編碼問題。我太渴望寫出高效簡潔的代碼。
廢話就少說了,程序裡處處都是注釋,你可以選擇看看我的代碼,或是選擇直接運行看看,《源碼下載》。
服務器代碼:
// 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):
#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;
}
作者:陳太漢
博客:http://www.cnblogs.com/hlxs/