VS 2019 MFC Socket 通讯例程服务器端Select一对多同步通信TCP/IP通信服务器端[一]

SOCKET概念:

1. 什么是socket
        TCP/IPTransmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的。
UDPUser Data Protocol,用户数据报协议)是与TCP相对应的协议。它是属于TCP/IP协议族中的一种。
        
socket可以看成是用户进程与内核网络协议栈的编程接口。TCP/IP协议的底层部分已经被内核实现了,而应用层是用户需要实现的,这部分程序工作在用户空间。用户空间的程序需要通过套接字来访问内核网络协议栈。套接口是全双工的通信,它不仅可以用于本机的进程间通信,还可以用于网络上不同主机的进程间通信。

2.Socket嵌套字类型

        1>流方套接字(SOCK_STREAM):它对应TCP协议,它提供面向连接的、可靠的数据传输服务,数据无差错、无重复的发送,且按发送顺序接收。
        2>数据报套接字(
SOCK_DGREAM):提供无连接服务。不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。
        3>原始套接字(
SOCK_RAW):它提供一种能力,让我们直接跨越传输层,直接对IP层进行数据封装,通过该套接字,我们可以直接将数据封装成IP层能够认识的协议格式。

技术要点:

        SOCKET socket( int af, int type, int protocol );

        int WSAStartup( WORD wVersionRequested, LPWSADATA lpWSAData );

        int bind( SOCKET s, const struct sockaddr FAR* name, int namelen );

        int bind( SOCKET s, const struct sockaddr FAR* name, int namelen );

        int connect( SOCKET s, const struct sockaddr FAR* name, int namelen );

        int listen( SOCKET s, int backlog );

        int recv( SOCKET s, char FAR* buf, int len, int flags );

        int send( SOCKET s, const char FAR* buf, int len, int flags );

        int select( int nfds, fd_set FAR* readfds, fd_set FAR* writefds, fd_set FAR* exceptfds, const struct timeval FAR* timeout );

参考文献:

https://blog.csdn.net/coder_xia/article/details/6702971?spm=1001.2014.3001.5506https://blog.csdn.net/coder_xia/article/details/6702971?spm=1001.2014.3001.5506

https://www.leavesongs.com/C/MFCSocket.htmlhttps://www.leavesongs.com/C/MFCSocket.html

字符转换

1、MultiByteToWideChar//多字节到宽字节

CString CMFCApplicationSocketDemoDlg::CharArryToCstring(char* CharText)
{
    int charLth = strlen(CharText);
    int len = MultiByteToWideChar(CP_ACP, 0, CharText, charLth, NULL, 0);
    TCHAR* buf = new TCHAR[len + 1];
    MultiByteToWideChar(CP_ACP, 0, CharText, charLth, buf, len);
    buf[len] = '\0'; 
    CString pWideChar;
    pWideChar.Append(buf);
    return pWideChar;
}

2、WideCharToMultiByte//宽字节到多字节

char* CMFCApplicationSocketDemoDlg::CstringToWideCharArry(CString CstrText)
{
    int lth = WideCharToMultiByte(CP_ACP,0, CstrText, CstrText.GetLength(),NULL,0,NULL,NULL);
    char* pStr = ( char*)malloc((lth + 1) * sizeof( char));
    memset(pStr, 0, (lth + 1) * sizeof( char));
    WideCharToMultiByte(CP_ACP, 0, CstrText.GetBuffer(), CstrText.GetLength(), (LPSTR)pStr, lth, NULL, NULL);
    *(pStr + lth+1) = '\0';
    printf("lth=%d", lth);
    printf("lth+1=%d", (lth+1)* sizeof(char));
    printf("LthStr=%d", sizeof(CString));
    printf("CharLen=%d", sizeof(char*));
    return  pStr;
}

3、CClientItemArray//自定义类用于存储连接客户端各项参数


class CClientItemArray
{
public:
    CString m_strIP;
    SOCKET  m_ClientSocket;
    HANDLE  m_hThread;
    CMFCApplicationSocketDemoDlg* m_pmainWnd;
    CClientItemArray()
    {
        m_ClientSocket = INVALID_SOCKET;
        m_hThread      = NULL;
        m_pmainWnd     = NULL;
    }
};

4、m_ClientArrayList//存储客户端和服务器的连接集合

CArraym_ClientArrayList;

5、SocketServerThreadFuc//连接客户端的服务线程

DWORD          WINAPI SocketServerThreadFuc(LPCVOID lparam);

6、SocketManagerThreadFuc//管理连接客户端的服务线程的管理线程    

static     UINT SocketManagerThreadFuc(LPVOID* pParam);

7、select模型//同步通信的关键

BOOL socket_Select(SOCKET hSocket, DWORD nTimeOut, BOOL bRead)
{
    FD_SET fdset;
    timeval tv;
    FD_ZERO(&fdset);
    FD_SET(hSocket,&fdset);
    nTimeOut = nTimeOut > 1000 ? 1000 : nTimeOut;
    tv.tv_sec = 0;
    tv.tv_usec = nTimeOut;
    int iRet = 0;
    if (bRead)
    {
        iRet = select(0, &fdset,NULL, NULL, &tv);
    }
    else
    {
        iRet = select(0, NULL, &fdset, NULL, &tv);
    }
    if (iRet <= 0)
    {
        return FALSE;
    }
    else if (FD_ISSET(hSocket, &fdset))
    {
        return TRUE;
    }
    return FALSE;
}

8.CSoceketInitConfig//Socket初始化

class CSoceketInitConfig
{
public:
    CSoceketInitConfig(BYTE minorVer = 2, BYTE majorVer = 2)
    {
        WORD    w_req = MAKEWORD(2, 2);
        WSADATA wsadata;
        int     erro;

        erro = WSAStartup(w_req, &wsadata);
        if (erro != 0)
        {
            printf("初始化嵌套字失败!\n");
            return;
        }
        else
        {
            printf("初始化嵌套字成功!\n");
        }

        if (LOBYTE(wsadata.wVersion) != 2 || LOBYTE(wsadata.wHighVersion) != 2)
        {
            printf("嵌套字版本号检测不符!\n");
            return;
        }
        else
        {
            printf("嵌套字版本号检测正确!\n");
        }
    }
};

MSDN对Socket的介绍

This function creates a socket that is bound to a specific service provider.

SOCKET socket(
  int af,
  int type,
  int protocol
);

Parameters

af

[in] Address family specification.

type

[in] Type specification for the new socket.

The following table shows the two type specifications supported for Winsock 1.1.

Type Description
SOCK_STREAM Provides sequenced, reliable, two-way, connection-based byte streams with an out of band (OOB) data transmission mechanism. Uses TCP for the Internet address family.
SOCK_DGRAM Supports datagrams, which are connectionless, unreliable buffers of a fixed (typically small) maximum length. Uses UDP for the Internet address family.
SOCK_RAW Supports raw sockets. Raw sockets allow an application to send and receive packets with customized headers.

In Winsock 2.2, many new socket types will be introduced and no longer need to be specified because an application can dynamically discover the attributes of each available transport protocol through the WSAEnumProtocols function. Socket type definitions appear in Winsock2.h, which will be periodically updated as new socket types, address families, and protocols are defined.

protocol

[in] Protocol to be used with the socket that is specific to the indicated address family. The following list shows the possible values.

  • IPPROTO_IP
  • IPPROTO_IPV6
  • IPPROTO_TCP
  • IPPROTO_UDP
  • SOL_SOCKET

For more information about these values, see Socket Options.

Return Values

If no error occurs, this function returns a descriptor referencing the new socket. If an error occurs, a value of INVALID_SOCKET is returned, and a specific error code can be retrieved by calling WSAGetLastError. The following table shows a list of possible error codes.

Error code Description
WSANOTINITIALISED A successful WSAStartup call must occur before using this function.
WSAENETDOWN The network subsystem or the associated service provider has failed.
WSAEAFNOSUPPORT The specified address family is not supported.
WSAEINPROGRESS A blocking Winsock call is in progress, or the service provider is still processing a callback function.
WSAEMFILE No more socket descriptors are available.
WSAENOBUFS No buffer space is available. The socket cannot be created.
WSAEPROTONOSUPPORT The specified protocol is not supported.
WSAEPROTOTYPE The specified protocol is the wrong type for this socket.
WSAESOCKTNOSUPPORT

The specified socket type is not supported in this address family.

4.WSAStartup

See Also

send | sendto | WSACleanup | WSAGetLastError

Requirements

OS Versions: Windows CE 1.0 and later.
Header: Winsock2.h.
Link Library: Ws2.lib.

This function initiates use of ws2.dll by a process.

int WSAStartup(
  WORD wVersionRequested,
  LPWSADATA lpWSAData
);

Parameters

wVersionRequested

[in] Highest version of Windows Sockets support that the caller can use. The high-order byte specifies the minor version (revision) number; the low-order byte specifies the major version number.

lpWSAData

[out] Pointer to the WSADATA structure that is to receive details of the Windows Sockets implementation.

Return Values

If no error occurs, this function returns zero. If an error occurs, it returns one of the error codes listed in the following table.

An application cannot call WSAGetLastError to determine the error code as is typically done in Windows Sockets if WSAStartup fails. ws2.dll will not have been loaded in the case of a failure so the client data area where the last error information is stored could not be established.

The following table shows a list of possible error codes.

Error code Description
WSASYSNOTREADY Indicates that the underlying network subsystem is not ready for network communication.
WSAVERNOTSUPPORTED The version of Windows Sockets support requested is not provided by this particular Windows Sockets implementation.
WSAEPROCLIM A limit on the number of tasks supported by the Windows Sockets implementation has been reached.
WSAEFAULT The lpWSAData parameter is not a valid pointer.

5.bind (Windows Sockets)

See Also

connect (Windows Sockets) | getsockname (Windows Sockets) | listen | setsockopt (Windows Sockets) | sockaddr | socket (Windows Sockets) | WSAGetLastError | WSAStartup

Requirements

OS Versions: Windows CE 1.0 and later.
Header: Winsock2.h.
Link Library: Ws2.lib.

This function associates a local address with a socket.

int bind(
  SOCKET s,
  const struct sockaddr FAR* name,
  int namelen
);

Parameters

s

[in] Descriptor identifying an unbound socket.

name

[in] Address to assign to the socket from the sockaddr structure.

namelen

[in] Length of the value in the name parameter.

Return Values

If no error occurs, this function returns zero. If an error occurs, it returns SOCKET_ERROR, and a specific error code can be retrieved by calling WSAGetLastError.

Remarks

This function is used on an unconnected socket before subsequent calls to the connect (Windows Sockets) or listen function. The bind function is used to bind to either connection-oriented (stream) or connectionless (datagram) sockets. When a socket is created with a call to the socket (Windows Sockets) function, it exists in a name space (address family) but has no name assigned to it. Use the bind function to establish the local association of the socket by assigning a local name to an unnamed socket.

A name consists of the three parts when using the Internet address family. The following list shows these parts:

  • The address family
  • A host address
  • A port number

In Winsock 2.2, the name parameter is not strictly interpreted as a pointer to a sockaddr structure. It is cast this way for Winsock 1.1 compatibility. Service providers are free to regard it as a pointer to a block of memory of size namelen. The first 2 bytes in this block (corresponding to the sa_family member of the sockaddr structure) must contain the address family that was used to create the socket. Otherwise, an error WSAEFAULT occurs.

If an application does not care what local address is assigned, specify the manifest constant value ADDR_ANY for the sa_data member of the name parameter. This allows the underlying service provider to use any appropriate network address, potentially simplifying application programming in the presence of multihomed hosts (that is, hosts that have more than one network interface and address).

For TCP/IP, if the port is specified as 0, the service provider assigns a unique port to the application with a value between 1024 and 5000. The application can use getsockname (Windows Sockets) after calling bind to learn the address and the port that has been assigned to it. If the Internet address is equal to INADDR_ANY, getsockname cannot necessarily supply the address until the socket is connected because several addresses can be valid if the host is multihomed. Binding to a specific port number other than port 0 is discouraged for client applications because there is a danger of conflicting with another socket already using that port number.

Notes for IrDA Sockets

  • The SOCKADDR_IRDA structure is used in the addr parameter.
  • There is no wildcard address equivalent to INADDR_ANY.
  • The Af_irda.h header file must be explicitly included.
  • Local names are not exposed in IrDA. IrDA client sockets therefore must never call the bind function before the connect (Windows Sockets) function. If the IrDA socket was previously bound to a service name using bind, the connect function will fail with SOCKET_ERROR.

For more inforamtion about IrDA support in Windows CE, see Infrared Communications.

The following table shows a list of possible error codes.

Error code Description
WSANOTINITIALISED A successful WSAStartup call must occur before using this function.
WSAENETDOWN The network subsystem has failed.
WSAEACCES An attempt was made to access a socket in a way forbidden by its access permissions. An example is using a broadcast address for sendto without broadcast permission being set using setsockopt (Windows Sockets).
WSAEADDRINUSE A process on the machine is already bound to the same fully qualified address and the socket has not been marked to allow address reuse with SO_REUSEADDR. For example, the IP address and port are bound in the af_inet case. (See the SO_REUSEADDR socket option under setsockopt (Windows Sockets).)
WSAEADDRNOTAVAIL The specified address is not a valid address for this machine.
WSAEFAULT The name or namelen parameter is not a valid part of the user address space, the namelen parameter is too small, the name parameter contains an incorrect address format for the associated address family, or the first two bytes of the memory block specified by name do not match the address family associated with the socket descriptor s.
WSAEINPROGRESS A blocking Winsock call is in progress, or the service provider is still processing a callback function.
WSAEINVAL The socket is already bound to an address.
WSAENOBUFS Not enough buffers available; too many connections.
WSAENOTSOCK The descriptor is not a socket.

6.connect (Windows Sockets)

See Also

accept (Windows Sockets) | bind (Windows Sockets) | getsockname (Windows Sockets) | recv | recvfrom | select | send | sendto | setsockopt (Windows Sockets) | sockaddr | socket (Windows Sockets) | WSAConnect | WSAEventSelect | WSAGetLastError | WSARecv | WSARecvFrom | WSASend | WSASendTo | WSAStartup

Requirements

OS Versions: Windows CE 1.0 and later.
Header: Winsock2.h.
Link Library: Ws2.lib.

This function establishes a connection to a specified socket.

int connect(
  SOCKET s,
  const struct sockaddr FAR* name,
  int namelen
);

Parameters

s

[in] Descriptor identifying an unconnected socket.

name

[in] Name of the socket to which the connection should be established.

namelen

[in] Length of the name.

7.listen

See Also

accept (Windows Sockets) | bind (Windows Sockets) | connect (Windows Sockets) | socket (Windows Sockets) | WSAGetLastError | WSAStartup

Requirements

OS Versions: Windows CE 1.0 and later.
Header: Winsock2.h.
Link Library: Ws2.lib.

This function places a socket at a state where it is listening for an incoming connection.

int listen(
  SOCKET s,
  int backlog
);

Parameters

s

[in] Descriptor identifying a bound, unconnected socket.

backlog

[in] Maximum length of the queue of pending connections. If backlog is set to SOMAXCONN, the underlying service provider responsible for socket s will set the backlog to a maximum reasonable value. There is no standard provision to obtain the actual backlog value.

Return Values

If no error occurs, this function returns zero. If an error occurs, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError.

The following table shows a list of possible error codes.

Error code Description
WSANOTINITIALISED A successful WSAStartup call must occur before using this function.
WSAENETDOWN The network subsystem has failed.
WSAEADDRINUSE The socket's local address is already in use and the socket was not marked to allow address reuse with SO_REUSEADDR. This error usually occurs during execution of the bind (Windows Sockets) function, but it could be delayed until the listen function if the bind was to a partially wildcard address (involving ADDR_ANY) and if a specific address needs to be committed at the time of the listen function.
WSAEINPROGRESS A blocking Winsock call is in progress, or the service provider is still processing a callback function.
WSAEINVAL The socket has not been bound with bind.
WSAEISCONN The socket is already connected.
WSAEMFILE No more socket descriptors are available.
WSAENOBUFS No buffer space is available.
WSAENOTSOCK The descriptor is not a socket.
WSAEOPNOTSUPP The referenced socket is not of a type that supports the listen operation.

8.recv

See Also

accept (Windows Sockets) | bind (Windows Sockets) | connect (Windows Sockets) | ioctlsocket | recvfrom | select | send | sendto | shutdown | socket (Windows Sockets) | WSAAccept | WSAConnect | WSAEventSelect | WSAGetLastError | WSAIoctl | WSARecv | WSASendTo | WSAStartup

Requirements

OS Versions: Windows CE 1.0 and later.
Header: Winsock2.h.
Link Library: Ws2.lib.

This function receives data from a connected socket.

int recv(
  SOCKET s,
  char FAR* buf,
  int len,
  int flags
);

Parameters

s

[in] Descriptor identifying a connected socket.

buf

[out] Buffer for the incoming data.

len

[in] Length of the buf parameter.

flags

[in] Flag specifying the way in which the call is made.

Return Values

If no error occurs, this function returns the number of bytes received. If the connection has been gracefully closed, the return value is zero. If an error occurs, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError. The following table shows a list of possible error codes.

Error code Description
WSANOTINITIALISED A successful WSAStartup call must occur before using this function.
WSAENETDOWN The network subsystem has failed.
WSAEFAULT The buf parameter is not completely contained in a valid part of the user address space.
WSAENOTCONN The socket is not connected.
WSAEINTR The socket was closed.
WSAEINPROGRESS A blocking Winsock call is in progress, or the service provider is still processing a callback function.
WSAENETRESET The connection has been broken due to the keep-alive activity detecting a failure while the operation was in progress.
WSAENOTSOCK The descriptor is not a socket.

WSAEOPNOTSUPP MSG_OOB was specified, but the socket is not stream style such as type SOCK_STREAM, out of band (OOB) data is not supported in the communication domain associated with this socket, or the socket is unidirectional and supports only send operations.

WSAESHUTDOWN The socket has been shut down; it is not possible to receive on a socket after shutdown has been invoked with how set to SD_RECEIVE or SD_BOTH.
WSAEWOULDBLOCK The socket is marked as nonblocking and the receive operation would block.
WSAEMSGSIZE The message was too large to fit into the specified buffer and was truncated.
WSAEINVAL The socket has not been bound with bind (Windows Sockets), an unknown flag was specified, MSG_OOB was specified for a socket with SO_OOBINLINE enabled, or (for byte stream sockets only) len was zero or negative.
WSAECONNABORTED The virtual circuit was terminated due to a time-out or other failure. The application should close the socket as it is no longer usable.
WSAETIMEDOUT The connection has been dropped because of a network failure or because the peer system failed to respond.
WSAECONNRESET The virtual circuit was reset by the remote side executing a hard or abortive close. The application should close the socket because it is no longer usable.

9.send

See Also

bind (Windows Sockets) | getsockopt (Windows Sockets) | recv | recvfrom | select | sendto | shutdown | setsockopt (Windows Sockets) | socket (Windows Sockets) | WSAEventSelect | WSAGetLastError | WSAStartup

Requirements

OS Versions: Windows CE 1.0 and later.
Header: Winsock2.h.
Link Library: Ws2.lib.

This function sends data on a connected socket.

int send(
  SOCKET s,
  const char FAR* buf,
  int len,
  int flags
);

Parameters

s

[in] Descriptor identifying a connected socket.

buf

[in] Buffer containing the data to be transmitted.

len

[in] Length of the data in the buf parameter.

flags

[in] Indicator specifying the way in which the call is made.

Return Values

If no error occurs, this function returns the total number of bytes sent, which can be less than the number indicated by len for nonblocking sockets. If an error occurs, a value of SOCKET_ERROR is returned, and a specific error code can be retrieved by calling WSAGetLastError. The following table shows a list of possible error codes.

Error code Description
WSANOTINITIALISED A successful WSAStartup call must occur before using this function.
WSAENETDOWN The network subsystem has failed.
WSAEACCES The requested address is a broadcast address, but the appropriate flag was not set. Call setsockopt (Windows Sockets) with the SO_BROADCAST parameter to allow the use of the broadcast address.
WSAEINTR The socket was closed.
WSAEINPROGRESS A blocking Winsock call is in progress, or the service provider is still processing a callback function.
WSAEFAULT The buf parameter is not completely contained in a valid part of the user address space.
WSAENETRESET The connection has been broken due to the keep-alive activity detecting a failure while the operation was in progress.
WSAENOBUFS No buffer space is available.
WSAENOTCONN The socket is not connected.
WSAENOTSOCK The descriptor is not a socket.
WSAEOPNOTSUPP MSG_OOB was specified, but the socket is not stream style such as type SOCK_STREAM, out of band (OOB) data is not supported in the communication domain associated with this socket, or the socket is unidirectional and supports only receive operations.
WSAESHUTDOWN The socket has been shut down. It is not possible to send on a socket after shutdown has been invoked with how set to SD_SEND or SD_BOTH.
WSAEWOULDBLOCK The socket is marked as nonblocking and the requested operation would block.
WSAEMSGSIZE The socket is message-oriented, and the message is larger than the maximum supported by the underlying transport.
WSAEHOSTUNREACH The remote host cannot be reached from this host at this time.
WSAEINVAL The socket has not been bound with bind (Windows Sockets), an unknown flag was specified, or MSG_OOB was specified for a socket with SO_OOBINLINE enabled.
WSAECONNABORTED The virtual circuit was terminated due to a time-out or other failure. The application should close the socket because it is no longer usable.
WSAECONNRESET The virtual circuit was reset by the remote side executing a hard or abortive close. For UDP sockets, the remote host was unable to deliver a previously sent UDP datagram and responded with a "Port Unreachable" ICMP packet. The application should close the socket because it is no longer usable.
WSAETIMEDOUT The connection has been dropped because of a network failure or because the system on the other end went down without notice.
WSAEPROTONOSUPPORT Protocol is not supported. For example, for raw sockets, if the IP_HDRINCL option set for the socket, and in the header the protocol field is set to TCP.

10.select

See Also

accept (Windows Sockets) | connect (Windows Sockets) | listen | recv | recvfrom | send | sendto | WSAEventSelect | setsockopt (Windows Sockets) | WSAGetLastError | WSARecv | WSARecvFrom | WSASendTo | WSAStartup | timeval

Requirements

OS Versions: Windows CE 1.0 and later.
Header: Winsock2.h.
Link Library: Ws2.lib.

This function determines the status of one or more sockets, waiting if necessary, to perform synchronous I/O.

int select(
  int nfds,
  fd_set FAR* readfds,
  fd_set FAR* writefds,
  fd_set FAR* exceptfds,
  const struct timeval FAR* timeout
);

Parameters

nfds

[in] Ignored. The nfds parameter is included only for compatibility with Berkeley sockets.

readfds

[in, out] Optional pointer to a set of sockets to be checked for readability.

writefds

[in, out] Optional pointer to a set of sockets to be checked for writability.

exceptfds

[in, out] Optional pointer to a set of sockets to be checked for errors.

timeout

[in] Maximum time for select to wait, provided in the form of a timeval structure. Set the timeout parameter to NULL for blocking operation.

Return Values

This function returns the total number of socket handles that are ready and contained in the fd_set structures, zero if the time limit expired, or the SOCKET_ERROR if an error occurred. If the return value is SOCKET_ERROR, WSAGetLastError can be used to retrieve a specific error code.

The following table shows a list of possible error codes.

Error code Description
WSANOTINITIALISED A successful WSAStartup call must occur before using this function.
WSAEFAULT The Windows Sockets implementation was unable to allocate needed resources for its internal operations or the readfds, writefds, exceptfds, or timeval parameters are not part of the user address space.
WSAENETDOWN The network subsystem has failed.
WSAEINVAL The time-out value is not valid, or all three descriptor parameters were NULL.
WSAEINTR The socket was closed.
WSAEINPROGRESS A blocking Winsock call is in progress, or the service provider is still processing a callback function.
WSAENOTSOCK One of the descriptor sets contains an entry that is not a socket.

 应用程序主界面

        本例程发送消息给客户端的方式可以通过选自常规模式和查找模式进行发送,常规模式可以选择给所有客户端发送消息,也可以选择给单个指定客户端发送消息。

        查找模式通过查找鼠标当前选择的客户端IP地址端口号和线程句柄和集合中的线程对比进行查找要发送消息的客户端来发送信息、

        文件功能还在更新当中,更新完后陆续会更新。

VS 2019 MFC Socket 通讯例程服务器端Select一对多同步通信TCP/IP通信服务器端[一]_第1张图片

 CClientItemArray类头文件

#pragma once
class CMFCApplicationSocketDemoDlg;//前置声明

class CClientItemArray
{
public:
	CString m_strIP;
	SOCKET  m_ClientSocket;
	HANDLE  m_hThread;
	CMFCApplicationSocketDemoDlg* m_pmainWnd;
	CClientItemArray()
	{
		m_ClientSocket = INVALID_SOCKET;
		m_hThread      = NULL;
		m_pmainWnd     = NULL;
	}
};


CClientItemArray类头CPP文件

#include"pch.h"
#include"CClientItemLis

应用程序头文件

#include"ListCtrlEx.h"//此头文件为List Control控件的重绘类,相关代码在之前的博客中已经提到和公开。

#include"CSoceketInitConfig.h"
#include
#include"CClientItemList.h"
#include"ListCtrlEx.h"
using namespace std;
#pragma once
#pragma warning(disable:4996)
#pragma warning(disable:6263)
#pragma warning(disable:6255)
#pragma warning(disable:6387)
#pragma warning(disable:26454)

const int      SERVER_PORT=         8000;
#define        MAXCLIENT            20
#define        MaxBufSize           2048
#define        WM_UPDATECLIENTNUB	WM_USER+100
DWORD          WINAPI SocketServerThreadFuc(LPCVOID lparam);
BOOL           socket_Select(SOCKET hSocket, DWORD nTimeOut,BOOL bRead);
class CMFCApplicationSocketDemoDlg : public CDialogEx
{

public:
	CMFCApplicationSocketDemoDlg(CWnd* pParent = nullptr);	
	static     UINT SocketManagerThreadFuc(LPVOID* pParam);

#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_MFCAPPLICATIONSOCKETDEMO_DIALOG };
#endif

	protected:
	virtual     void DoDataExchange(CDataExchange* pDX);	

protected:
	HICON		m_hIcon;
	virtual		BOOL  OnInitDialog();
	afx_msg		void  OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg		void  OnPaint();
	afx_msg		HCURSOR OnQueryDragIcon();
	DECLARE_MESSAGE_MAP()
public:
	CArraym_ClientArrayList;
	afx_msg		void OnBnClickedSocketinit();
	SOCKET		m_ListenServer;
	CButton		m_MsgSend;
	BOOL		StartListen;
	BOOL		StartAllClient;
	USHORT		Server_Port;
	CEdit		m_RevTextMsg;
	FD_SET		m_fdsetCollection;
	afx_msg		void OnBnClickedSocketlistenclose();
	afx_msg		void SetRevTextMsg(CString nText);
	afx_msg		CString GetSystemTime();
	afx_msg		BOOL SocketServerSendMsgToClient(CString Msg,CClientItemArray *CClient,BOOL ExceptServer);
	afx_msg		BOOL CheckClientEqual(CClientItemArray *ClientP,CClientItemArray*ClientQ);
	CEdit		m_EditServerPort;
	afx_msg		void OnEnChangeEditsendMsg();
	afx_msg		void OnBnClickedButtonSendmsg();
	afx_msg		void RemoveClientFromClientArray(CClientItemArray bRemoveClientItem);
	afx_msg     void RefreshClientList();
	
	CButton		m_StartServerListenCmd;
	CButton		m_CloseServerListenCmd;
	int			m_ClientCount;
	CComboBox	m_SelectComClient;
	CListCtrlEx m_OnLineClientListDataBase;
	afx_msg     void OnLvnItemchangedListclientarrylist(NMHDR* pNMHDR, LRESULT* pResult);
	afx_msg     void OnNMClickListclientarrylist(NMHDR* pNMHDR, LRESULT* pResult);
	afx_msg     LRESULT OnUpdateclientnub(WPARAM wParam, LPARAM lParam);
	afx_msg     char* CstringToWideCharArry(CString CstrText);
	afx_msg     CString CharArryToCstring(char *CharText);
	CComboBox   m_SelectClientComMode;
	afx_msg     void OnCbnSelchangeComboselectclientsendmode();
};

 应用程序CPP文件


#include "pch.h"
#include "framework.h"
#include "MFCApplicationSocketDemo.h"
#include "MFCApplicationSocketDemoDlg.h"
#include "afxdialogex.h"
using namespace std;
#ifdef _DEBUG
#define new DEBUG_NEW
#endif

class CAboutDlg : public CDialogEx
{
public:
	CAboutDlg();

#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_ABOUTBOX };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);   

protected:
	DECLARE_MESSAGE_MAP()
	
};

CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
END_MESSAGE_MAP()



CMFCApplicationSocketDemoDlg::CMFCApplicationSocketDemoDlg(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_MFCAPPLICATIONSOCKETDEMO_DIALOG, pParent)
	, m_ClientCount(0)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
	StartListen    = FALSE;
	StartAllClient = FALSE;
	Server_Port	   = 0;
	m_ListenServer = NULL;
	FD_ZERO(&m_fdsetCollection);
}



void CMFCApplicationSocketDemoDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_BUTTON_SENDMSG, m_MsgSend);
	DDX_Control(pDX, IDC_EDITRECEV_MSG, m_RevTextMsg);
	DDX_Control(pDX, IDC_EDITSERVER_PORT, m_EditServerPort);
	DDX_Control(pDX, IDC_SOCKETINIT, m_StartServerListenCmd);
	DDX_Control(pDX, IDC_SOCKETLISTENCLOSE, m_CloseServerListenCmd);
	DDX_Text(pDX, IDC_EDITCLIENT_COUNT, m_ClientCount);
	DDX_Control(pDX, IDC_COMBOSELECTCLIENT, m_SelectComClient);
	DDX_Control(pDX, IDC_LISTCLIENTARRYLIST, m_OnLineClientListDataBase);
	DDX_Control(pDX, IDC_COMBOSELECTCLIENTSENDMODE, m_SelectClientComMode);
}

BEGIN_MESSAGE_MAP(CMFCApplicationSocketDemoDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_SOCKETINIT, &CMFCApplicationSocketDemoDlg::OnBnClickedSocketinit)
	ON_EN_CHANGE(IDC_EDITSEND_MSG, &CMFCApplicationSocketDemoDlg::OnEnChangeEditsendMsg)
	ON_BN_CLICKED(IDC_SOCKETLISTENCLOSE, &CMFCApplicationSocketDemoDlg::OnBnClickedSocketlistenclose)
	ON_EN_CHANGE(IDC_EDITSEND_MSG, &CMFCApplicationSocketDemoDlg::OnEnChangeEditsendMsg)
	ON_BN_CLICKED(IDC_BUTTON_SENDMSG, &CMFCApplicationSocketDemoDlg::OnBnClickedButtonSendmsg)
	ON_NOTIFY(LVN_ITEMCHANGED, IDC_LISTCLIENTARRYLIST, &CMFCApplicationSocketDemoDlg::OnLvnItemchangedListclientarrylist)
	ON_NOTIFY(NM_CLICK, IDC_LISTCLIENTARRYLIST, &CMFCApplicationSocketDemoDlg::OnNMClickListclientarrylist)
	ON_MESSAGE(WM_UPDATECLIENTNUB, &CMFCApplicationSocketDemoDlg::OnUpdateclientnub)
	ON_CBN_SELCHANGE(IDC_COMBOSELECTCLIENTSENDMODE, &CMFCApplicationSocketDemoDlg::OnCbnSelchangeComboselectclientsendmode)
END_MESSAGE_MAP()



BOOL CMFCApplicationSocketDemoDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);
	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != nullptr)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	SetIcon(m_hIcon, TRUE);			
	SetIcon(m_hIcon, FALSE);		
	UpdateData(TRUE);
	m_MsgSend.EnableWindow(FALSE);
	m_StartServerListenCmd.EnableWindow(TRUE);
	m_CloseServerListenCmd.EnableWindow(FALSE);
	m_SelectComClient.SetCurSel(20);
	m_SelectClientComMode.SetCurSel(0);
	SetDlgItemInt(IDC_EDITSERVER_PORT, SERVER_PORT);
	CRect RectUserList;
	m_OnLineClientListDataBase.GetClientRect(&RectUserList);
	m_OnLineClientListDataBase.SetExtendedStyle(m_OnLineClientListDataBase.GetExtendedStyle() | LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES | LVS_EX_GRIDLINES);
	CString strHeader[3] = { 0 };
	int nGridWith = RectUserList.Width() / 3;
	strHeader[0] = _T("客户端端口");
	strHeader[1] = _T("客户IP地址");
	strHeader[2] = _T("客户端线程");
	for (int i = 0; i < 3; i++)
	{
		m_OnLineClientListDataBase.InsertColumn(i, strHeader[i], LVCFMT_CENTER, nGridWith, i);
	}
	UpdateData(FALSE);
	return TRUE;  
}

void CMFCApplicationSocketDemoDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else if ((nID & 0xFFF0) == SC_SIZE)
	{
		return;
	}
	else
	{
		CDialogEx::OnSysCommand(nID, lParam);
	}
}


void CMFCApplicationSocketDemoDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this);

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast(dc.GetSafeHdc()), 0);
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialogEx::OnPaint();
	}
}
HCURSOR CMFCApplicationSocketDemoDlg::OnQueryDragIcon()
{
	return static_cast(m_hIcon);
}


void CMFCApplicationSocketDemoDlg::OnBnClickedSocketinit()
{
	Server_Port=GetDlgItemInt(IDC_EDITSERVER_PORT);
	if (Server_Port >65535|| Server_Port<1024)
	{
		MessageBox(_T("请输入合适端口"),_T("信息提示:"),MB_OK|MB_ICONINFORMATION);
		GetDlgItem(IDC_EDITSERVER_PORT)->SetFocus();
		SetDlgItemInt(IDC_EDITSERVER_PORT, SERVER_PORT);
		Server_Port = SERVER_PORT;
		
	}
	StartListen	   = TRUE;
	StartAllClient = TRUE;
	AfxBeginThread((AFX_THREADPROC)SocketManagerThreadFuc, this);
	m_StartServerListenCmd.EnableWindow(FALSE);
	m_CloseServerListenCmd.EnableWindow(TRUE);
}

DWORD WINAPI SocketServerThreadFuc(LPCVOID lparam)
{
	USES_CONVERSION;
	CClientItemArray ClientSocket = *(CClientItemArray*)lparam;
	int  reclth = 0;
	int  sedlth = 0;
	char recvBuff[MaxBufSize];
	char sendBuff[MaxBufSize];
	CWnd* thHwnd = AfxGetApp()->GetMainWnd();

	while (ClientSocket.m_pmainWnd->StartAllClient==TRUE)
	{
		if (socket_Select(ClientSocket.m_ClientSocket,100, TRUE))
		{
			reclth = recv(ClientSocket.m_ClientSocket, recvBuff, sizeof(recvBuff), 0);
			if (reclth > 0)
			{
				cout << "接收到的信息是:" << recvBuff << "来自客户端:" << ClientSocket.m_ClientSocket << endl;
				CString RcvMsg;
				CString WtoStr;
				CString strMsg;
				RcvMsg.Format(_T("信息来自客户<-:ClientSocket=%d"), ClientSocket.m_ClientSocket);
				RcvMsg = RcvMsg + _T(", IP= ") + ClientSocket.m_strIP;

				strMsg = ClientSocket.m_pmainWnd->CharArryToCstring(recvBuff);
				RcvMsg = RcvMsg + _T("信息:") + strMsg;
				ClientSocket.m_pmainWnd->SetRevTextMsg(RcvMsg);
				ClientSocket.m_pmainWnd->SocketServerSendMsgToClient(RcvMsg,&ClientSocket,TRUE);
				cout << "请输入要发送到客户端的信息:" << endl;
				ClientSocket.m_pmainWnd->SetRevTextMsg(_T("请输入要发送到客户端的信息"));

			}
			else
			{
				CString strConnectSocket;
				strConnectSocket.Format(_T("ClientSocket=%d的客户端离线"), ClientSocket.m_ClientSocket);
				ClientSocket.m_pmainWnd->RemoveClientFromClientArray(ClientSocket);
				ClientSocket.m_pmainWnd->SetRevTextMsg(strConnectSocket);
				cout << "ClientSocket" << ClientSocket.m_ClientSocket << "的客户机离线" << endl;
				break;
			}
			memset(recvBuff, 0, sizeof(recvBuff));
			memset(sendBuff, 0, sizeof(sendBuff));
		}
	}
	return 0;
}

UINT CMFCApplicationSocketDemoDlg::SocketManagerThreadFuc(LPVOID* pParam)
{

	CMFCApplicationSocketDemoDlg* ManageThread = (CMFCApplicationSocketDemoDlg*)pParam;
	CWnd* thHwnd = AfxGetApp()->GetMainWnd();
	int nCount = 0;
	::PostMessage(thHwnd->GetSafeHwnd(), WM_UPDATECLIENTNUB, WPARAM(nCount), LPARAM(nCount));
	CSoceketInitConfig CInitSock(2, 2);

	ManageThread->m_ListenServer = socket(AF_INET,SOCK_STREAM,0);
	SOCKADDR_IN ListerAddr;
	sockaddr_in ClientAddr;
	ListerAddr.sin_family = AF_INET;
	ListerAddr.sin_addr.S_un.S_addr = INADDR_ANY;
	ListerAddr.sin_port = htons(ManageThread->Server_Port);

	int lth = bind(ManageThread->m_ListenServer,(LPSOCKADDR)&ListerAddr,sizeof(ListerAddr));
	if (lth==SOCKET_ERROR)
	{
		cout << "端口绑定失败!" << endl;
		ManageThread->SetRevTextMsg(_T("端口绑定失败"));
		return 0;
	}
	else
	{
		cout << "端口绑定成功!绑定端口为:" << ManageThread->Server_Port << endl;

		CString strPort;
		strPort.Format(_T("端口绑定成功!绑定端口为:%d"), ManageThread->Server_Port);
		ManageThread->SetRevTextMsg(strPort);

	}
	int L = listen(ManageThread->m_ListenServer,MAXCLIENT);
	cout << "服务器准备就绪,等待连接请求!" << endl;
	ManageThread->SetRevTextMsg(_T("服务器准备就绪,等待连接请求!"));
	while (ManageThread->StartListen==TRUE)
	{

		if (socket_Select(ManageThread->m_ListenServer,100, TRUE))
		{
			int socketAddrLth = sizeof(sockaddr_in);
			SOCKET ClientSocket ;
			ClientSocket = accept(ManageThread->m_ListenServer, (SOCKADDR*)&ClientAddr, &socketAddrLth);
			if (ClientSocket == INVALID_SOCKET)
			{
				continue;
			}

			CClientItemArray Item;
			Item.m_ClientSocket = ClientSocket;
			Item.m_strIP = inet_ntoa(ClientAddr.sin_addr);
			Item.m_pmainWnd = ManageThread;
			int index = ManageThread->m_ClientArrayList.Add(Item);
			cout << "客户端已连接到服务器,连接Socket:" << ClientSocket << endl;
			
			CString strSocket;
			strSocket.Format(_T("一个客户端已连接到服务器,连接Socket=%d"), (int)ClientSocket);
			ManageThread->SetRevTextMsg(strSocket);

			Item.m_hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)SocketServerThreadFuc, &(ManageThread->m_ClientArrayList.GetAt(index)), CREATE_SUSPENDED, NULL);
			ManageThread->m_ClientArrayList.GetAt(index).m_hThread = Item.m_hThread;
			ResumeThread(Item.m_hThread);
			ManageThread->SetRevTextMsg(_T("IP=") + Item.m_strIP + _T("的客户端转为在线"));

			nCount = nCount + 1;
			::PostMessage(thHwnd->GetSafeHwnd(), WM_UPDATECLIENTNUB, WPARAM(nCount), 0);
			nCount = 0;

			ManageThread->m_OnLineClientListDataBase.DeleteAllItems();
			ManageThread->m_OnLineClientListDataBase.Invalidate();
			for (int i = 0; i < ManageThread->m_ClientArrayList.GetCount(); i++)
			{
				CString strCltPt;
				CString strCltIp;
				CString strCltThread;
				strCltPt.Format(_T("%d"), ManageThread->m_ClientArrayList[i].m_ClientSocket);
				strCltIp = ManageThread->m_ClientArrayList[i].m_strIP;
				strCltThread.Format(_T("%p"), ManageThread->m_ClientArrayList[i].m_hThread);

				ManageThread->m_OnLineClientListDataBase.InsertItem(i, strCltPt);
				ManageThread->m_OnLineClientListDataBase.SetItemText(i, 1, strCltIp);
				ManageThread->m_OnLineClientListDataBase.SetItemText(i, 2, strCltThread);
			}
			
		}	
	}
	closesocket(ManageThread->m_ListenServer);
	WSACleanup();
	ManageThread->m_StartServerListenCmd.EnableWindow(TRUE);
	ManageThread->m_CloseServerListenCmd.EnableWindow(FALSE);
	printf("服务器退出监听状态!\n");
	ManageThread->SetRevTextMsg(_T("服务器退出监听状态!"));
	nCount = 0;
	::PostMessage(thHwnd->GetSafeHwnd(), WM_UPDATECLIENTNUB, WPARAM(nCount),LPARAM(nCount));
	return 0;

}

void CMFCApplicationSocketDemoDlg::OnBnClickedSocketlistenclose()
{
	StartListen = FALSE;
	Sleep(200);
	StartAllClient = FALSE;
	int AllClient = m_ClientArrayList.GetCount();
	for (int nCount = 0; nCount < AllClient; nCount++)
	{
		closesocket(m_ClientArrayList.GetAt(nCount).m_ClientSocket);
	}
	m_ClientArrayList.RemoveAll();
	closesocket(m_ListenServer);
	RefreshClientList();
	UpdateData(TRUE);
	SetDlgItemText(IDC_EDITCLIENTSELSKT, _T(""));
	SetDlgItemText(IDC_EDITCLIENTSELIP, _T(""));
	SetDlgItemText(IDC_EDITCLIENTTHREAD, _T(""));
	UpdateData(FALSE);
}

void CMFCApplicationSocketDemoDlg::SetRevTextMsg(CString nText)
{
	CString str;
	GetDlgItemText(IDC_EDITRECEV_MSG, str);
	LONG lth = str.GetLength();
	m_RevTextMsg.SetSel(lth, lth, TRUE);
	m_RevTextMsg.SetFocus();
	m_RevTextMsg.ReplaceSel(GetSystemTime() + _T("\r\n") + nText + _T("\r\n"));
}

CString CMFCApplicationSocketDemoDlg::GetSystemTime()
{
	CString m_CurrentTime;
	CTime m_GetTime = CTime:: GetTickCount();
	m_CurrentTime = m_GetTime.Format(_T("%Y/%m/%d:%H:%M:%S"));
	return m_CurrentTime;
}

BOOL CMFCApplicationSocketDemoDlg::SocketServerSendMsgToClient(CString Msg, CClientItemArray *CClient, BOOL ExceptServer)
{
	USES_CONVERSION;
	BOOL SuccedFlag=FALSE;
	char *pStr= CstringToWideCharArry(Msg);
	
	if (m_ClientArrayList.GetCount()==0)
	{
		SetRevTextMsg(_T("当前没有客户端连接到服务器,消息:") + Msg + _T("=未发送!"));
		SuccedFlag = FALSE;
	}
	else
	{
		for (int i = 0; i < m_ClientArrayList.GetCount(); i++)
		{
			if (ExceptServer==FALSE)
			{
				if (!CClient || CheckClientEqual(CClient, &(m_ClientArrayList.GetAt(i))) == TRUE)
				{
					int lenth = send(m_ClientArrayList.GetAt(i).m_ClientSocket, pStr, _tcslen(Msg) * sizeof(pStr), 0);
					if (lenth < 0)
					{
						SetRevTextMsg(_T("服务器发送->信息:") + Msg + _T("=未发送!"));
						BOOL SuccedFlag;
						SuccedFlag = FALSE;
					}
					else
					{
						SetRevTextMsg(_T("服务器发送->信息:") + Msg + _T("=已发送!"));
						SuccedFlag = TRUE;
					}
				}
			}
			else if (ExceptServer==TRUE)
			{
				if (!CClient || !CheckClientEqual(CClient, &(m_ClientArrayList.GetAt(i))) == TRUE)
				{
					int lenth = send(m_ClientArrayList.GetAt(i).m_ClientSocket, pStr, _tcslen(Msg) * sizeof(pStr), 0);
					if (lenth < 0)
					{
						SetRevTextMsg(_T("服务器分发->信息:") + Msg + _T("到客户机:") + m_ClientArrayList.GetAt(i).m_strIP + _T(" 失败!"));
						BOOL SuccedFlag;
						SuccedFlag = FALSE;
					}
					else
					{
						SetRevTextMsg(_T("服务器分发->信息:") + Msg+_T("到客户机:") + m_ClientArrayList.GetAt(i).m_strIP+_T(" 成功!"));
						SuccedFlag = TRUE;
					}
				}
			}
			
		}
	}
	return SuccedFlag;
}

BOOL CMFCApplicationSocketDemoDlg::CheckClientEqual(CClientItemArray* ClientP, CClientItemArray* ClientQ)
{
	if (ClientP->m_ClientSocket== ClientQ->m_ClientSocket&& ClientP->m_hThread==ClientQ->m_hThread && ClientP->m_strIP==ClientQ->m_strIP)
	{
		return TRUE;
	}
	else
	{
		return FALSE;
	}	
}


void CMFCApplicationSocketDemoDlg::OnEnChangeEditsendMsg()
{
	CString m_Msg_Enter;
	GetDlgItemText(IDC_EDITSEND_MSG,m_Msg_Enter);
	if (m_Msg_Enter==_T("")||!StartListen)
	{
		m_MsgSend.EnableWindow(FALSE);
	}
	else
	{
		m_MsgSend.EnableWindow(TRUE);
	}
}


void CMFCApplicationSocketDemoDlg::OnBnClickedButtonSendmsg()
{
	CString strSendMsg;
	CString GetStrIP, GetStrSocket, GetstrThread;
	CString CpStrIP, CpSock, CpThread;

	GetDlgItemText(IDC_EDITCLIENTSELSKT, GetStrSocket);
	GetDlgItemText(IDC_EDITCLIENTSELIP,  GetStrIP);
	GetDlgItemText(IDC_EDITCLIENTTHREAD, GetstrThread);
	GetDlgItemText(IDC_EDITSEND_MSG, strSendMsg);
	int m_SendMode = m_SelectClientComMode.GetCurSel();
	if (m_SendMode ==0)
	{
		int m_Client = m_SelectComClient.GetCurSel();
		if (m_Client == 20)
		{
			SocketServerSendMsgToClient(strSendMsg, NULL, FALSE);
		}
		else if (m_Client >= 0 && m_Client <= m_ClientArrayList.GetCount() - 1)
		{
			for (int i = 0; i < m_ClientArrayList.GetCount(); i++)
			{
				if (i == m_Client)
				{
					SocketServerSendMsgToClient(strSendMsg, &(m_ClientArrayList.GetAt(i)), FALSE);
				}
			}
		}
		else
		{
			CString strClietSelect;
			GetDlgItemText(IDC_COMBOSELECTCLIENT, strClietSelect);
			SetRevTextMsg(_T("选择要发送信息的客户端:") + strClietSelect + _T("不在客户端链表内或客户端不在线!"));
		}
	}
	else if (m_SendMode==1)
	{
		if (GetStrIP!=_T("")&& GetStrSocket!=_T("")&& GetstrThread!=_T(""))
		{
			for (int i = 0; i < m_ClientArrayList.GetCount(); i++)
			{
				CpStrIP = _T("");
				CpSock = _T("");
				CpThread = _T("");
				CpStrIP = m_ClientArrayList.GetAt(i).m_strIP;
				CpSock.Format(_T("%d"), m_ClientArrayList.GetAt(i).m_ClientSocket);
				CpThread.Format(_T("%p"), m_ClientArrayList.GetAt(i).m_hThread);
				if (GetStrIP.CompareNoCase(CpStrIP) ==0 && GetStrSocket.CompareNoCase(CpSock) == 0 && GetstrThread.CompareNoCase(CpThread) ==0 )
				{
					SocketServerSendMsgToClient(strSendMsg, &(m_ClientArrayList.GetAt(i)), FALSE);
					SetRevTextMsg(_T("Socket=")+CpSock + _T("||") + _T("IP=")+ CpStrIP + _T("||") + _T("Thread=")+ CpThread);
				}
				else
				{
					printf("选在要发送消息的客户端不在服务器列表内!");
				}
			}
	
		}
		else
		{
			MessageBox(_T("请从例表中选择要发送信息的客户端!"),_T("信息提示!"),MB_OKCANCEL|MB_ICONWARNING);
			return;
		}
		
		
	}
	
	SetDlgItemText(IDC_EDITSEND_MSG,_T(""));
}

void CMFCApplicationSocketDemoDlg::RemoveClientFromClientArray(CClientItemArray bRemoveClientItem)
{
	CWnd* thHwnd = AfxGetApp()->GetMainWnd();
	for (int ClientArrIndex = 0; ClientArrIndex < m_ClientArrayList.GetCount(); ClientArrIndex++)
	{
		if (bRemoveClientItem.m_ClientSocket==m_ClientArrayList[ClientArrIndex].m_ClientSocket &&
			bRemoveClientItem.m_hThread==m_ClientArrayList[ClientArrIndex].m_hThread &&
			bRemoveClientItem.m_pmainWnd==m_ClientArrayList[ClientArrIndex].m_pmainWnd &&
			bRemoveClientItem.m_strIP==m_ClientArrayList[ClientArrIndex].m_strIP)
		{
			m_ClientArrayList.RemoveAt(ClientArrIndex);
			int nCl = 1;
			::PostMessage(thHwnd->GetSafeHwnd(), WM_UPDATECLIENTNUB, 0, LPARAM(nCl));
			RefreshClientList();
		}
	}
}

void CMFCApplicationSocketDemoDlg::RefreshClientList()
{

		m_OnLineClientListDataBase.DeleteAllItems();
		m_OnLineClientListDataBase.Invalidate();
		for (int i = 0; i < m_ClientArrayList.GetCount(); i++)
		{
			CString strCltPt;
			CString strCltIp;
			CString strCltThread;

			strCltPt.Format(_T("%d"), m_ClientArrayList[i].m_ClientSocket);
			strCltIp = m_ClientArrayList[i].m_strIP;
			strCltThread.Format(_T("%p"), m_ClientArrayList[i].m_hThread);

			m_OnLineClientListDataBase.InsertItem(i, strCltPt);
			m_OnLineClientListDataBase.SetItemText(i, 1, strCltIp);
			m_OnLineClientListDataBase.SetItemText(i, 2, strCltThread);
			m_OnLineClientListDataBase.Invalidate();
		}
		
}

BOOL socket_Select(SOCKET hSocket, DWORD nTimeOut, BOOL bRead)
{
	FD_SET fdset;
	timeval tv;
	FD_ZERO(&fdset);
	FD_SET(hSocket,&fdset);
	nTimeOut = nTimeOut > 1000 ? 1000 : nTimeOut;
	tv.tv_sec = 0;
	tv.tv_usec = nTimeOut;
	int iRet = 0;
	if (bRead)
	{
		iRet = select(0, &fdset,NULL, NULL, &tv);
	}
	else
	{
		iRet = select(0, NULL, &fdset, NULL, &tv);
	}
	if (iRet <= 0)
	{
		return FALSE;
	}
	else if (FD_ISSET(hSocket, &fdset))
	{
		return TRUE;
	}
	return FALSE;
}




void CMFCApplicationSocketDemoDlg::OnLvnItemchangedListclientarrylist(NMHDR* pNMHDR, LRESULT* pResult)
{
	LPNMLISTVIEW pNMLV = reinterpret_cast(pNMHDR);
	int nItem = -1;
	CString strMsg;
	strMsg = _T("");
	NM_LISTVIEW *lpNMItemActivate = (NM_LISTVIEW*)pNMHDR;	 
	if (m_OnLineClientListDataBase.GetCheck(lpNMItemActivate->iItem)==1)
	{
		nItem = lpNMItemActivate->iItem;
		CString  strMessage[3] = { _T("") };
		for (int i = 0; i < 3; i++)
		{
			strMessage[i] = m_OnLineClientListDataBase.GetItemText(nItem, i);
		}
		UpdateData(TRUE);
		SetDlgItemText(IDC_EDITCLIENTSELSKT, strMessage[0]);
		SetDlgItemText(IDC_EDITCLIENTSELIP,  strMessage[1]);
		SetDlgItemText(IDC_EDITCLIENTTHREAD, strMessage[2]);
		UpdateData(FALSE);
	}
	else
	{
		UpdateData(TRUE);
		SetDlgItemText(IDC_EDITCLIENTSELSKT, _T(""));
		SetDlgItemText(IDC_EDITCLIENTSELIP,  _T(""));
		SetDlgItemText(IDC_EDITCLIENTTHREAD, _T(""));
		UpdateData(FALSE);
		return;
	}
	
	*pResult = 0;
}

void CMFCApplicationSocketDemoDlg::OnNMClickListclientarrylist(NMHDR* pNMHDR, LRESULT* pResult)
{
	LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast(pNMHDR);
	*pResult = 0;
	int nItem = -1;
	
	NMLISTVIEW* pNMListView = (NMLISTVIEW*)pNMHDR;

	if (-1 != pNMListView->iItem)        
	{
		nItem = pNMListView->iItem;
		CString  strMessage[3] = { _T("") };
		for (int i = 0; i < 3; i++)
		{
			strMessage[i] = m_OnLineClientListDataBase.GetItemText(nItem, i);
		}
		UpdateData(TRUE);
		SetDlgItemText(IDC_EDITCLIENTSELSKT, strMessage[0]);
		SetDlgItemText(IDC_EDITCLIENTSELIP,  strMessage[1]);
		SetDlgItemText(IDC_EDITCLIENTTHREAD, strMessage[2]);
		UpdateData(FALSE);
	}
	else
	{
		UpdateData(TRUE);
		SetDlgItemText(IDC_EDITCLIENTSELSKT, _T(""));
		SetDlgItemText(IDC_EDITCLIENTSELIP,  _T(""));
		SetDlgItemText(IDC_EDITCLIENTTHREAD, _T(""));
		UpdateData(FALSE);
		return;
	}
	*pResult = 0;

}


afx_msg LRESULT CMFCApplicationSocketDemoDlg::OnUpdateclientnub(WPARAM wParam, LPARAM lParam)
{
	int m_Count;
	int m_Dec;
	m_Dec = (int)(lParam);
	m_Count = (int)(wParam);
	UpdateData(TRUE);
	if (m_Dec!=0&& m_Count==0)
	{
		
		m_ClientCount = m_ClientCount- m_Dec;
		
	}
	else if (m_Count != 0 && m_Dec==0)
	{
		m_ClientCount = m_ClientCount + m_Count;
	}
	else if (m_Count==0&& m_Dec==0)
	{
		m_ClientCount = 0;
	}
	UpdateData(FALSE);
	return 0;
}

char* CMFCApplicationSocketDemoDlg::CstringToWideCharArry(CString CstrText)
{
	int lth = WideCharToMultiByte(CP_ACP,0, CstrText, CstrText.GetLength(),NULL,0,NULL,NULL);
	char* pStr = ( char*)malloc((lth + 1) * sizeof( char));
	memset(pStr, 0, (lth + 1) * sizeof( char));
	WideCharToMultiByte(CP_ACP, 0, CstrText.GetBuffer(), CstrText.GetLength(), (LPSTR)pStr, lth, NULL, NULL);
	*(pStr + lth+1) = '\0';
	printf("lth=%d", lth);
	printf("lth+1=%d", (lth+1)* sizeof(char));
	printf("LthStr=%d", sizeof(CString));
	printf("CharLen=%d", sizeof(char*));
	return  pStr;
}

CString CMFCApplicationSocketDemoDlg::CharArryToCstring(char* CharText)
{
	int charLth = strlen(CharText);
	int len = MultiByteToWideChar(CP_ACP, 0, CharText, charLth, NULL, 0);
	TCHAR* buf = new TCHAR[len + 1];
	MultiByteToWideChar(CP_ACP, 0, CharText, charLth, buf, len);
	buf[len] = '\0'; 
	CString pWideChar;
	pWideChar.Append(buf);
	return pWideChar;
}


void CMFCApplicationSocketDemoDlg::OnCbnSelchangeComboselectclientsendmode()
{
	int m_SendMode = m_SelectClientComMode.GetCurSel();
	if (m_SendMode==0)
	{
		m_SelectComClient.EnableWindow(TRUE);
	}
	else if (m_SendMode==1)
	{
		m_SelectComClient.EnableWindow(FALSE);
	}
}

VS 2019 MFC Socket 通讯例程服务器端Select一对多同步通信TCP/IP通信服务器端[一]_第2张图片

 常规模式群发信息

 VS 2019 MFC Socket 通讯例程服务器端Select一对多同步通信TCP/IP通信服务器端[一]_第3张图片

 查找模式给单个客户端发送信息

 VS 2019 MFC Socket 通讯例程服务器端Select一对多同步通信TCP/IP通信服务器端[一]_第4张图片

 客户端信息分发功能

VS 2019 MFC Socket 通讯例程服务器端Select一对多同步通信TCP/IP通信服务器端[一]_第5张图片

  客户端例程:

https://blog.csdn.net/lzc881012/article/details/127775714https://blog.csdn.net/lzc881012/article/details/127775714

你可能感兴趣的:(C++,MFC,网络,c++,mfc,windows,visual,studio)