socket编程,简单多线程服务端测试程序
socket编程,简单多线程服务端测试程序
前些天重温了MSDN关于socket编程的WSAStartup、WSACleanup、socket、closesocket、bind、listen、accept、recv、send等函数的介绍,今天写了一个CUI界面的测试程序(依赖MFC)作为补充。程序功能简介如下:
1:一个线程做监听用。
2:监听线程收到客户端连接后,创建新线程接收客户端数据。所有对客户端线程将加入容器,以便管理。
3:服务端打印所有客户端发来的信息。
4:服务端CUI界面输入数字0,将关闭所有连接线程,释放socket,并退出程序。
程序实现依赖类:
1:MFC
2:CSingleton模板,我关于singleton实现的文章中有源码。http://www.cnblogs.com/hgwang/p/6085922.html
3:CThreadLockCs,CRITICAL_SECTION封装类,我关于singleton实现的文章中有源码。http://www.cnblogs.com/hgwang/p/6085922.html。
4:windows socket库,我关于windows socket的文章中有介绍及调用方法,http://www.cnblogs.com/hgwang/p/6074038.html。
下面是该测试程序服务端封装类的源码:
Listen.h头文件
#pragma once #ifndef WHG_LISTEN
#define WHG_LISTEN #include "Singleton.h" class CListen
{
public:
CListen(void);
~CListen(void);
//类入口点,会创建监听线程
void SetAddress(unsigned short port,std::string ip="");
//用于线程访问关闭socket,并为CListen记录关闭信息
void DeleteLink(SOCKET s);
private:
//线程和CListen类共享的任务信息
struct ThreadSocketInfo
{
CWinThread* pWt;
SOCKET* pS;
CListen* pListen;
ThreadSocketInfo()
{
pListen = NULL;
pWt = NULL;
pS = NULL;
}
~ThreadSocketInfo()
{
pListen = NULL;
pWt = NULL;
pS = NULL;
}
};
//监听socket
SOCKET listensocket;
//监听线程指针
CWinThread* m_pListenThread;
//任务信息列表
std::vector<ThreadSocketInfo> vec_WorkThreads;
//当前监听ip和端口
unsigned short m_port;
std::string m_ip;
//线程互斥访问锁
CThreadLockCs m_tlcs; private:
//监听线程工作对象
static UINT AFX_CDECL ListenThread(LPVOID);
//对客户端线程工作对象
static UINT AFX_CDECL WorkThread(LPVOID);
//bind、listen、accept实现
void Address(unsigned short port,std::string ip="");
}; //申明singleton的监听线程访问对象,全局唯一实例
typedef CSingleton<CListen> LISTEN; #endif
Listen.cpp:
#include "StdAfx.h"
#include "Listen.h" CListen::CListen(void)
:m_pListenThread(NULL)
{
listensocket = ;
WSAData wsa;
if (WSAStartup(MAKEWORD(,),&wsa) != )
{
cout<<"WSAStartup "<<endl;
WSACleanup();
}
else
{
listensocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if (listensocket == INVALID_SOCKET)
{
cout<<"socket() error,error code "<<WSAGetLastError()<<endl;
}
}
} CListen::~CListen(void)
{
//要先关闭对客户端连接,再关闭监听socket
if (vec_WorkThreads.size() > )
{
std::vector<ThreadSocketInfo>::iterator iter = vec_WorkThreads.begin();
for (;iter!=vec_WorkThreads.end();iter++)
{
SOCKET* s = (*iter).pS;
if (s)
{
closesocket(*s);
delete s;
(*iter).pS = NULL;
}
CWinThread* pwt = (*iter).pWt;
if (pwt)
{
WaitForSingleObject(pwt->m_hThread,INFINITE);
delete pwt;
(*iter).pWt = NULL;
}
}
}
if (listensocket)
{
closesocket(listensocket);
}
WSACleanup();
cout<<"WSACleanup "<<endl; if (m_pListenThread)
{
WaitForSingleObject(m_pListenThread->m_hThread,INFINITE);
delete m_pListenThread;
}
vec_WorkThreads.clear();
} //监听线程工作对象
UINT CListen::ListenThread(LPVOID param)
{
CListen* p = (CListen*)param;
if (p)
{
p->Address(p->m_port,p->m_ip);
}
return ;
} //对客户端线程工作对象
UINT CListen::WorkThread(LPVOID param)
{
ThreadSocketInfo* tsi = (ThreadSocketInfo*)param;
SOCKET acp = *(tsi->pS);
CListen* pListen = tsi->pListen;
delete tsi; do
{
char buf[];
int len = recv(acp,buf,,);
if (len == )
{
cout<<"connection has been closed "<<endl;
break;
}
else if (len == SOCKET_ERROR)
{
cout<<"recv error,error code "<<WSAGetLastError()<<endl;
break;
}
else
{
char* outbuf = new char[len+];
memcpy(outbuf,buf,len);
outbuf[len] = ;
cout<<"recv data,"<<outbuf<<endl;
delete outbuf;
}
} while ();
//删除当前连接记录
pListen->DeleteLink(acp);
return ;
} //用于线程访问关闭socket,并为CListen记录关闭信息
void CListen::DeleteLink(SOCKET s)
{
m_tlcs.lock();
std::vector<ThreadSocketInfo>::iterator iter = vec_WorkThreads.begin();
for (;iter!=vec_WorkThreads.end();iter++)
{
SOCKET* ss = (*iter).pS;
if (ss && *ss == s)
{
closesocket(s);
delete ss;
iter->pS = NULL;
}
}
m_tlcs.unlock();
} //类入口点,会创建监听线程
void CListen::SetAddress(unsigned short port,std::string ip)
{
//监听线程只允许启动一次
if (m_pListenThread == NULL)
{
m_ip = ip;
m_port = port;
m_pListenThread = AfxBeginThread(ListenThread,this,THREAD_PRIORITY_NORMAL,,CREATE_SUSPENDED,NULL);
if (m_pListenThread)
{
m_pListenThread->m_bAutoDelete = FALSE ;
m_pListenThread->ResumeThread();
}
}
} //bind、listen、accept实现
void CListen::Address(unsigned short port,std::string ip)
{
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_port = htons(port);
if (ip.empty())
{
service.sin_addr.s_addr = INADDR_ANY;
}
else
{
service.sin_addr.s_addr = inet_addr(ip.c_str());
} if (bind(listensocket,(sockaddr*)&service,sizeof(service)) == SOCKET_ERROR)
{
cout<<"bind() error,error code "<<WSAGetLastError()<<endl;
return;
}
cout<<"bind "<<endl; if (listen(listensocket,SOMAXCONN) == SOCKET_ERROR)
{
cout<<"listen() error,error code "<<WSAGetLastError()<<endl;
return;
}
cout<<"listen "<<endl; while ()
{
sockaddr_in recvLinkAddr;
int recvAddr = sizeof(recvLinkAddr);
SOCKET acp = accept(listensocket,(sockaddr*)&recvLinkAddr,&recvAddr);
if (acp == INVALID_SOCKET)
{
cout<<"accept error,error code "<<WSAGetLastError()<<endl;
return;
}
cout<<"获取新的连接请求,ip:"<<inet_ntoa(recvLinkAddr.sin_addr)<<",port:"<<recvLinkAddr.sin_port<<endl; SOCKET* s = new SOCKET(acp);
ThreadSocketInfo* tsi = new ThreadSocketInfo;
tsi->pListen = this;
tsi->pS = s;
CWinThread* workthread = AfxBeginThread(WorkThread,tsi,THREAD_PRIORITY_NORMAL,,CREATE_SUSPENDED,NULL);
if (workthread)
{
workthread->m_bAutoDelete = FALSE;
workthread->ResumeThread();
tsi->pWt = workthread;
ThreadSocketInfo t = *tsi;
m_tlcs.lock();
vec_WorkThreads.push_back(t);
m_tlcs.unlock();
}
}
}
客户端代码:
// WinsockClient.cpp : Defines the entry point for the console application.
// #include "stdafx.h"
int _tmain(int argc, _TCHAR* argv[])
{
cout<<"input id:";
std::string str;
cin>>str; WSAData wsa;
if (WSAStartup(MAKEWORD(,),&wsa) != )
{
WSACleanup();
return ;
}
SOCKET cnetsocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
do
{
if (cnetsocket == INVALID_SOCKET)
break;
sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons();
server.sin_addr.s_addr = inet_addr("127.0.0.1");
if (connect(cnetsocket,(sockaddr*)&server,sizeof(server)) == SOCKET_ERROR)
{
break;
}
str += " : windows socket test!";
while ()
{
int len = send(cnetsocket,str.c_str(),str.length(),);
cout<<"send data:"<<str.c_str()<<" ,length = "<<str.length()<<endl;
if (len < str.length())
{
cout<<"data send uncompleted"<<endl;
str = str.substr(len+,str.length());
len = send(cnetsocket,str.c_str(),str.length(),);
cout<<"send data uncomplete,send remaining data :"<<str.c_str()<<" ,length = "<<str.length()<<endl;
}
else if (len == SOCKET_ERROR)
{
break;
}
Sleep();
}
} while ();
closesocket(cnetsocket);
WSACleanup(); return ;
}
main函数:
// MultithreadServer.cpp : Defines the entry point for the console application.
// #include "stdafx.h"
#include "MultithreadServer.h" #include "Listen.h"
#include "Singleton.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
// The one and only application object
CWinApp theApp;
using namespace std; int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
int nRetCode = ; // initialize MFC and print and error on failure
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), ))
{
// TODO: change error code to suit your needs
_tprintf(_T("Fatal Error: MFC initialization failed\n"));
nRetCode = ;
}
LISTEN::Instance()->SetAddress(,"127.0.0.1"); int outId;
cin>>outId;
if (outId == )
{
LISTEN::Close();
}
return nRetCode;
}
测试结果:
1:4个客户端连接
2:客户端4关闭连接
3:输入0,关闭整个服务端,自动断开1.2.3的客户端
这里面涉及到几个错误代码,中文说明如下:
1:10054,远程主机强迫关闭了一个现有的连接。
2:10053,你的主机中的软件中止了一个已建立的连接。
3: 10004,一个封锁操作被对 WSACancelBlockingCall 的调用中断。
至此,程序正常结束。
socket编程,简单多线程服务端测试程序的更多相关文章
- TCP/IP网络编程之多线程服务端的实现(二)
线程存在的问题和临界区 上一章TCP/IP网络编程之多线程服务端的实现(一)的thread4.c中,我们发现多线程对同一变量进行加减,最后的结果居然不是我们预料之内的.其实,如果多执行几次程序,会发现 ...
- socket 编程中。 服务端用到多线程
客户端连接服务端之后, 服务端会生成与客户端交换信息的socket. 在服务端实现多线程: 为每个连接创建一个线程进行信息交换. import threading from socket import ...
- (C#:Socket)简单的服务端与客户端通信。
要求:1.可以完成一对一的通信:2.实现服务端对客户端一对多的选择发送:3.可以实现服务端的群发功能:4.可以实现客户端文件的发送: 要点:服务器端:第一步:用指定的端口号和服务器的ip建立一个End ...
- Python socket编程客户端与服务端通信
[本文出自天外归云的博客园] 目标:实现客户端与服务端的socket通信,消息传输. 客户端 客户端代码: from socket import socket,AF_INET,SOCK_STREAM ...
- TCP/IP网络编程之多线程服务端的实现(一)
为什么引入线程 为了实现服务端并发处理客户端请求,我们介绍了多进程模型.select和epoll,这三种办法各有优缺点.创建(复制)进程的工作本身会给操作系统带来相当沉重的负担.而且,每个进程有独立的 ...
- 《Linux多线程服务端编程:使用muduo C++网络库》上市半年重印两次,总印数达到了9000册
<Linux多线程服务端编程:使用muduo C++网络库>这本书自今年一月上市以来,半年之内已经重印两次(加上首印,一共是三次印刷),总印数达到了9000册,这在技术书里已经算是相当不错 ...
- Linux多线程服务端编程一些总结
能接触这本书是因为上一个项目是用c++开发基于Linux的消息服务器,公司没有使用第三方的网络库,卷起袖子就开撸了.个人因为从业经验较短,主 要负责的是业务方面的编码.本着兴趣自己找了这本书.拿到书就 ...
- 《Linux多线程服务端编程》笔记——多线程服务器的适用场合
如果要在一台多核机器上提供一种服务或执行一个任务,可用的模式有 运行一个单线程的进程 运行一个多线程的进程 运行多个单线程的进程 运行多个多线程的进程 这些模式之间的比较已经是老生常谈,简单地总结 模 ...
- winsock 编程(简单客户&服务端通信实现)
winsock 编程(简单客户&服务端通信实现) 双向通信:Client send message to Server, and if Server receive the message, ...
随机推荐
- 用微软makecert.exe生成一个自签名的证书
RT makecert.exe不用去找,安装VS2008后,在开始菜单相应的路径找到该命令提示符:Microsoft Visual Studio 2008/Visual Studio Tools/Vi ...
- <密码的实现>输入密码的时候,显示“*”,而不是显示输入内容
一开始还以为用C语言和C++不能实现输入密码的时候显示出“*”而不显示输入的内容呢!没想到偶然的机会试出了用while循环结构可以实现.以下是我整理的C语言和C++的代码,供初学者参考. 这是C语言实 ...
- PHP22期基础班总结
11月7号,我们结束了为期17天的PHP基础班课程,最后一天的晚自习之后,马总问了我们的一个问题,基础班1000块钱的课程,我们认为是否值得这个价格?这其实是一个很好的问题. 2016年1000块钱能 ...
- Introduction to Programming Contests (stanford)
http://web.stanford.edu/class/cs9http://web.stanford.edu/class/cs97si/7si/
- 使用SQL Server临时表来实现字符串合并处理
处理的数据 CREATE TABLE tb(col1 varchar(10),col2 int) INSERT tb SELECT 'a',1 union ALL SELECT 'a',2 union ...
- 学习笔记(C++Primer)--易错点总结(Chapter2)
2.1.2Type Conversions(1/10/2017) 1.If we assign an out-of-range value to an object of unsigned type, ...
- yii2.0 面包屑的使用
yii2中面包屑是yii2自带的小部件,类似本网站的导航栏应该就是采用面包屑来完成的 例子如下,需要引入 yii\widgets\Breadcrumbs echo Breadcrumbs::widge ...
- cocos2d-js引擎学习笔记
cocos2d-js3.0实用语法 /*初始化继承类*/ var Enemy = cc.Sprite.extend({ hp: 0, fileName: "enemy.png", ...
- poi做Excel数据驱动,支持.xls和.xlsx格式的excel文档,比起jxl强大不少
import java.io.FileInputStream;import java.io.InputStream;import java.util.Iterator;import java.util ...
- dump报文转换为wrieshark报文
我们开发中经常会出原始的报文,如下所示: 45 00 01 3d 8e 6a 00 00 80 11 ab 46 00 00 00 00 ff ff ff ff 00 44 00 43 01 29 6 ...