精华内容
下载资源
问答
  • Windows 局域网语音对讲
    2021-07-30 11:39:28

    【实例简介】

    Windows下局域网语音对讲程序 ,亲测可行。

    【实例截图】

    【核心代码】

    new_

    └── new_

    ├── 发送

    │   ├── Debug

    │   │   ├── Sound_send.bsc

    │   │   ├── Sound_send.exe

    │   │   ├── Sound_send.ilk

    │   │   ├── Sound_send.obj

    │   │   ├── Sound_send.pch

    │   │   ├── Sound_send.pdb

    │   │   ├── Sound_send.sbr

    │   │   ├── StdAfx.obj

    │   │   ├── StdAfx.sbr

    │   │   ├── vc60.idb

    │   │   └── vc60.pdb

    │   ├── ReadMe.txt

    │   ├── Sound_send.cpp

    │   ├── Sound_send.dsp

    │   ├── Sound_send.dsw

    │   ├── Sound_send.ncb

    │   ├── Sound_send.opt

    │   ├── Sound_send.plg

    │   ├── StdAfx.cpp

    │   └── StdAfx.h

    └── 接收

    ├── Debug

    │   ├── Sound_win.bsc

    │   ├── Sound_win.exe

    │   ├── Sound_win.ilk

    │   ├── Sound_win.obj

    │   ├── Sound_win.pch

    │   ├── Sound_win.pdb

    │   ├── Sound_win.sbr

    │   ├── StdAfx.obj

    │   ├── StdAfx.sbr

    │   ├── vc60.idb

    │   └── vc60.pdb

    ├── ReadMe.txt

    ├── Sound_win.cpp

    ├── Sound_win.dsp

    ├── Sound_win.dsw

    ├── Sound_win.ncb

    ├── Sound_win.opt

    ├── Sound_win.plg

    ├── StdAfx.cpp

    └── StdAfx.h

    5 directories, 40 files

    更多相关内容
  • 可以实现局域网内同网段IP的实时通讯,同时支持多人聊天,本聊天软件采用Linux终端输入输出,可以自行添加UI实现更加完善的聊天工具!
  • 局域网聊天

    2018-08-21 16:52:23
    本功能设计实现了局域网聊天系统的群聊以及私聊部分,群聊部分借鉴《Qt及Qt Quick开发实战精解》中的实例,添加了私聊功能,适合初学者学习,博客链接:...
  • 本工具包含两个发布版本和一个工程源代码,在windows下运行源文件时候,可能需要将文件转编码,防止乱码的问题。参考性极强,功能展示详见 https://blog.csdn.net/qq_41488943/article/details/104918643
  • 局域网聊天室.rar

    2019-07-31 16:33:32
    基于单播的局域网聊天室,用的是windows C,程序会有些小问题没优化
  • 本文实例为大家分享了C#使用Socket实现局域网聊天的具体代码,供大家参考,具体内容如下 先运行一个java写的局域网聊天,效果图如下 后使用c#图形修改如下: C#代码: servlet服务端 using System; using System....
  • 通过利用Qt应用程序框架提供的QTcpSocket和QUdpSocket类进行网络通信,在windows平台(支持跨平台,将源码在目标平台重编译即可)上实现了两大功能: 1)实现客户端与服务器端之间文件传输功能; 2)实现客户端与...
  • QT版局域网聊天系统

    2018-09-05 11:17:51
    项目介绍:仿腾讯QQ软件的界面,完成了一个局域网聊天工具。基本的聊天会话功能主要使用Udp广播方式在群组里进行消息会话,聊天内容实时地显示在聊天框里。传输文件时,用户聊天窗视不同角色分别扮演服务器和客户端...
  • 局域网大文件传送,windows下socket套接字,一个Server端,一个Client端,先运行Server端,启动监听,Client输入Server端的IP再连接。就可以聊天和发送文件。
  • python socket局域网聊天文件传输程序 ,界面用wxPython编写,实现多线程 ,跨平台,windows和linux下过行都可
  • C语言用UDP实现局域网聊天程序源码,能够实现用户的注册,登陆,群发和私聊的功能。
  • Windows的socket编程

    目录

    1 聊天室

    1.1 common.h

    1.2 服务端

    1.3 客户端

    1.4 遇到的一些问题

    2 附录

    2.1 线程


     

    聊天室具体知识点

    1、基本Socket API函数及使用方法

    socket,bind,connect,accept,listen,send,recv,closesocket,htonl(host to network long),ntohl(network to host long),inet_addr(点分十进制IP转成长整型),inet_ntoa(网络地址转成点分十进制IP),getsocketname(获取一个套接字名字),getpeername(获取与套接口相连的端地址)

    2、用户验证登陆,用户私聊,用户之间传文件等

    3、自定义协议,粘包问题,心跳包处理异常掉线,文件续传功能

    1 聊天室

    服务端

    1、WinSock环境加载

    WSAStartup(MAKEWORD(2, 2), &wsaData)——MAKEWORD,宏产生一个WORD值,标识版本

    2、创建套接字——socket

    SOCKET WSAAPI socket( int af, int type, int protocol );——参数分别是IP地址簇,传输模式,传输协议

    成功则返回一个套接字——套接字与IP协议,传输协议,传输模式绑定在一起。

    3、套接字绑定IP和端口——bind

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

    把socket与IP地址和端口绑定在一起。

    sockaddr_in和sockaddr二者字节数相同,都是16字节

    struct sockaddr_in{
        short sin_family; //2字节
        u_short sin_port;  //2字节
        struct in_addr sin_addr;  //IPv4是4字节
        char sin_zero[8];  
    };
    
    struct sockaddr{
        u_short sa_family;  //2字节
        char sa_data[14];  //2+4+8
    };

    4、

    ReadMe

    
    +++++++++++++++++++++++++++++++
    +                             +
    +          聊天室              +
    +                             +
    +++++++++++++++++++++++++++++++
    
    
    1 服务端
    1.1 服务端程序流程
    加载环境(WSAStartup)
    ——创建SOCKET(socket)
    ——SOCKET绑定IP和端口(bind)
    ——监听SOCKET(listen)
    ——等待客户端连接(accept)
    ——发送数据(send)
    ——接收数据(recv)
    ——关闭SOCKET(clostsocket)
    卸载环境(WSACleanup)
    PS:
        
    1.2 socket链表
    对于连接到服务器端的客户端(socket),服务器端要定义一个链表来管理它们——因为客户端的连接的断开具有随机性,所以使用链表
    
    1.3 服务器监听线程
    当服务器端调用accept函数等待客户端连接时,是出于阻塞状态的,所以了避免阻塞主线程,为该过程创建一个线程。——问题是如何让主线程等待该线程结束
    
    1.4 客户消息处理线程
    每来一个客户端连接的socket,就创建一个处理线程来与客户端进行交互
    
    1.5 自定义协议
    服务器端与客户但进行交互需要定义协议
    协议分为协议头和协议体——协议头中定义了交互的消息类型,长度等信息;协议体中则是交互的数据。
    
    
    2 客户端
    2.1 客户端程序流程
    加载环境(WSAStartup)
    ——创建SOCKET(socket)
    ——连接服务器(connect):需要IP地址和端口
    ——发送数据(send)
    ——接收数据(recv)
    ——关闭SOCKET(closesocket)
    卸载环境(WSACleanup)
    
    PS:
    
    2.2 登陆与验证
    客户端登陆需要发送用户信息以及密码等给服务器端——这里也要用到自定义协议来承载这些信息
    服务器端收到用户信息,检查是否合法,合法则向客户端返回验证通过信息
    
    2.3 显示其他在线用户信息,显示聊天信息
    (1)连接服务器并收发消息的线程
    (2)显示接收信息的线程
    
    
    3 自定义协议
    这一部分可以先设计简单一些——协议头包含消息类型,协议体的大小
    对于更复杂的协议,可以学习参考TCP协议,HTTP协议等
    3.1 协议头
    
    3.2 协议体
    
    
    4 同步I/O和异步I/O
    
    
    
    5 聊天室的一些特性
    5.1 用户上线/下线通知
    (1)某用户上线,则把该用户信息发送给其他用户;并把其他用户在线信息发送给该用户
    (2)用户下线,则把该用户的下线信息发送给其他用户
    
    

     

    1.1 common.h

    服务端和客户端所共有的一部分。

    1、加载/卸载socket环境

    2、发送数据,接收数据

    3、服务端和客户端通信的消息

    common.h

    
    WinSocket——局域网聊天室
    17/100
    weixin_43390831
    
    
    C++
    #ifndef __COMMON_H__
    #define __COMMON_H__
    
    #include <iostream>
    #include <list>
    #include <string>
    #include <vector>
    #include <winsock2.h>
    #pragma comment(lib, "ws32_lib")
    
    #define MAX_IP_LEN 16  //放在common.cpp中
    
    #define MAX_USER_LEN 50  //用户名最大长度
    #define MAX_PASS_LEN 50  //密码最大长度
    
    
    //消息类型
    #define MSG_TYPE_LOGIN 1  
    
    #define MSG_TYPE_CLIENT_ONLINE 4
    #define MSG_TYPE_CLIENT_OFFLINE 5
    
    #define MSG_TEXT_TOALL 6
    
    
    //消息类
    class CMsgHead{
    public:
        char m_type;  //当前消息类型
    	UINT m_cnLen;  //协议头之后的消息长度——body的长度
    	
    	CMsgHead()
    	{
    		m_type = -1;
    		m_cnLen = 0;
    	}
    	
    	CMsgHead(char nType)
    	{
    		m_type = nType;
    		m_cnLen = 0;
    	}
    	
    	CMsgHead(char nType, UINT cnLen)
    	{
    		m_type = nType;
    		m_cnLen = cnLen;
    	}
    };
    
    
    // 登陆信息 
    struct CLoginInfo
    {
        TCHAR m_szUser[MAX_USER_LEN];
        TCHAR m_szPass[MAX_PASS_LEN];
        
        CLoginInfo()
        {
            memset(m_szUser, 0, MAX_USER_LEN);
            memset(m_szPass, 0, MAX_PASS_LEN);
        }
        
        CLoginInfo(TCHAR * lpszUser, TCHAR * lpszPass)
        {
            if (!lpszUser || !lpszPass) return;
            memcpy(m_szUser, lpszUser, MAX_USER_LEN);
            memcpy(m_szPass, lpszPass, MAX_PASS_LEN);
        }
    };
    
    
    //客户信息
    struct CClientInfo
    {
        TCHAR m_szIpAddr[MAX_IP_LEN];
        CClientInfo()
    	{
    	    memset(m_szIpAddr, 0, MAX_IP_LEN);
    	} 
    	
    	CClientInfo(TCHAR * lpszIpAddr)
    	{
    	    if (!lpszIpAddr) return;
    	    memset(m_szIpAddr, 0, MAX_IP_LEN);
    	    memcpy(m_szIpAddr, lpszIpAddr, MAX_IP_LEN);
    	}
    	
    	CClientInfo(CClientInfo *tInfo)
    	{
    	    if (!tInfo) return;
    	    memset(m_szIpAddr, 0, MAX_IP_LEN);
    	    memcpy(m_szIpAddr, tInfo->m_szIpAddr, MAX_IP_LEN);
    	}
    }; 
    
    
    //加载socket环境
    BOOL WinSockInit();
    //卸载socket环境
    void WinSockUnload();
    
    
    //select
    //探测一个或多个socket的状态,去执行同步I/O 
    //如果必要,可以等待 
    BOOL SOCKET_Select(SOCKET hSocket, int nTimeOut = 100, BOOL bRead = TRUE);
    
    //发送数据
    int SendData(SOCKET sock, char * buf, unsigned int len);
    //接收数据
    int RecvData(SOCKET sock, char * buf, unsigned int len);
    
    
    #endif
    
    
    
     

    common.cpp

    
    #include "common.h"
    
    BOOL WinSockInit()
    {
        WSADATA wsaData = {0};
        if (WSAStartup(MAKEWORD(2, 2), &wsaData) == NO_ERROR){
            return TRUE;
        }
        else{
            return FALSE;
        }
    }
    
    void WinSockUnload()
    {
        WSACleanup();
    }
    
    BOOL SOCKET_Select(SOCKET hSocket, int nTimeOut, BOOL bRead)
    {
        //int WSAAPI select (int nfds/*for compatibility*/, fd_set *readfds, 
    	//                     fd_set *writefds, fd_set *exceptfds, const timeval *timeout); 
    	
    	// readfds——listen and accept succeed,data is available for reading
    	//            connection has been closed/reset/terminated
    	//            说明缓冲的数据是可读的 ,此时可以调用recv函数 
    	// writefds——connect succeed,data can be sent 
    	//            说明缓冲可写 ,此时可以调用send函数 
        fd_set readfds;
    	fd_set writefds;
    	fd_set exceptfds;
    	
    	FD_ZERO(&readfds);
    	FD_ZERO(&writefds);
    	FD_ZERO(&exceptfds);
    	
    	FD_SET(hSocket, &readfds);
    	
    	timeval timeout;
    	timeout.tv_sec = nTimeOut;
    	timeout.tv_usec = 0;
    	
    	int iRet = 0;  //返回socket句柄的总数
    	               //在timeout时间内,监测socket的状态 
    	iRet = select(0, &readfds, NULL, NULL, &timeout);
    	
    	if (iRet <= 0)
    	{
    	    std::cout << "error or timeout" << std::endl;
    	    return FALSE;
    	}
    	 
        return TRUE;
    }
    
    
    //发送数据
    //int send(__in SOCKET s, __out char * buf/*发送缓冲区*/, __in int len/*缓冲区大小*/, __in int flags);  //返回发送的字节数
    int SendData(SOCKET sock, char * buf, unsigned int len)
    {
    	unsigned int offset = 0;
    	while (offset < len)
    	{
    		int nSend = send(sock, buf + offset, len - offset, 0);
    		if (SOCKET_ERROR == nSend)
    		{
    			return -1;
    		}
    		offset += nSend;
    	}
    	return 1;
    }
    
    
    //接收数据
    //int recv(__in SOCKET s, __out char * buf, __in int len/*缓冲区大小*/, __in int flags);  //返回接收了多少字节数据,接收失败则返回SOCKET_ERROR(值为-1)	
    int RecvData(SOCKET sock, char * buf, unsigned int len)
    {
    	unsigned int offset = 0;  //buf起点的偏移,之后接收数据存放在offset之后
    	while (offset < len)
    	{
    	    int nRecv = recv(sock, buf + offset, len - offset, 0);  //把数据存放在偏移位置之后,避免之前的数据被覆盖
            if (SOCKET_ERROR == nRecv)
    		{
                return -1;
            }
            if (0 == nRecv)  //接收完所有数据
    		{
                return 0;
            }
            
            offset += nRecv;		
    	}
    	return 1;
    }
    

     

    1.2 服务端

    1、服务端编码流程

    CServer.h

    #ifndef __CSERVER_H__
    #define __CSERVER_H__
    
    #include "common.h"
    
    #define __COMMON_H__
    
    //#define __COMMON_H__
    using std::list;
    using std::string;
    
    /*
    1、加载Socket环境
    2、新建Socket
    3、绑定Socket
    4、监听Socket
    5、等待Socket连接
    6、发送数据/接收数据
    7、卸载Socket环境 
    */
    
    //服务端的设计——
    
    //(1) 一个监听线程,等待客户端的连接
    
    //(2) 一个处理线程,接收客户端数据,并处理
    
    //(3) 有些接口需要暴露,有些接口不能暴露
    
    //(4) 如何只提供全局唯一服务类对象——static
    
    //(5) void*空间可以强制转换成想要的类型
    
    //(6) 对于错误码,可以把它特化成类——继承自基类错误码,然后定义个处理函数,这样就可以用多态来处理不同的错误码(上报错误信息)
    
    
    //服务器端管理每个客户端节点
    //记录每个连接的客户端节点
    class CClientItem
    {
    public:
        SOCKET m_Socket;  //accept返回的客户端socket
    	TCHAR m_szIpAddr[MAX_IP_LEN];  //IP地址
    	HANDLE m_hThread;  //处理该客户单socket的线程
    	
    	CClientItem()
    	{
    		m_Socket = INVALID_SOCKET;
    		ZeroMemory(m_szIpAddr, MAX_IP_LEN);
    		m_hThread = NULL;
    	}
    };
    
    
    //一个服务器类应该具备哪些接口和成员 
    class CServer{
    private:
        void RemoveTargetClient(CClientItem *pItem);
    
    
    public:
        CServer();
        ~CServer();
        
        BOOL StartServer(UINT nPort);
        void StopServer();
        
        //处理客户端发来的请求数据 
        BOOL ProcessMsg(CMsgHead &msg, TCHAR *pCnt, CClientItem *pClient);  //入参为协议头,协议体,客户端信息
    
    //--------------两个线程的参数是固定的,所以设置成static,无需this参数-------------------//
        //静态的原因:acceptProc只有一个参数,static可以没有this参数 
        static DWORD WINAPI acceptProc(LPVOID lpParameter);  //开了线程,用于监听端口等待客户端的连接 
        
        //用于处理不同的连接,每来一个连接,则开启一个线程 
        static DWORD WINAPI ClientThreadProc(LPVOID pParameter);  //入参为客户端节点信息——socket,IP,线程ID
    //---------------------------------------------------------------------------------------//
    	
    	//检查用户的登陆信息,正确则授权 
    	BOOL CheckUserInfo(TCHAR * lpszUser, TCHAR * lpszPass);
    	
    	//把其他用户的在线消息发送给该上线用户——全量 
    	BOOL SendOtherUserInfo(CClientItem * pClient);
    	
    	//把登陆用户发送给其他用户
    	
    	//当有用户退出时,把用户退出的消息发送给其他玩家——增量 
    	BOOL SendTargetToOnlineUser(CClientItem * pClient, BOOL bOnLine);
    	
    	//等待其他线程完成 
    	DWORD WaitForAllThreads(UINT nCount, HANDLE * lpHandles, 
    	                            BOOL bWaitAll, DWORD dwMillionseconds); 
    private:
    	SOCKET m_ListenSock;  //套接字 
    	UINT m_nPort;  //端口
    	//定义该静态变量后,需要在源文件中进行初始化,而不能在头文件中进行初始化
    	//否则会报——重复定义错误。如果不在头文件中初始化,且也不在源文件中初始化,则无法使用该变量 
    	static list<CClientItem*> m_ClientList;  //一个队列或链表来管理客户端的连接
    };
    
    
    
    //获取全局唯一服务器对象
    CServer * GetServer();
    
    
    
    #endif

    CServer.cpp

    
    #include "CServer.h"
    #define __CSERVER_H__
    
    CServer * GetServer(){  //这里使用的是单例模式,尽管多次调用,也只会有一个CServer实例
    	static CServer * pServer = NULL;
    	if (!pServer){
    		pServer = new CServer();  //没有考虑到多线程,即只有一个线程会调用这个函数,主线程
    	}
    	return pServer;
    }
    
    CServer::CServer(){
    	m_ListenSock = INVALID_SOCKET;
    	m_nPort = 0;
    }
    
    CServer::~CServer(){
    
    }
    
    list<CClientItem*> CServer::m_ClientList;
    
    //---------------------------//
    //         具体功能实现      //
    //---------------------------//
    
    //-------------------------------//
    // 
    //启动服务 
    //启动接收服务,避免阻塞主界面 
    //有多个客户端来连接,如果不用线程,使用while循环则会阻塞在这里 
    //-------------------------------//
    BOOL CServer::StartServer(UINT nPort){
    	m_nPort = nPort;
        //启动一个线程
        DWORD threadId = 0;
        //启动accept线程,等待连接,避免阻塞主界面 
    	HANDLE hThread = CreateThread(
    	                        NULL, 
    	                        0, 
    							CServer::acceptProc, 
    	                        this, //这里用了this指针目的是什么 
    							0, 
    							&threadId);
    	if (hThread == NULL)
    	{
    		std::cout << "创建监听线程失败----" << std::endl; 
    	    return FALSE;
    	}
    	
    	WaitForSingleObject(hThread, INFINITE);
    	return TRUE;
    }
    
    
    //删除客户端连接——这里的代码要注意一下
    void CServer::RemoveTargetClient(CClientItem *pItem)
    {
    	if (!pItem) return;
    	//首先找这个节点,如果找到则删除链表中的这个节点
    	
        //CServer::m_ClientList.remove(pItem);	
    	
    	//当不使用该socket时,则关闭socket
    	closesocket(pItem->m_Socket);  //关闭 socket
    	CloseHandle(pItem->m_hThread);  //关闭线程句柄
    	delete pItem;
    }
    
    //把其他用户的在线消息发送给该上线用户 
    BOOL CServer::SendOtherUserInfo(CClientItem * pClient)
    {
        if (m_ClientList.size() <= 1)
        {
            return TRUE;
        }
        
        std::vector<CClientInfo> vecInfo;
        
        for (list<CClientItem*>::iterator itr = m_ClientList.begin(); itr != m_ClientList.end(); ++itr)
        {
            if (!*itr || *itr == pClient) continue;
            CClientInfo info = CClientInfo((*itr)->m_szIpAddr);
            vecInfo.push_back(info);
        }
        //发送的是vec,接收的时候也要用vec解释 
        UINT nLen = vecInfo.size() * sizeof(CClientInfo);
        CMsgHead msgHead(MSG_TYPE_CLIENT_ONLINE, nLen);
        
        int iHeadSend = SendData(pClient->m_Socket, (char *)&msgHead, sizeof(msgHead));
    	int iBodySend = SendData(pClient->m_Socket, (char *)&vecInfo, nLen);
    	
    	if (iHeadSend <= 0 || iBodySend <= 0)
    	{
    	    RemoveTargetClient(pClient);
    	    return FALSE;
    	} 
        
        return TRUE;
        
    }
    
    //发送某用户的上线或下线消息给其他用户 
    //把发送事件(即其他在线用户)放入到一个队列中,单独一个线程负责发送 
    BOOL CServer::SendTargetToOnlineUser(CClientItem * pClient, BOOL bOnLine)
    {
        if (m_ClientList.size() <= 1)
        {
            return TRUE;
        }
        
        CClientInfo clientInfo = CClientInfo(pClient->m_szIpAddr);
        
        //发送类型 
        UINT nType = 0;
    	if (bOnLine)
    	{
    	    nType = MSG_TYPE_CLIENT_ONLINE;
    	}
    	else
    	{
    	    nType = MSG_TYPE_CLIENT_OFFLINE;
    	}
        CMsgHead msgHead(nType, sizeof(clientInfo));
        
        for (list<CClientItem*>::iterator itr = m_ClientList.begin(); itr != m_ClientList.end(); ++itr)
        {
            if (!*itr || *itr == pClient) continue;
    	    int iHeadSend = SendData((*itr)->m_Socket, (char *)&msgHead, sizeof(msgHead));
    		int iBodySend = SendData((*itr)->m_Socket, (char *)&clientInfo, sizeof(clientInfo));
    	}
        
        return TRUE;
    }
    
    //客户端消息处理程序
    //根据客户端发送过来的消息类型进行不同的处理 
    BOOL CServer::ProcessMsg(CMsgHead &msg, TCHAR *pCnt, CClientItem *pClient)
    {
        //客户端发送Msg 
    	//
    	switch (msg.m_type)
    	{
    	    case MSG_TYPE_LOGIN:
    	        {
    	            //验证登陆信息
    				//如果验证成功,则把授权结果返回给客户端 
    	            BOOL bAuthResult = FALSE; 
    	            CLoginInfo * pLoginInfo = (CLoginInfo *)pCnt; 
    	            BOOL bRet = CheckUserInfo(pLoginInfo->m_szUser, pLoginInfo->m_szPass);
    	            
    	            if (bRet)
    	            {
    	                bAuthResult = TRUE;
    	            }
    	            
    	            CMsgHead msgHead(MSG_TYPE_LOGIN, sizeof(bAuthResult));
    	            int iHead = SendData(pClient->m_Socket, (char *)&msgHead, sizeof(msgHead));
    	            int iBody = SendData(pClient->m_Socket, (char *)&bAuthResult, sizeof(bAuthResult));
    	            if (iHead <= 0 || iBody <= 0)
    	            {
    	                SendTargetToOnlineUser(pClient, FALSE); 
    	                RemoveTargetClient(pClient);
    	                return TRUE;
    	            }
    	            
    	            //如果该用户认证成功,则把其他在线用户的信息发送给该用户
    				if (bAuthResult == TRUE)
    				{
    				    SendOtherUserInfo(pClient);
    				    SendTargetToOnlineUser(pClient, TRUE);
    				}
    	        }
    	        break;
    	    
    	    default: break;
    	    
    	} 
    	
    	return TRUE;
    }
    
    
    //客户端连接处理程序
    DWORD WINAPI CServer::ClientThreadProc(LPVOID pParameter)
    {
    	CClientItem *pClient = (CClientItem *)pParameter;
    	while (TRUE)
    	{
            if (SOCKET_Select(pClient->m_Socket))
    		{
    			CMsgHead msg;
    			//首次接收数据,先接收协议头
    			int iRet = RecvData(pClient->m_Socket, (char *)&msg, sizeof(msg));
    			if (iRet < 0)  //小于0说明接收数据异常,把客户端删除
    			{
    			    m_ClientList.remove(pClient);  //——移除无效节点 
    				return -1;  //错误返回一个数字
    			}
    			
    			//如果接收协议头成功,则继续接收数据
    			TCHAR * pszBuf = new TCHAR(msg.m_cnLen);  //分配一个协议体——body,那么大的长度缓冲区
    			ZeroMemory(pszBuf, msg.m_cnLen);
    			
    			iRet = RecvData(pClient->m_Socket, (char *)pszBuf, msg.m_cnLen);  //接收数据
    			if (iRet <= 0){
    				//接收数据失败
    				delete [] pszBuf;
    				m_ClientList.remove(pClient);  //——移除无效节点 
    				return -1;  //错误返回一个数字
    			}
    			
    			GetServer()->ProcessMsg(msg, pszBuf, pClient);  //执行客户端信息处理函数
    			delete [] pszBuf;
    		}
    	}
    	return 1;
    }
    
    BOOL CServer::CheckUserInfo(TCHAR * lpszUser, TCHAR * lpszPass)
    {
        return TRUE;  //可以到数据库中校验,也不可在服务器本地校验 
    }
    
    
    //---------------------------//
    //
    //--------------------------//
    DWORD CServer::acceptProc(LPVOID lpParameter)
    {
        CServer* pThis = (CServer*)lpParameter;
        
        //----建立一个套接字----// 
        pThis->m_ListenSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        if (pThis->m_ListenSock == INVALID_SOCKET){
    	    std::cout << "建立socket失败----create socket failed : " << GetLastError() << std::endl;
    		return 1;
    	}
    	std::cout << "socket success" << std::endl;
    	
    	//----套接字绑定IP地址和端口----//
    	// int bind(_In_ SOCKET s, _In_ const struct sockaddr *name, _In_ int namelen) 
    	sockaddr_in sServer = {0};
    	sServer.sin_family = AF_INET;
    	//sServer.sin_addr.s_addr = htonl(INADDR_ANY);  //host to network long, sin_addr是一个u_long(IPv4地址是一个无符号long)
    	                                              //INADDR_ANY——绑定本地的所有IP
    	//unsigned long WSAAPI inet_addr(__in IN const char FAR * cp); 
    	sServer.sin_addr.s_addr = inet_addr("127.0.0.1");  //绑定特定IP地址 
    			
    	sServer.sin_port = htons(pThis->m_nPort);  //host to network short
    	//no error:return 0;error:return SOCKET_ERROR 
    	if (bind(pThis->m_ListenSock, (sockaddr*)&sServer, sizeof(sServer)) == SOCKET_ERROR){
    		std::cout << "绑定端口失败----bind error : " << GetLastError() << std::endl; 
    		return 1;
    	}
    	std::cout << "bind success" << std::endl;
    	
    	//----监听套接字----//
    	// int listen(_In_ SOCKET s/*绑定了IP和端口的套接字*/,  _In_ int backlog/*监听队列中允许保持的尚未处理的最大连接数*/);
    	//backlog——多个客户端同时连接服务器的限制(是一个并发限制),最大值是 SOMAXCONN 
    	//no error : return 0; error : return SOCKET_ERROR 
    	if (listen(pThis->m_ListenSock, SOMAXCONN) == SOCKET_ERROR){
    		std::cout << "监听套接字失败----listen error : " << GetLastError() <<std::endl;
    	}
    	std::cout << "listen success" << std::endl;
    	
    	
    	//----等待客户端连接----// 
    	while (TRUE){
    		sockaddr_in clientAddr = {0};  
    		int iLen = sizeof(sockaddr_in);
    		// SOCKET accept(_In_ SOCKET s/*server socket*/, _Out_ struct sockaddr *addr/*client addr_info*/, _Inout_ int *addrlen);
    		// 监听服务端socket成功后,可以获得客户端IP和端口信息
    		// 返回一个与客户端通信的 socket
    		// 一直处于监听状态 
    		// accept函数是阻塞的——当没有客户端连接时,一直在这里等待连接
    		// 可以用select函数对accept进行异步处理——当没有客户端连接时可以执行accept后面的语句 
    		SOCKET accSock = accept(pThis->m_ListenSock, (sockaddr *)&clientAddr, &iLen);
    		if (accSock == INVALID_SOCKET){
    			continue;
    		}
    			
    		//把接收到的客户端放在一个链表里,用于客户端的收发	
    		  //放在accept调用后面
    	    {
    		    //在accept函数接收到socket后,加入队列
    		    CClientItem *pItem = new CClientItem();
    			pItem->m_Socket = accSock;
    			
    			char* strIp = new char[MAX_IP_LEN]; 
    			//strIp = inet_ntoa(clientAddr.sin_addr);  //返回char*类型,inet_ntoa将网络地址转成点分十进制IP字符串
    			                                         //inet_addr,将点分十进制IP转成网络字节IP(一个u_long类型)
    			
    			memcpy(strIp, inet_ntoa(clientAddr.sin_addr), MAX_IP_LEN);
    			memcpy(pItem->m_szIpAddr, strIp, MAX_IP_LEN);
    			
    		    //创建新线程并挂起 
    			pItem->m_hThread = CreateThread(NULL, 0, ClientThreadProc, pItem, CREATE_SUSPENDED, NULL);
    		    m_ClientList.push_back(pItem);
    		    ResumeThread(pItem->m_hThread);  //唤醒线程 
    	    }
    	}
    	
    	std::cout << "acceptPro will be done" << std::endl; 
    	return 0;
    }
    
    DWORD CServer::WaitForAllThreads(UINT nCount, HANDLE * lpHandles, 
    	                            BOOL bWaitAll = TRUE, DWORD dwMillionseconds = INFINITE)
    {
        //这个函数在哪里调用比较合适呢
    	//必须确保线程开启后,该函数才会被调用 
        HANDLE * hThreads = new HANDLE[m_ClientList.size()];
        
        int i = 0;
        for (list<CClientItem*>::iterator itr = m_ClientList.begin(); itr != m_ClientList.end(); ++itr, ++i)
        {
            if (!*itr) continue;
    	    hThreads[i] = (*itr)->m_hThread;
    	}
    	
    	return WaitForAllThreads(i, hThreads, bWaitAll, dwMillionseconds);
    	
    }
    
    
    

     

    1.3 客户端

    CClient.h

    #ifndef __CCLIENT_H__
    #define __CCLIENT_H__ 
    #include <string>
    #include "common.h"
    using std::string;
    
    //客户端,创建一个sokcet
    //然后就可以使用该socket连接到服务器端的IP和port 
    class CClient{
    
    public:
        CClient();
    	~CClient();
    	
    	//
    	BOOL WINAPI ConnectServer(char * lpszIpAddr, UINT nPort, 
    	                                 char * lpszUser, char * lpszPswd);
        
    	//把登陆的用户名,密码发送给服务器端
    	//此时就需要自定义协议——协议头和协议体 
        BOOL LoginProcess();
        
        //处理服务器端发送过来的程序 
        void ProcessMsg(CMsgHead msg, TCHAR * body);
        
        //发送聊天消息给服务端
    	BOOL SendText();
        
    private:
    
        TCHAR m_szServerIp[MAX_IP_LEN];
        UINT m_nPort;
        TCHAR m_szUser[MAX_USER_LEN];
        TCHAR m_szPass[MAX_PASS_LEN];
        
        SOCKET m_ConnectSocket;
    };
    
    #endif

    CClient.cpp

    
    #include "CClient.h"
    
    
    CClient::CClient()
    {
        memset(m_szServerIp, 0, MAX_IP_LEN);
        memset(m_szUser, 0, MAX_USER_LEN);
        memset(m_szPass, 0, MAX_PASS_LEN);
        
        m_ConnectSocket = INVALID_SOCKET;
    }
    
    CClient::~CClient()
    {
    
    } 
    
    void CClient::ProcessMsg(CMsgHead msg, TCHAR * body)
    {
        switch (msg.m_type)
        {
        case MSG_TEXT_TOALL : 
            {
                //服务器收到聊天记录,并把该聊天记录发送其他客户端 
                //客户但接收到这个消息,说明发过来的是线上某个客户聊天信息 
    			 
            }
            break;
        case MSG_TYPE_CLIENT_ONLINE : 
            {
                //服务器收到上线消息,并把上线用户的信息发送其他给客户端 
                //客户端收到这个消息,说明发过来的是上线用户的信息
    			 
            }
            break;
        case MSG_TYPE_CLIENT_OFFLINE : 
            {
                //服务器收到下线消息,并把下线消息发送给其他客户端
    			//客户端收到这个消息,说明发过来的是下线用户的信息 
            }
            break;
        default : break; 
        }
    }
    
    BOOL CClient::LoginProcess()
    {
        CMsgHead msgHead(MSG_TYPE_LOGIN, sizeof(CLoginInfo));
        CLoginInfo loginInfo(m_szUser, m_szPass);
        
        //先发送协议头,再发送协议体 
        int iHeadSend = SendData(m_ConnectSocket, (char *)&loginInfo, sizeof(msgHead));
        int iBodySend = SendData(m_ConnectSocket, (char *)&loginInfo, sizeof(loginInfo));
        
        if (iHeadSend >= 0 && iBodySend >= 0)
        {
            return TRUE;
        } 
        return FALSE;
    }
    
    
    BOOL CClient::ConnectServer(char * lpszIpAddr, UINT nPort, 
                                     char * lpszUser, char * lpszPass)
    {
        if (!lpszIpAddr || nPort < 1 || nPort > 65535 || !lpszUser || !lpszPass)
        {
            return FALSE;
        }
        
        m_nPort = nPort;
    	memcpy(m_szServerIp, lpszIpAddr, MAX_IP_LEN);
    	memcpy(m_szUser, lpszUser, MAX_USER_LEN);
    	memcpy(m_szPass, lpszPass, MAX_PASS_LEN);
    	
    	m_ConnectSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    	if (m_ConnectSocket == INVALID_SOCKET)
    	{
    	    std::cout << "login error" << std::endl;
    	    return FALSE;
    	}
    	
    	//本地地址和网络地址的转换 
    	sockaddr_in server = {0};
    	server.sin_family = AF_INET;
    	server.sin_port = htons(m_nPort);  //u_int -> u_short
    	server.sin_addr.s_addr = inet_addr(lpszIpAddr); 
    	
    	
    	if (connect(m_ConnectSocket, (sockaddr*)&server, sizeof(sockaddr)) == SOCKET_ERROR)
    	{  //连接失败,则关闭socket 
    	    std::cout << "connect failed" << std::endl;
    	    closesocket(m_ConnectSocket);
    		return FALSE;
    	}
    	
    	BOOL bRet = LoginProcess();  //登陆把用户名和密码传给服务器端 
    	if (!bRet)
    	{
    	   //连接失败 
    	    std::cout << "Login failed" << std::endl;
    	    closesocket(m_ConnectSocket);
    		return FALSE;
    	}
    	
    	while (TRUE)
    	{
    	    //判断socket是否为空,为空,则执行后面的步骤,避免阻塞 
    	    if (SOCKET_Select(m_ConnectSocket))
    	    {
    	        //接收服务端发送过来的验证信息 
    	        CMsgHead msg;
    	        int iRet = RecvData(m_ConnectSocket, (char *)&msg, sizeof(msg));
    	        if (iRet <= 0)
    	        {
    	            //接收协议头失败 
    	            std::cout << "recv server msg head failed" << std::endl;
    	            closesocket(m_ConnectSocket);
    	            return FALSE;
    	        }
    	        
    	        //验证成功后接收服务器端消息 
    	        TCHAR * pszBuf =new TCHAR[msg.m_cnLen];
    	        memset(pszBuf, 0, msg.m_cnLen);
    	        
    	        //接收协议体失败 
    	        iRet = RecvData(m_ConnectSocket, (char *)pszBuf, msg.m_cnLen);
    	        
    	        if (iRet <= 0)
    	        {
    	            delete [] pszBuf;
    	            std::cout << "recv server msg body failed" << std::endl;
    	            closesocket(m_ConnectSocket);
    	            return FALSE;
    	        }
    	        
    	        //验证成功,则交给处理过程去处理 
    			ProcessMsg(msg, pszBuf);
    			delete [] pszBuf;   
    	    }
    	}
    	
    	return TRUE;	 
    }
    
    
    BOOL CClient::SendText()
    {
        printf("%s : ", m_szUser);
    	std::string str;
    	std::cin >> str;
    	
    	UINT nLen = str.length() + 1;
    	char * pText = new char[nLen];
    	memcpy(pText, str.c_str(), nLen);
    	
    	CMsgHead msgHead(MSG_TEXT_TOALL, nLen);
    	
    	int iHeadSend = SendData(m_ConnectSocket, (char *)&msgHead, sizeof(msgHead));
    	int iBodySend = SendData(m_ConnectSocket, (char *)pText, nLen);
    	
    	if (iHeadSend <= 0 || iBodySend <= 0)
    	{
    	    std::cout << "send text failed" << std::endl;
    	    return FALSE;
    	}
    	return TRUE;
    }
    

     

    1.4 遇到的一些问题

    1、对于static数据成员,只能在类中声明,在源文件中定义

    2、对于static函数成员,不能访问non-static成员,因为没有this指针

     

     

    2 附录

    2.1 线程

    1、线程创建

    (1)CreateThread

    WINBASEAPI HANDLE WINAPI 
    CreateThread (
                 LPSECURITY_ATTRIBUTES lpThreadAttributes,   //安全属性
                 SIZE_T dwStackSize,   //线程堆栈大小
                 LPTHREAD_START_ROUTINE lpStartAddress,  //线程函数地址
                 LPVOID lpParameter,   //线程函数参数
                 DWORD dwCreationFlags,   //创建标志
                 LPDWORD lpThreadId);  //返回线程ID
    

    (2)ThreadProc——线程函数原型要求

    DWORD WINAPI ThreadPro(
                          LPVOID lpParameter);

    线程函数必须符合这个原型。WINAPI,在声明函数时添加该宏即可。

    PS:

    WINAPI——是一种宏,声明了一种调用方式。 调用约定 定义函数从调用方接收参数的方式(msdn解释)调用约定有 _ _ pascal/ _ pascal/pascal、 _ _ cdecl/ _ cdecl/cdecl 或 _ _ stdcall/ _ stdcall/stdcall 。

    当不加WINAPI是报错: [Error] invalid conversion from 'DWORD (*)(void*) {aka long unsigned int (*)(void*)}' to 'LPTHREAD_START_ROUTINE {aka long unsigned int (__attribute__((__stdcall__)) *)(void*)}' [-fpermissive]

    从报错可以看出,在编译器时添加了函数签名:stdcall,这种

    2、线程同步对象

    (1)、互斥对象(Mutex)

    (2)、事件对象(Event)

    (3)、信号量(Semaphore)

    (4)、临界区(critical section)、

    (5)、可等待计时器(Waitable Timer)

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • 局域网聊天软件

    2018-07-19 23:40:43
    基于MFC的socket编程设计的局域网聊天软件,仅有C语言的基础,本人初次尝试Visual C++6.0平台上使用C++进行Windows程序设计的网络编程,对C++、MFC只是初步认识,在此基础上查找各种网络资源和文本资源初次尝试,...
  • qt做的局域网聊天工具

    热门讨论 2013-04-10 10:49:58
    用qt做的一个局域网聊天工具,类似飞鸽,在windows和linux下都可以编译运行,可以互相传文件,聊天,显示在线用户列表,显示在线用户的IP、主机名、用户名。
  • 我们创建一个Windows窗体应用 来实现客户端 首先是界面 包括一个richTextBox作为聊天的对话框 一个TextBox作为聊天内容的输入框 一个TextBox用来输入服务器IP,默认为本机ip 一个TextBox用来输入用户名称,对话框中...

    编写完服务器端后(https://blog.csdn.net/qq_16555407/article/details/100132741)
    就可以开始写客户端的代码了

    我们创建一个Windows窗体应用 来实现客户端
    首先是界面
    在这里插入图片描述
    包括一个richTextBox作为聊天的对话框
    一个TextBox作为聊天内容的输入框
    一个TextBox用来输入服务器IP,默认为本机ip
    一个TextBox用来输入用户名称,对话框中的每条信息前都会显示发送该信息的用户的名字
    一个Connect按钮连接服务器
    一个Disconnet按钮中断连接
    一个Exit按钮退出程序
    一个Send按钮发送消息
    最终要的是一个timer控件,用来实现类似Unity中Update函数的功能。

    using System.Text;
    using System.Windows.Forms;
    using System.Net;
    using System.Net.Sockets;
    using System.Threading;

    我们首先需要几个函数,方便按钮点击事件来调用
    1.Connect()

    private bool Connect()
            {
                string ip = textBoxIP.Text;
                if(ip == "")
                {
                    MessageBox.Show("Please enter the server IP!");
                    return false;
                }
                else
                {
                    try
                    {
                        tcpClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                        tcpClient.Connect(new IPEndPoint(IPAddress.Parse(ip), 8099));
                        myName = textBoxName.Text;
                    }
                    catch (Exception e)
                    {
                        MessageBox.Show("Connection failed!" + e.ToString());
                        return false;
                    }
                    isConnected = true;
                    return true;
                }
            }
    

    2.Disconnet()

    private bool Disconnect()
            {
                if (tcpClient== null)
                {
                    return true;
                }
                if (tcpClient.Connected)
                {
                    string ms = textBoxName.Text + "is Disconnected";
                    byte[] d = Encoding.UTF8.GetBytes(ms);
                    tcpClient.Send(d);
    
                    //让线程去关闭连接
                    isConnected = false;
                    receiveMsgThread.Join();
                    //tcpClient.Close();
                    return true;
                }
    
                return false;
            }
    

    3.SendMsg()

    private bool SendMsg(string m)
            {
                byte[] d = Encoding.UTF8.GetBytes(m);
                try
                {
                    tcpClient.Send(d);
                }
                catch (Exception e)
                {
                    MessageBox.Show("Failed to send: " + e.ToString());
                    return false;
                }
                return true;
            }
    

    4.ReceiveMsg()
    为什么还需要isConnected这个额外的判断标志?isConnected出现在这里是在断开连接的时候帮助子线程跳出循环并结束,并以此来释放tcpClient对象,关闭连接。

    private void ReceiveMsg()
            {
                //while (!tcpClient.Poll(10, SelectMode.SelectRead))
                while (tcpClient.Connected&& isConnected)//只要没有断开连接,就不停地接收消息
                {
                    try
                    {
                        int len = tcpClient.Receive(data);
                        lock (MsgContainer)
                        {
                            MsgContainer = Encoding.UTF8.GetString(data, 0, len);
                        }
                    }catch(Exception e)
                    {
                        MessageBox.Show("Disconnected: " + e.ToString());
                    }
                }
                MsgContainer = "Disconnected";
                isConnected = false;
                tcpClient.Close();
            }
    

    然后是各个控件的事件响应函数
    1.Timer
    这个函数每个10ms(在控件属性中设置)就会调用一次,用来更新对话框中的内容。为什么需要这个函数?因为在子线程中无法访问主线程里创建的控件richTextBox。

    private void timer1_Tick(object sender, EventArgs e)
            {
                //使用timer控件,生成每隔一个固定时间t执行的函数,
                //使用函数更新对话框中的内容,这是因为在子线程中不能修改主线程创建的控件richTextBox
                lock (MsgContainer)
                {
                    if (MsgContainer != null && MsgContainer != "")
                    {
                        richTextBox1.AppendText(MsgContainer + "\r");
                        MsgContainer = "";
                        richTextBox1.ScrollToCaret();
                    }
                }
                if (!isConnected)
                {
                    buttonConnect.Enabled = true;
                    textBoxName.Enabled = true;
                }
            }
    

    2.发送按钮

    private void buttonSend_Click(object sender, EventArgs e)
            {
                if(isConnected)
                {
                    if (SendMsg(myName + ": " + textBox1.Text))
                        textBox1.Text = "";
                }
                else
                {
                    MessageBox.Show("Please connect to the server.");
                }
            }
    

    3.连接按钮

    private void buttonConnect_Click(object sender, EventArgs e)
            {
                if(Connect())
                {
                    //开启接收消息的线程,在后台不断接收消息
                    receiveMsgThread = new Thread(ReceiveMsg);
                    receiveMsgThread.IsBackground = true;
                    receiveMsgThread.Start();
                    //
                    buttonConnect.Enabled = false;
                    textBoxName.Enabled = false;
                    isConnected = true;
                    SendMsg(myName+ " is Connected");
                }
            }
    

    4.断开连接按钮

    private void buttonDisCon_Click(object sender, EventArgs e)
            {
                if (Disconnect())
                {
                    buttonConnect.Enabled = true;
                    textBoxName.Enabled = true;
                }
            }
    

    5.退出按钮

    private void buttonExit_Click(object sender, EventArgs e)
            {
                Disconnect();
                Application.Exit();
            }
    

    总的来说,客户端的编写没有什么特别的,需要注意的是Timer的使用,如果使用Unity3D创建客户端则直接使用Update()函数即可。再就是退出连接或者断开连接的时候,我们需要一个bool类型的公共变量isConnect让子线程退出循环并结束该线程,否则会出现一些问题。

    展开全文
  • 局域网内的聊天软件

    2016-03-14 14:30:45
    自己和同学一起写的一个软件,UDP 广播发现同一个局域网的安装同一软件的主机(绑定固定的本机ip 或者禁用其他的网卡自保留一个否则接受不到),tcp通信,TCP通讯还待完善,可以不用报持每个人的通讯socket,当要发送...
  • 聊天室用C语言实现,一个服务器,多个客户的,局域网socket通信。有简单的界面,可以显示在线用户,上传下载文件等功能。
  • windows系统、多台主机在同一局域网环境下。 2、目录下共有4个exe文件及一张jpg.只需要保证将它们放在同一目录下即可。 3、用户使用时只需要点击mchat.exe即可。 4、当本系统第一次运行时,会自动生成data文件夹...
  • 基于单播的局域网聊天

    千次阅读 2019-07-31 10:14:16
    这是本人的计算机网络课设,感谢向老师。本程序用的是C和Window的API,也...所以用的模板是B站中的一部MFC局域网聊天室视频教程,地址:https://www.bilibili.com/video/av38700336?from=search&seid=60038915...

           这是本人的计算机网络课设,感谢向老师。本程序用的是C和Window的API,也算是window sdk编程,因为学习不太认真导致自己写的时候出了很多问题,所以就用的网上的资源进行学习。所以用的模板是B站中的一部MFC局域网聊天室视频教程,地址:https://www.bilibili.com/video/av38700336?from=search&seid=6003891501149823218  。

    这篇拖了很久,想想还是不用写了,视频上讲的很清楚了,展示一下效果吧:

     

     

    展开全文
  • 控制台迷你局域网聊天室 V1.0 1.基于控制台的局域网聊天室程序,用C++编写,VC++6.0 VC++2010编译通过 2.程序中调用了windows API函数,利用了WinSock以及多线程等编程技巧 3.程序采用TCP-IP协议的C/S模式,并且服务端...
  • 这个程序是在window下用c编写的socket基于多线程的程序,程序能够完美运行,并且程序注解也是很清楚的哦!Liunx版本的c编写的socket多线程的聊天室也是有的,程序中有可直接执行的exe文件方便大家测试。
  • using System.Windows.Forms; using System.Net; using System.Net.Sockets; using System.Threading; namespace ChatBox { public partial class Server : Form { Thread thread = null; string server_ip = ...
  • 该楼层疑似违规已被系统折叠隐藏此楼查看此楼//server#include#include#include#include#include#definePORT100#definesocklen_tint#pragmacomment(lib,"wsock32.lib")SOCKETs;SOCKETclient;sockaddr_infrom;...
  • nc实现局域网聊天

    2021-04-14 23:50:51
    windows本地安装nc,访问 ...在cmd里面查看电脑在这个局域网内的IP地址,输入命令: ipconfig/all 复制IPv4地址这一项后面的地址。 在cmd里面输入: nc -l 9999 监听本地9999端口。 如果出现local liste
  • 基于windows环境下使用UDP/IP协议多线程聊天程序, 目前条件有限,没有独立的IP,所以只在局域网中测试(结果:成功), 如果有独立的IP,在广域网理论上也能成功。 windows端登陆空间程序:需要在网页端正常运转的...
  • 这是一个基于控制台的简单LAN聊天程序,所有用户都必须在一个公共网络文件夹(请确保该文件夹为空)中进行访问。 它是一个基本程序,可用于在LAN连接的计算机系统之间进行简单的聊天,但具有使您也可以共享链接和...
  • Java实现---局域网聊天

    千次阅读 2018-09-11 17:50:31
    聊天室实现是通过C/S架构实现,既要有服务器端,也要有客户端。 实现原理: (1)服务器实例化一个 ServerSocket 对象,表示通过服务器上的端口通信。 (2)服务器调用 ServerSocket 类的 accept() 方法,该方法将一直...
  • 以前在学校自学java的时候写的一个java swing的局域网聊天软件。 代码结构需要优化。还有很多BUG,而且软件也没有完善,不过聊天功能是实现了(可以发送图片、文字信息)。可以跨平台使用。测试环境在windows xp、...
  • Linux下局域网通讯软件 传输层协议:UDP 功能:注册 登陆 私聊 群聊 在线列表 下线 重复登陆提示 运行环境:Linux ubunt10 04下测试
  • JAVA基于局域网聊天室系统(源代码+论文) 摘 要 视频聊天系统作为一种新型的通信和交流工具,突破了地域的限制,可以提供更为便捷、灵活、全面的音、视频信息的传递和服务,具有极其广泛的发展前景。 本文介绍了...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 14,903
精华内容 5,961
热门标签
关键字:

windows局域网聊天