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编程,简单多线程服务端测试程序的更多相关文章

  1. TCP/IP网络编程之多线程服务端的实现(二)

    线程存在的问题和临界区 上一章TCP/IP网络编程之多线程服务端的实现(一)的thread4.c中,我们发现多线程对同一变量进行加减,最后的结果居然不是我们预料之内的.其实,如果多执行几次程序,会发现 ...

  2. socket 编程中。 服务端用到多线程

    客户端连接服务端之后, 服务端会生成与客户端交换信息的socket. 在服务端实现多线程: 为每个连接创建一个线程进行信息交换. import threading from socket import ...

  3. (C#:Socket)简单的服务端与客户端通信。

    要求:1.可以完成一对一的通信:2.实现服务端对客户端一对多的选择发送:3.可以实现服务端的群发功能:4.可以实现客户端文件的发送: 要点:服务器端:第一步:用指定的端口号和服务器的ip建立一个End ...

  4. Python socket编程客户端与服务端通信

    [本文出自天外归云的博客园] 目标:实现客户端与服务端的socket通信,消息传输. 客户端 客户端代码: from socket import socket,AF_INET,SOCK_STREAM ...

  5. TCP/IP网络编程之多线程服务端的实现(一)

    为什么引入线程 为了实现服务端并发处理客户端请求,我们介绍了多进程模型.select和epoll,这三种办法各有优缺点.创建(复制)进程的工作本身会给操作系统带来相当沉重的负担.而且,每个进程有独立的 ...

  6. 《Linux多线程服务端编程:使用muduo C++网络库》上市半年重印两次,总印数达到了9000册

    <Linux多线程服务端编程:使用muduo C++网络库>这本书自今年一月上市以来,半年之内已经重印两次(加上首印,一共是三次印刷),总印数达到了9000册,这在技术书里已经算是相当不错 ...

  7. Linux多线程服务端编程一些总结

    能接触这本书是因为上一个项目是用c++开发基于Linux的消息服务器,公司没有使用第三方的网络库,卷起袖子就开撸了.个人因为从业经验较短,主 要负责的是业务方面的编码.本着兴趣自己找了这本书.拿到书就 ...

  8. 《Linux多线程服务端编程》笔记——多线程服务器的适用场合

    如果要在一台多核机器上提供一种服务或执行一个任务,可用的模式有 运行一个单线程的进程 运行一个多线程的进程 运行多个单线程的进程 运行多个多线程的进程 这些模式之间的比较已经是老生常谈,简单地总结 模 ...

  9. winsock 编程(简单客户&服务端通信实现)

    winsock 编程(简单客户&服务端通信实现) 双向通信:Client send message to Server, and if  Server receive the message, ...

随机推荐

  1. Java线程小记

    线程是程序内部不同的执行分支,实现多线程主要有两种方式:继承自Thread类或实现Runnable接口,其实Thread类也是实现了Runnable接口,基于Java的单继承机制,我们建议使用实现Ru ...

  2. appnium框架以及源码研究

    android4.0后,google提供了uiautomator来进行自动化方案,appium在高版本android上就是基于这个,4.0下是基于selendroid. appium相当于一个中转站, ...

  3. 微信内置浏览器私有接口WeixinJSBridge介绍(转)

    这篇文章主要介绍了微信内置浏览器私有接口WeixinJSBridge介绍,本文讲解了发送给好友.分享函数.隐藏工具栏.隐藏三个点按钮等功能,需要的朋友可以参考下 微信网页进入,右上角有三个小点,没错, ...

  4. C++编程技巧(长期更新)

    1.数组使用 int* p = new int[5](); // 数组新建并全部初始化为0 等价于: int* p; p = new int[5](); int* q = new int[5];    ...

  5. Linux之top

    简介 top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器. top显示系统当前的进程和其他状况,是一个动态显示过程,即可以通过用户按 ...

  6. svn的基本配置及安装

    1.检查是否已安装 rpm -qa subversion 如果要卸载旧版本: yum remove subversion 2.安装 yum install subversion 3.检查是否安装成功 ...

  7. JSONObject处理java.util.Date

    JSONObject的内容为: {"userId":"A000004FFDCE14","userName":"好好干g" ...

  8. 使用JavaScript把页面上的表格导出为Excel文件

    如果在页面上展示了一个数据表格,而用户想把这个表格导出为Excel文件,那么在要求不高的情况下,可以不通过服务器生成表格,而是直接利用JavaScript的Blob和Object URL特性将表格导出 ...

  9. python并行任务之生产消费模式

    一. 生产者/消费者模式 概念:生产者产生一块数据,放到buffer中,与此同时,消费者在从buffer中取出并消耗这些数据 理解:像生活中厂家生产出产品,顾客购买消耗这些产品,buffer就是存放商 ...

  10. 去掉svn与文件之间 的关联

    今天在检出文件的时候,没注意检出目录 ,居然直接检出到D盘里了.然后就看到D盘上有个大大的绿勾,看起来很不舒服,想去掉. 自己看看小乌龟里好像没这功能,于是百度,一大堆都是要改和注册相关的东西,照着做 ...