GJM :C++ 网络编程 [转载]
这只是一个小小的实例,包括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/
GJM :C++ 网络编程 [转载]的更多相关文章
- linux下c的网络编程---转载
1.tcp协议
- Linux网络编程入门 (转载)
(一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...
- (转载)完成端口(CompletionPort)详解 - 手把手教你玩转网络编程系列之三
转自:http://blog.csdn.net/piggyxp/article/details/6922277 前 言 本系列里完成端口的代码在两年前就已经写好了,但是由于许久没有写东西了,不知该如何 ...
- [转载] 读《UNIX网络编程 卷1:套接字联网API》
原文: http://cstdlib.com/tech/2014/10/09/read-unix-network-programming-1/ 文章写的很清楚, 适合初学者 最近看了<UNIX网 ...
- Java 网络编程最佳实践(转载)
http://yihongwei.com/2015/09/remoting-practice/ Java 网络编程最佳实践 Sep 10, 2015 | [Java, Network] 1. 通信层 ...
- TCP与UDP在socket编程中的区别 (网络收集转载)
http://blog.chinaunix.net/uid-26421509-id-3814684.html 一.TCP与UDP的区别 基于连接与无连接 对系统资源的要求(TCP较多,UDP少) ...
- 【转载】Java 网络编程
本文主要是自己在网络编程方面的学习总结,先主要介绍计算机网络方面的相关内容,包括计算机网络基础,OSI参考模型,TCP/IP协议簇,常见的网络协议等等,在此基础上,介绍Java中的网络编程. 一. ...
- UNIX网络编程(转载)
1.1 客户端程序和服务端程序 网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 网络程序是先有服务器程序启动,等待客户端的程序运行并建立连接.一般的来说是服务端 ...
- 【转载】[基础知识]【网络编程】TCP/IP
转自http://mc.dfrobot.com.cn/forum.php?mod=viewthread&tid=27043 [基础知识][网络编程]TCP/IP iooops 胖友们楼主我又 ...
随机推荐
- ASP.NET MVC 4 Optimization的JS/CSS文件动态合并及压缩
JS/CSS文件的打包合并(Bundling)及压缩(Minification)是指将多个JS或CSS文件打包合并成一个文件,并在网站发布之后进行压缩,从而减少HTTP请求次数,提高网络加载速度和页面 ...
- 使用Google产品以来遇到的最糟糕、最霸道、最让人抓狂的设计
很久没有登录cnblogs@gmail.com这个邮箱,今天通过gmail.com登录了一下,登录后出现一个对话框要求设置性别与出生日期,而且必须要设置,不设置不让登录. 这个邮箱是我们网站用的是邮箱 ...
- Quartz Java resuming a job excecutes it many times--转
原文地址:http://stackoverflow.com/questions/1933676/quartz-java-resuming-a-job-excecutes-it-many-times Q ...
- CSS3 Animation Cheat Sheet:实用的 CSS3 动画库
CSS3 Animation Cheat Sheet 是一组预设的动画库,为您的 Web 项目添加各种很炫的动画.所有你需要做的是添加样式表到你的网站,为你想要添加动画效果的元素应用预制的 CSS 类 ...
- SQLServer学习笔记系列3
一.写在前面的话 今天又是双休啦!生活依然再继续,当你停下来的时候,或许会突然显得不自在.有时候,看到一种东西,你会发现原来在这个社会上,优秀的人很多,默默 吃苦努力奋斗的人也多!星期五早上按时上班, ...
- Windows Azure HandBook (5) Azure混合云解决方案
<Windows Azure Platform 系列文章目录> 在很多情况下,我们都会遇到本地私有云和公有云做互通互联的混合云场景.对于这种混合云的场景,微软的Windows Azure会 ...
- .net后台模拟浏览器get/post请求
#region 后台模拟浏览器get/post请求 /// <summary> /// 发送请求方式 /// </summary> /// <param name=&qu ...
- SQL Server代理(7/12):作业活动监视器
SQL Server代理是所有实时数据库的核心.代理有很多不明显的用法,因此系统的知识,对于开发人员还是DBA都是有用的.这系列文章会通俗介绍它的很多用法. 在这个系列的前几篇文章里,你创建配置了SQ ...
- ES6模块的import和export用法总结
ES6之前以前出现了js模块加载的方案,最主要的是CommonJS和AMD规范.commonjs前者主要应用于服务器,实现同步加载,如nodejs.AMD规范应用于浏览器,如requirejs,为异步 ...
- 【原创】Kakfa cluster包源代码分析
kafka.cluster包定义了Kafka的基本逻辑概念:broker.cluster.partition和replica——这些是最基本的概念.只有弄懂了这些概念,你才真正地使用kakfa来帮助完 ...