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头文件

 1 #pragma once
 2
 3 #ifndef WHG_LISTEN
 4 #define WHG_LISTEN
 5
 6 #include "Singleton.h"
 7
 8 class CListen
 9 {
10 public:
11     CListen(void);
12     ~CListen(void);
13     //类入口点,会创建监听线程
14     void SetAddress(unsigned short port,std::string ip="");
15     //用于线程访问关闭socket,并为CListen记录关闭信息
16     void DeleteLink(SOCKET s);
17 private:
18     //线程和CListen类共享的任务信息
19     struct  ThreadSocketInfo
20     {
21         CWinThread* pWt;
22         SOCKET*        pS;
23         CListen*    pListen;
24         ThreadSocketInfo()
25         {
26             pListen = NULL;
27             pWt = NULL;
28             pS = NULL;
29         }
30         ~ThreadSocketInfo()
31         {
32             pListen = NULL;
33             pWt = NULL;
34             pS = NULL;
35         }
36     };
37     //监听socket
38     SOCKET            listensocket;
39     //监听线程指针
40     CWinThread*        m_pListenThread;
41     //任务信息列表
42     std::vector<ThreadSocketInfo>    vec_WorkThreads;
43     //当前监听ip和端口
44     unsigned short    m_port;
45     std::string        m_ip;
46     //线程互斥访问锁
47     CThreadLockCs    m_tlcs;
48
49 private:
50     //监听线程工作对象
51     static UINT AFX_CDECL ListenThread(LPVOID);
52     //对客户端线程工作对象
53     static UINT AFX_CDECL WorkThread(LPVOID);
54     //bind、listen、accept实现
55     void Address(unsigned short port,std::string ip="");
56 };
57
58 //申明singleton的监听线程访问对象,全局唯一实例
59 typedef CSingleton<CListen>    LISTEN;
60
61 #endif

Listen.cpp:

  1 #include "StdAfx.h"
  2 #include "Listen.h"
  3
  4 CListen::CListen(void)
  5 :m_pListenThread(NULL)
  6 {
  7     listensocket = 0;
  8     WSAData wsa;
  9     if (WSAStartup(MAKEWORD(1,1),&wsa) != 0)
 10     {
 11         cout<<"WSAStartup "<<endl;
 12         WSACleanup();
 13     }
 14     else
 15     {
 16         listensocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
 17         if (listensocket == INVALID_SOCKET)
 18         {
 19             cout<<"socket() error,error code "<<WSAGetLastError()<<endl;
 20         }
 21     }
 22 }
 23
 24 CListen::~CListen(void)
 25 {
 26     //要先关闭对客户端连接,再关闭监听socket
 27     if (vec_WorkThreads.size() > 0)
 28     {
 29         std::vector<ThreadSocketInfo>::iterator iter = vec_WorkThreads.begin();
 30         for (;iter!=vec_WorkThreads.end();iter++)
 31         {
 32             SOCKET* s = (*iter).pS;
 33             if (s)
 34             {
 35                 closesocket(*s);
 36                 delete s;
 37                 (*iter).pS = NULL;
 38             }
 39             CWinThread* pwt = (*iter).pWt;
 40             if (pwt)
 41             {
 42                 WaitForSingleObject(pwt->m_hThread,INFINITE);
 43                 delete pwt;
 44                 (*iter).pWt = NULL;
 45             }
 46         }
 47     }
 48     if (listensocket)
 49     {
 50         closesocket(listensocket);
 51     }
 52     WSACleanup();
 53     cout<<"WSACleanup "<<endl;
 54
 55     if (m_pListenThread)
 56     {
 57         WaitForSingleObject(m_pListenThread->m_hThread,INFINITE);
 58         delete m_pListenThread;
 59     }
 60     vec_WorkThreads.clear();
 61 }
 62
 63 //监听线程工作对象
 64 UINT CListen::ListenThread(LPVOID param)
 65 {
 66     CListen* p = (CListen*)param;
 67     if (p)
 68     {
 69         p->Address(p->m_port,p->m_ip);
 70     }
 71     return 0;
 72 }
 73
 74 //对客户端线程工作对象
 75 UINT CListen::WorkThread(LPVOID param)
 76 {
 77     ThreadSocketInfo* tsi = (ThreadSocketInfo*)param;
 78     SOCKET acp = *(tsi->pS);
 79     CListen* pListen = tsi->pListen;
 80     delete tsi;
 81
 82     do
 83     {
 84         char buf[1024];
 85         int len = recv(acp,buf,1024,0);
 86         if (len == 0)
 87         {
 88             cout<<"connection has been closed "<<endl;
 89             break;
 90         }
 91         else if (len == SOCKET_ERROR)
 92         {
 93             cout<<"recv error,error code "<<WSAGetLastError()<<endl;
 94             break;
 95         }
 96         else
 97         {
 98             char* outbuf = new char[len+1];
 99             memcpy(outbuf,buf,len);
100             outbuf[len] = 0;
101             cout<<"recv data,"<<outbuf<<endl;
102             delete outbuf;
103         }
104     } while (1);
105     //删除当前连接记录
106     pListen->DeleteLink(acp);
107     return 0;
108 }
109
110 //用于线程访问关闭socket,并为CListen记录关闭信息
111 void CListen::DeleteLink(SOCKET s)
112 {
113     m_tlcs.lock();
114     std::vector<ThreadSocketInfo>::iterator iter = vec_WorkThreads.begin();
115     for (;iter!=vec_WorkThreads.end();iter++)
116     {
117         SOCKET* ss = (*iter).pS;
118         if (ss && *ss == s)
119         {
120             closesocket(s);
121             delete ss;
122             iter->pS = NULL;
123         }
124     }
125     m_tlcs.unlock();
126 }
127
128 //类入口点,会创建监听线程
129 void CListen::SetAddress(unsigned short port,std::string ip)
130 {
131     //监听线程只允许启动一次
132     if (m_pListenThread == NULL)
133     {
134         m_ip    = ip;
135         m_port    = port;
136         m_pListenThread = AfxBeginThread(ListenThread,this,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED,NULL);
137         if (m_pListenThread)
138         {
139             m_pListenThread->m_bAutoDelete = FALSE ;
140             m_pListenThread->ResumeThread();
141         }
142     }
143 }
144
145 //bind、listen、accept实现
146 void CListen::Address(unsigned short port,std::string ip)
147 {
148     sockaddr_in service;
149     service.sin_family = AF_INET;
150     service.sin_port = htons(port);
151     if (ip.empty())
152     {
153         service.sin_addr.s_addr = INADDR_ANY;
154     }
155     else
156     {
157         service.sin_addr.s_addr = inet_addr(ip.c_str());
158     }
159
160     if (bind(listensocket,(sockaddr*)&service,sizeof(service)) == SOCKET_ERROR)
161     {
162         cout<<"bind() error,error code "<<WSAGetLastError()<<endl;
163         return;
164     }
165     cout<<"bind "<<endl;
166
167     if (listen(listensocket,SOMAXCONN) == SOCKET_ERROR)
168     {
169         cout<<"listen() error,error code "<<WSAGetLastError()<<endl;
170         return;
171     }
172     cout<<"listen "<<endl;
173
174     while (1)
175     {
176         sockaddr_in recvLinkAddr;
177         int recvAddr = sizeof(recvLinkAddr);
178         SOCKET acp     = accept(listensocket,(sockaddr*)&recvLinkAddr,&recvAddr);
179         if (acp == INVALID_SOCKET)
180         {
181             cout<<"accept error,error code "<<WSAGetLastError()<<endl;
182             return;
183         }
184         cout<<"获取新的连接请求,ip:"<<inet_ntoa(recvLinkAddr.sin_addr)<<",port:"<<recvLinkAddr.sin_port<<endl;
185
186         SOCKET* s = new SOCKET(acp);
187         ThreadSocketInfo* tsi = new ThreadSocketInfo;
188         tsi->pListen = this;
189         tsi->pS = s;
190         CWinThread* workthread = AfxBeginThread(WorkThread,tsi,THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED,NULL);
191         if (workthread)
192         {
193             workthread->m_bAutoDelete = FALSE;
194             workthread->ResumeThread();
195             tsi->pWt = workthread;
196             ThreadSocketInfo t = *tsi;
197             m_tlcs.lock();
198             vec_WorkThreads.push_back(t);
199             m_tlcs.unlock();
200         }
201     }
202 }

客户端代码:

 1 // WinsockClient.cpp : Defines the entry point for the console application.
 2 //
 3
 4 #include "stdafx.h"
 5 int _tmain(int argc, _TCHAR* argv[])
 6 {
 7     cout<<"input id:";
 8     std::string str;
 9     cin>>str;
10
11     WSAData wsa;
12     if (WSAStartup(MAKEWORD(1,1),&wsa) != 0)
13     {
14         WSACleanup();
15         return 0;
16     }
17     SOCKET cnetsocket = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
18     do
19     {
20         if (cnetsocket == INVALID_SOCKET)
21             break;
22         sockaddr_in server;
23         server.sin_family = AF_INET;
24         server.sin_port = htons(8828);
25         server.sin_addr.s_addr = inet_addr("127.0.0.1");
26         if (connect(cnetsocket,(sockaddr*)&server,sizeof(server)) == SOCKET_ERROR)
27         {
28             break;
29         }
30         str += " : windows socket test!";
31         while (1)
32         {
33             int len = send(cnetsocket,str.c_str(),str.length(),0);
34             cout<<"send data:"<<str.c_str()<<" ,length = "<<str.length()<<endl;
35             if (len < str.length())
36             {
37                 cout<<"data send uncompleted"<<endl;
38                 str = str.substr(len+1,str.length());
39                 len = send(cnetsocket,str.c_str(),str.length(),0);
40                 cout<<"send data uncomplete,send remaining data :"<<str.c_str()<<" ,length = "<<str.length()<<endl;
41             }
42             else if (len == SOCKET_ERROR)
43             {
44                 break;
45             }
46             Sleep(5000);
47         }
48     } while (0);
49     closesocket(cnetsocket);
50     WSACleanup();
51
52     return 1;
53 }

main函数:

 1 // MultithreadServer.cpp : Defines the entry point for the console application.
 2 //
 3
 4 #include "stdafx.h"
 5 #include "MultithreadServer.h"
 6
 7 #include "Listen.h"
 8 #include "Singleton.h"
 9 #ifdef _DEBUG
10 #define new DEBUG_NEW
11 #endif
12 // The one and only application object
13 CWinApp theApp;
14 using namespace std;
15
16 int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
17 {
18     int nRetCode = 0;
19
20     // initialize MFC and print and error on failure
21     if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
22     {
23         // TODO: change error code to suit your needs
24         _tprintf(_T("Fatal Error: MFC initialization failed\n"));
25         nRetCode = 1;
26     }
27     LISTEN::Instance()->SetAddress(8828,"127.0.0.1");
28
29     int outId;
30     cin>>outId;
31     if (outId == 0)
32     {
33         LISTEN::Close();
34     }
35     return nRetCode;
36 }

测试结果:

1:4个客户端连接

2:客户端4关闭连接

3:输入0,关闭整个服务端,自动断开1.2.3的客户端

这里面涉及到几个错误代码,中文说明如下:

1:10054,远程主机强迫关闭了一个现有的连接。

2:10053,你的主机中的软件中止了一个已建立的连接。

3: 10004,一个封锁操作被对 WSACancelBlockingCall 的调用中断。

至此,程序正常结束。

时间: 2024-07-30 13:53:15

socket编程,简单多线程服务端测试程序的相关文章

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

客户端连接服务端之后, 服务端会生成与客户端交换信息的socket. 在服务端实现多线程: 为每个连接创建一个线程进行信息交换. import threading from socket import * from time import ctime HOST='127.0.0.1' PORT = 1111 BUFSIZE= 1024 ADDR = (HOST,PORT) tcpSerSock = socket(AF_INET, SOCK_STREAM) tcpSerSock.bind(ADDR

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

目标:实现客户端与服务端的socket通信,消息传输. 客户端 客户端代码: from socket import socket,AF_INET,SOCK_STREAM #服务端的ip地址 server_ip = '127.0.0.1' #服务端socket绑定的端口号 server_port = 20000 if __name__ == '__main__': while True: str_msg = input("请输入要发送信息:") if str_msg != "&

Unix Socket 编程:TCP服务端

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <arpa/inet.h> int main(int argc,char * argv[]) { int serv_sock,clnt_sock; char msg[] = "Hello SZT !~&q

《Linux多线程服务端编程——使用muduo C++网络库》学习笔记

第一章 线程安全的对象生命期管理 第二章 线程同步精要 第三章 多线程服务器的适用场合与常用编程模型 第四章 C++多线程系统编程精要 1.(P84)11个常用的最基本Pthreads函数: 2个:线程的创建和等待结束(join).封装为muduo::Thread 4个:mutex的创建.销毁.加锁.解锁.封装为muduo::MutexLock 5个:条件变量的创建.销毁.等待.通知.广播.muduo::Condition 2.(P85)不推荐使用读写锁的原因是它往往造成提高性能的错觉(允许多个

socket的客户端和服务端(Windows控制台程序)

首先是两个程序,分别是socket的客户端和服务端.(在windows系统vc6.0编译)服务器端:#include <Winsock2.h>#include <stdio.h>#pragma comment(lib,"WS2_32.lib")void main(){    WSADATA wsd;    SOCKET server;                                            //服务器socket    SOCKAD

GCDAynscSocket简单使用-服务端

距离上次写<GCDAynscSocket简单使用-服务端>差不多一个月了,现在把服务端的介绍给补上. 服务端的介绍比较简单,因为服务端和客户端的接收和发送数据的方法是一样的,不同的地方在于服务端是开启一个服务被动的等待客户端的接入.所以只介绍一下如何开启和关闭服务. 1.开启服务 GCDAynscSocket封装的非常好了,大部分情况下我们只要调用它的接口就可以了. 开启服务可调用的方法: /** * Tells the socket to begin listening and accept

转:实现一个简单的服务端推送方案

原文来自于:http://blog.csdn.net/jiao_fuyou/article/details/17090355 客户端和服务端的交互有推和拉两种方式:如果是客户端拉的话,通常就是Polling:如果是服务端推的话,一般就是Comet,目前比较流行的Comet实现方式是Long Polling. 注:如果不清楚相关名词含义,可以参考:Browser 與 Server 持續同步的作法介紹. 先来看看Polling,它其实就是我们平常所说的轮询,大致如下所示: Polling 因为服务端

Netty实例-简单的服务端-客户端实现,注释详细

       书籍推荐:                                       实例代码 :http://download.csdn.net/detail/jiangtao_st/7677503 Netty Server端实现 /** * * <p> * Netty Server Simple * </p> * * @author 卓轩 * @创建时间:2014年7月7日 * @version: V1.0 */ public class NettyServer

基于socket.io客户端与服务端的相互通讯

socket.io是对websocket的封装,用于客户端与服务端的相互通讯.官网:https://socket.io/. 下面是socket.io的用法: 1.由于使用express开的本地服务,先下载相关依赖 cnpm install express socket.io 2.服务端代码 const express = require("express"); const io = require("socket.io"); const app = express(