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, ...
随机推荐
- ToString() 格式化
c# ToString() 格式化字符串 格式化数值:有时,我们可能需要将数值以一定的格式来呈现,就需要对数值进行格式化.我们使用格式字符串指定格式.格式字符串采用以下形式:Axx,其中 A 为格式 ...
- 关于Java配置文件properties的学习
在Java早期的开发中,常用*.properties文件存储一些配置信息.其文件中的信息主要是以key=value的方式进行存储,在早期受到广泛的应用.而后随着xml使用的广泛,其位置渐渐被取代,不过 ...
- 手工杀毒辅助软件(PC Hunter) V1.51 免费绿色版
软件名称: 手工杀毒辅助软件(PC Hunter) 软件语言: 简体中文 授权方式: 免费软件 运行环境: Win 32位/64位 软件大小: 4.7MB 图片预览: 软件简介: PC Hunter是 ...
- reposync 同步yum仓库
reposync 同步远程yum仓库到本地 OPTIONS -h, --help 显示帮助 -c CONFIG, --config=CONFIG ...
- Sql Server中三种字符串合并方法的性能比较
文章来自:博客园-DotNet菜园 最近正在处理一个合并字符吕的存储过程,在一个测试系统的开发中,要使用到字符串合并功能,直接在Sql中做.示例:有表內容﹕名称 內容1 abc1 ...
- 1.3 ODPS
来源(完全照搬.仅做记录):http://blog.itpub.net/26613085/viewspace-1327313/ 注册odps账号注册地址:http://www.aliyun.com/p ...
- Debian搭建PPTPD
先安装pptpd: apt-get install pptpd 编辑 /etc/pptpd.conf #取消下面两行的注释,在文件的底部. # localip 192.168.0.1 # remote ...
- Android Studio环境下代码混淆+签名打包
Android Studio环境下代码混淆+签名打包 作者 Mr_冯先生 关注 2016.08.21 01:10 字数 1040 阅读 734评论 5喜欢 34 注:本文使用的Android Stud ...
- C#抽象类与接口的区别
一.抽象类:抽象类是特殊的类,只是不能被实例化(可以用派生类实例化基类对象):除此以外,具有类的其他特性:重要的是抽象类可以包括抽象方法(当然它可以有普通方法),这是普通类所不能的.抽象方法只能声明于 ...
- Merge into的使用详解-你Merge了没有【转】
Merge是一个非常有用的功能,类似于Mysql里的insert into on duplicate key. Oracle在9i引入了merge命令, 通过这个merge你能够在一个SQL语句中对一 ...