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, ...
随机推荐
- C#+ArcEngine中com对象的释放问题
1.问题描述 最近在写C#下AE的开发,在循环获取数据并修改时碰到了两个问题"超出系统资源"和"超出打开游标最大数":在网上看了一些资料,发现都是说在循环中没有 ...
- servlet就实现在线用户表
在学习servlet的过程中,学习了如何用servlet实现在线用户表. 只有服务器处于开机状态才会有在线用户表的存在,在服务器关机的情况下自然就不存在在线用户表的说法:所以,楼主认为在线用户表的信息 ...
- 路由页面缓存开启 以及 keep-alive 给你埋下的坑
为什么要用keep-alive呢, 因为这个会缓存dom模板, 下次再回到这个页面, 就可以利用这个已经渲染好的dom结构了, 如果数据不一样, 也会启用 virtualDoM 进行diff更新, 这 ...
- windows和Ubuntu双系统的安装和基本操作
因为最近刚开始学习ubuntu系统,首先要安装系统,搜集了网上的资料觉得这篇博客特别有用 http://www.cnblogs.com/Duane/p/5424218.html,我也是按照里面的步骤 ...
- 【转】使用sinopia五步快速完成本地npm搭建
使用sinopia五步快速完成本地npm搭建 时间 2016-03-01 14:55:30 繁星UED 原文 http://ued.fanxing.com/shi-yong-sinopiawu-b ...
- 基于Debian系统配置Nginx环境的Node.js应用教程
Node.js,是当前比较流行的能够动态的快速响应内容的JavaScript框架,在有些环境下比我们使用的PHP应用都能够提高效率.目 前,Node.js可以与我们常用的Nginx.Apache等服务 ...
- weex 语法高亮
@1.ctrl+shift+p,调出控制命令面板,@2.输入pic,点击进入 @3.输入vue Syntax Highlight,等待下载 所有的sublime下载插件同理. vue Syntax H ...
- 设计模式 -- 责任链模式或者叫职责链模式(Chain of Responsibility(CoR))
什么是链?前后相连,一个连接下一个,其中包括多个节点,其实责任链模式也类似,他是多个对象之间相互关联,下一个对象的操作由上一个对象关联下来,上一个对象有个方法用于指向其他对象.职责链之间的前后关系是可 ...
- android自定义控件(理论知识学习 +自定义属性的讲解)
View树和UI界面架构图 UI界面架构图: android视图最外层是一个window对象. phoneWindow来实现. phoneWindow将一个decorView作为整个布局的根vie ...
- hdu1021
#include <stdio.h> int fib(int m){ int n_2=1,n_1=2,n,i; if(m==0)return 1; if(m==1)return 2; fo ...