c/s模型之tcp群聊

_拥你一生,如沐星辰_ _拥你一生,如沐星辰_     2022-09-23     475

关键词:

说明:
利用TCP协议和多线程实现群聊功能。一个服务器,多个客户端(同一个程序多次启动)。客户端向服务端发送数据,由服务端进行转发到其他
客户端。

/服务端
// WSASever.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <WinSock2.h>
#include <Windows.h>
#include <vector>
#pragma comment (lib,"wSock32.lib")

SOCKET sockLink;
SOCKET g_psockSockLink[1024] = {0};    //存放客户端的sock
int g_nSocketNum=0;            //记录客户端sock的数目


//多线程进行接受和转发
DWORD WINAPI SeverThread(LPVOID lParam)
{
    int nErr = 0;
    char pSeverBuff[MAXBYTE] = { 0 };    //接受客户端的数据
    char pSendBuff[MAXBYTE] = { 0 };    //显示在窗口,包括来自哪个IP地址,端口号,数据
    
    SOCKET sockLink = (SOCKET)lParam;    //当前的客户端sock

    SOCKADDR_IN sockAddr;
    int len = sizeof(SOCKADDR_IN);

    while (TRUE)
    {
        //接受客户端
        nErr = recv(sockLink,pSeverBuff, MAXBYTE, 0);
        if (nErr == SOCKET_ERROR)
        {
            break;
            return -1;
        }
        
        //根据sock获取sock地址
        getpeername(sockLink, (sockaddr*)&sockAddr, &len);
        
        //将Ip、端口号、数据存入pSendBuff
        sprintf_s(pSendBuff,"%s(%d):%s
", inet_ntoa(sockAddr.sin_addr), ntohs(sockAddr.sin_port), pSeverBuff);
        
        //显示在窗口
        printf("%s
", pSendBuff);
        
        //转发
        for (int i = 0;i<g_nSocketNum;++i)
        {
            //不为当前发送方的sock
            if (g_psockSockLink[i] != sockLink)
            {
                send(g_psockSockLink[i], pSendBuff, MAXBYTE, 0);
            }
        }
            
    }
    //当客户端关闭时,服务端也随之关闭
    //if (nErr == INVALID_SOCKET)
        //return-1;
    return 0;
}



int _tmain(int argc, _TCHAR* argv[])
{
    //版本检测
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    wVersionRequested = MAKEWORD(2, 2);

    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {

        printf("WSAStartup failed with error: %d
", err);
        return 1;
    }

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
    {
        printf("Could not find a usable version of Winsock.dll
");
        WSACleanup();
        return 1;
    }
    else
        printf("The Winsock 2.2 dll was found okay
");

    //程序开始
    
    //创建socket->bind-》listen->accept->recv->send->closesocket

    SOCKET severSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (severSocket == INVALID_SOCKET)
    {
        printf("new socket error!");
    }

    //设置端口号和IP地址、协议。
    SOCKADDR_IN sockAddr;
    sockAddr.sin_port = htons(10086);
    sockAddr.sin_family = AF_INET;
    sockAddr.sin_addr.s_addr = htonl (INADDR_ANY);
        

    //IP地址表示方法
    /*方法1:m_addr.sin_addr.S_un.S_un_b.s_b1 = 192; 
           m_addr.sin_addr.S_un.S_un_b.s_b2 = 168; 
         m_addr.sin_addr.S_un.S_un_b.s_b3 = 0;
         m_addr.sin_addr.S_un.S_un_b.s_b4 = 1;
    方法2:  m_addr.sin_addr.S_un.S_un_w.s_w1 = (168 << 8) | 192; m_addr.sin_addr.S_un.S_un_w.s_w2 = (1 << 8) | 0;
    方法3:  m_addr.sin_addr.S_un.S_addr = (1 << 24) | (0 << 16) | (168 << 8) | 192
    方法4;    service.sin_addr.s_addr = inet_addr("127.0.0.1");
    */
    
    /*sockAddr.sin_addr.S_un.S_un_b.s_b1 = 127;
    sockAddr.sin_addr.S_un.S_un_b.s_b2 = 0;
    sockAddr.sin_addr.S_un.S_un_b.s_b3 = 0;
    sockAddr.sin_addr.S_un.S_un_b.s_b4 = 1;*/
    
    //绑定
    if (bind(severSocket, (sockaddr*)&sockAddr, sizeof(SOCKADDR_IN)) == SOCKET_ERROR)
    {
        printf("bind error! %d
", WSAGetLastError());
    }

    //监听5个
    if (listen(severSocket, 5) == SOCKET_ERROR)
    {
        printf("listen error!%d
", WSAGetLastError());
    }

    //创建一个一客户端连接的socket
    while (true)
    {    
        //接受来自客户端的sock,并存入客户端的数组中
        SOCKET sockLink = accept(severSocket, NULL, NULL);
        if (sockLink != INVALID_SOCKET)
        {
            printf("communication sucess!
");
        }
        
        g_psockSockLink[g_nSocketNum++] = sockLink;

        //每启动一个客户端,启动一条线程
        HANDLE hThread = CreateThread(NULL, 0, SeverThread, (LPVOID)sockLink, 0, NULL);
        //CloseHandle(hThread);
        if (hThread == NULL)
            continue;
    }

    closesocket(severSocket);
    closesocket(sockLink);
    WSACleanup();
    return 0;

}

 

 

//客户端
// WASClient.cpp : 定义控制台应用程序的入口点。
//

//#include <WinSock2.h>一定要在#include <Windows.h>前面

#include "stdafx.h"
#include <WinSock2.h>
#include <Windows.h>
#pragma comment (lib,"wSock32.lib")


//用于来自接受服务器的数据,避免的send中造成阻塞。
DWORD WINAPI RectThread(LPVOID lParam)
{
    SOCKET sockLink = (SOCKET)lParam;
    char pReturnValue[MAXBYTE] = { 0 };
    while (true)
    {
        recv(sockLink, pReturnValue, MAXBYTE, 0);
        printf("%s
", pReturnValue);
    }
    return 0;
}


int _tmain(int argc, _TCHAR* argv[])
{
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;
    
    //版本检测
    wVersionRequested = MAKEWORD(2, 2);

    err = WSAStartup(wVersionRequested, &wsaData);
    if (err != 0) {

        printf("WSAStartup failed with error: %d
", err);
        return 1;
    }

    if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) 
    {
        printf("Could not find a usable version of Winsock.dll
");
        WSACleanup();
        return 1;
    }
    else
        printf("The Winsock 2.2 dll was found okay
");


    //程序开始
    //创建socket-》连接connect-》发送send-》接受recv-》释放closesocke

    SOCKET clientSocket=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (clientSocket == INVALID_SOCKET)
    {
        printf("new socket error!");
    }

    SOCKADDR_IN sockAddr;
    //一定要把主机字节序换成网络字节序 并是short类型   htons()
    sockAddr.sin_port = htons(10086);
    sockAddr.sin_family = AF_INET;
    sockAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    

    //IP地址表示方法
    /*方法1:  m_addr.sin_addr.S_un.S_un_b.s_b1 = 192; m_addr.sin_addr.S_un.S_un_b.s_b2 = 168; m_addr.sin_addr.S_un.S_un_b.s_b3 = 0; m_addr.sin_addr.S_un.S_un_b.s_b4 = 1; 
    方法2:  m_addr.sin_addr.S_un.S_un_w.s_w1 = (168 << 8) | 192; m_addr.sin_addr.S_un.S_un_w.s_w2 = (1 << 8) | 0; 
    方法3:  m_addr.sin_addr.S_un.S_addr = (1 << 24) | (0 << 16) | (168 << 8) | 192*/

    /*sockAddr.sin_addr.S_un.S_un_b.s_b1 = 127;
    sockAddr.sin_addr.S_un.S_un_b.s_b2 = 0;
    sockAddr.sin_addr.S_un.S_un_b.s_b3 = 0;
    sockAddr.sin_addr.S_un.S_un_b.s_b4 = 1;
    */
    
    //连接
    if (connect(clientSocket, (sockaddr*)&sockAddr, sizeof(SOCKADDR_IN)) != SOCKET_ERROR)
    {
        printf("communication sucess!
");
    }

    char pClientBuf[MAXBYTE] = { 0 };        //存放输入数据

    //启动线程
    HANDLE hThread = CreateThread(NULL, 0, RectThread, (LPVOID)clientSocket, 0, NULL);
    if (hThread == NULL)
    {
        printf("CreateThread Error num:%d", GetLastError());
        CloseHandle(hThread);
    }
    CloseHandle(hThread);


    //请求连接,发送数据
    while (TRUE)
    {
        gets_s(pClientBuf);
        int  nSendErr=send(clientSocket, pClientBuf, MAXBYTE, 0);
        if (nSendErr== SOCKET_ERROR)
        {
            break;
        }
    }

    WSACleanup();
    closesocket(clientSocket);

    return 0;
}

 

 

注意点:
1.#include <WinSock2.h>一定要在#include <Windows.h>前面
如:
#include <WinSock2.h>
#include <Windows.h>

2.设定端口号时,一定要把主机字节序换成网络字节序 并是short类型 htons()
sockAddr.sin_port = htons(10086);

3.网络连接的流程:
服务端:创建socket->绑定bind->监听listen->接受客户端的套接字accept->接收recv->发送send->释放closesocket
客户端://创建socket-》连接connect-》发送send-》接受recv-》释放closesocke

4.getpeername(sockLink, (sockaddr*)&sockAddr, &len);
该函数可以根据当前的sock获取对象的sock地址,从而获取对应的IP地址、端口号,协议。

5.IP地址表示方法
方法1: m_addr.sin_addr.S_un.S_un_b.s_b1 = 192;
m_addr.sin_addr.S_un.S_un_b.s_b2 = 168;
m_addr.sin_addr.S_un.S_un_b.s_b3 = 0;
m_addr.sin_addr.S_un.S_un_b.s_b4 = 1;
方法2; service.sin_addr.s_addr = inet_addr("192.168.0.1");
方法3: m_addr.sin_addr.S_un.S_un_w.s_w1 = (168 << 8) | 192; m_addr.sin_addr.S_un.S_un_w.s_w2 = (1 << 8) | 0;
方法4: m_addr.sin_addr.S_un.S_addr = (1 << 24) | (0 << 16) | (168 << 8) | 192

6.SOCKET sockLink = accept(severSocket, NULL, NULL);
accept返回的是一个新的sock,该sock可以与客户端进行连接。就好比服务端与客户端建立一条管道,两者间随时可以进行通信。sockLink与clientSocket
是一对组合。因此不同的客户端启动,将会有不同的sock接入服务端。

7.
问题:客户端为什么专门启动一条线程来接受消息?
解析:首先,该程序是群聊功能,无法确定别人的客户端什么时候回发送消息过来。
其次,如何将send和recv写在同一个while中,当send发送消息后,如果别人客户端没有消息进来,此时就在recv阻塞,直到其他客户端发来消息才会解除,
该客户端才可以继续发送消息,无法实现一个客户端发送多次消息。

 

计算机网络基础

目录导航网络协议介绍OSI七层模型与TCP/IP五层模型介绍TCP/IP五层模型之物理层TCP/IP五层模型之数据链路层TCP/IP五层模型之网络层TCP/IP五层模型之传输层TCP/IP五层模型之应用层计算机网络基础网络协议介绍正文OSI七层模型与TCP/IP五... 查看详情

c/s模型之命名管道

说明:利用管道实现服务端与客户端之间的交互。效果等同于利用socket。命名管道(NamedPipe)是一种简单的进程间通信(IPC)机制,是服务器进程和一个或多个客户进程之间通信的单向或双向管道。其本质是文件读写、内存共享。采... 查看详情

c/s模型之udp协议

 说明:利用UDP协议,创建一个服务器和一个客户端。两者间进行通信。由客户端进行输入内容,而服务器将接受的内容进行再一次返回,并显示在服务端。//UDP_Seversock.cpp:定义控制台应用程序的入口点。#include"stdafx.h"#include&l... 查看详情

c/s模型:tcp,udp构建客户端和服务器端(bio实现

Java中提供了socket编程来构建客户端和服务器端TCP构建服务器端的步骤:(1)bind:绑定端口号(2)listen:监听客户端的连接请求(3)accept:返回和客户端连接的实例(4)read/write:进行读写操作,也就是和客户端进行交互(5)c... 查看详情

网络基础之osi模型与tcp模型

         ISO/OSI(penSystemInternetwork)         根据网络功能划分层次:        &nb 查看详情

6osi参考模型之“传输层”

目录TCPUDP1、TCP1.1、概述TCP提供一种面向连接的、可靠的字节流服务字节流服务(bytestreamservice):TCP不在字节流中插入记录标识符TCP对字节流的内容不作任何解释。TCP不知道传输的数据字节流是二进制数据,还是ASCII字符、EBCDIC字... 查看详情

玩转5g之--初探5g网络模型(osi模型和tcp/ip模型)

...:1.计算机网络: 2.分层思想:3.OSI七层参考模型:4.TCP/IP的四层、五层模型与OSI七层模型的对应关系: 5.端到端和点到点的区别? 6.数据封装与解封的过程与TCP/IP协议族,以及设备与层的对应关系... 查看详情

c/s模型-tcp(代码片段)

...erver代码client代码概述下图是基于TCP协议的客户端/服务器模型的一般流程建立连接过程:服务器调用socket()、bind()、listen()完成初始化后,调用accept()阻塞等待,处于监听端口的状态,客户端调用socket()初始化后ÿ... 查看详情

开源his之c/s选型

...诊收费开始,所以我并不打算一启动就写一个服务,并为之选型:TCP/IP、WebService、WCF等等。那怎么样开始呢?组件! 利用代理类创建UI使用的各种组件,一开始代理类其实是直接操作本地的实现类,之后我将让组件使用的是... 查看详情

网络基础之osi模型及tcp/ip协议栈

OSI参考模型开放系统互连参考模型为实现开放系统互连所建立的通信功能分层模型。其目的是为异种计算机互连提供一个共同的基础和标准框架,并为保持相关标准的一致性和兼容性提供共同的参考。这里所说的开放系统,实质... 查看详情

网络编程

...通过C或B都可以实现对S(服务器)的访问。 二、TCP/IP模型    实现网络通信的基础是网络通信协议。所谓“协议”就是通信计算机双方必须共同遵从的一组约定,例如怎样建立连接、怎样互相识别等,网络协议... 查看详情

计算机网络层次结构之osi模型和tcp/ip模型

...享3、分布式处理4、提高系统的可靠性5、负载均衡OSI参考模型我们用用一个口诀来记忆OSI参考模型:物联网淑慧试用。其从下向上具体划分为࿱ 查看详情

基于qt的在线打字练习软件助手(c/s模型)good

...QT中QTcpServer和QTcpSocket以及UI编程,实现了基于TCP协议的C/S模型在线打字练习软件助手,服务端处理各客户端打字数据,以及显示在线打字客户列表即实时更新打字数据。客户端可实现离线打字练习以及在线打字练习,其中在线打... 查看详情

102.tcp实现多线程连接与群聊(代码片段)

协议之间的关系socket在哪socket是什么 Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口... 查看详情

网络基础知识之tcp的三次握手

...复杂的体系结构,一般将网络分成多个层次,主要有tcp/ip模型和osi模型。分层的网络模型可以大大简化网络的复杂度,可以标准化网络接口简化模块化设计,确保技术的互操作性等作用。OSI模型的七层结构第一层:物理层,二进... 查看详情

网络之tcp/ip网络模型有哪几层(代码片段)

TCP/IP网络模型有哪几层对于同一台设备上的进程间通信,有很多种方式,比如有管道、消息队列、共享内存、信号等方式,而对于不同设备上的进程间通信,就需要网络通信,而设备是多样性的,所以要兼... 查看详情

网络之tcp/ip网络模型有哪几层(代码片段)

TCP/IP网络模型有哪几层对于同一台设备上的进程间通信,有很多种方式,比如有管道、消息队列、共享内存、信号等方式,而对于不同设备上的进程间通信,就需要网络通信,而设备是多样性的,所以要兼... 查看详情

一起talkandroid吧(第三百二十四回:android中网络通信之tcp通信模型一)(代码片段)

...户端的例子,这一回中咱们说的例子是网络通信之TCP通信模型。闲话休提,言归正转。让我们一起TalkAndroid吧!看官们,我们在上一回中通过具体的代码演示了如何进行TCP通信,不过这种通信属于最基本的通信,... 查看详情