基于 服务器部署在linux 客户端在windows 的多人聊天室

上面是三人聊天室的终端实现,也可以加界面做成客户端形式,用云服务器可以实现qq的随发随用功能,采用的是C/S架构。目前BUG:超过两人必须同时开启,不然后面的人终端显示不停输出换行;关闭一人后其他人出现终端显示不停输出换行。代码展示:

客户端:

/*要在win上使用socket需要进行相应的配置*/

#include<bits/stdc++.h>

#include<WinSock2.h>
#include<WS2tcpip.h>
#include<Windows.h>


#pragma comment(lib,"Ws2_32.lib")
using namespace std;

#define BUF_SIZE 1024

char buf[BUF_SIZE];

unsigned sendmsg(void* arg) {
    SOCKET sock = *((SOCKET*)arg);
    while (1) {
        scanf("%s", buf);
        if (!strcmp(buf, "Q\n") || !strcmp(buf, "q\n"))
        {
            closesocket(sock);
            exit(1);
        }
        send(sock, buf, strlen(buf), 0);
    }
    return 0;
}


unsigned recvmsg(void* arg) {
    SOCKET sock = *((SOCKET*)arg);
    char msg[BUF_SIZE];
    while (1) {
        int len = recv(sock, msg, sizeof msg - 1, 0);
        if (len == -1) //服务器挂了
            return -1;
        msg[len] = '\0';
        printf("%s\n", msg);
    }
    return 0;
}

    int  main() {

        WORD wVersionRequested;
        WSADATA wsaData;
        int err;

        wVersionRequested = MAKEWORD(2, 2);

        err = WSAStartup(wVersionRequested, &wsaData);
        if (err != 0) {
            return -1;
        }

        if (LOBYTE(wsaData.wVersion) != 2 ||
            HIBYTE(wsaData.wVersion) != 2) {
            WSACleanup();
            return -1;
        }

    //创建socket
    SOCKET hsock;
    hsock = socket(AF_INET, SOCK_STREAM, 0);

       //bind
    SOCKADDR_IN s_r;
    //memset(&s_r, 0, sizeof s_r);
    s_r.sin_family = AF_INET;
    s_r.sin_port =htons(6665);
    inet_pton(AF_INET, "192.168.17.132", &s_r.sin_addr);

    //connect
    connect(hsock, (sockaddr*)&s_r, sizeof s_r);

    cout << "多人聊天室 启动!! --注:服务器部署在linux上 请输入你的名字"<<endl;

    //开两个线程进行发消息和收消息 
    
    //循环发消息
    HANDLE s_hand=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)sendmsg, (void*)&hsock,0,NULL);

    //循环收消息
    HANDLE r_hand=CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)recvmsg, (void*)&hsock, 0, NULL);
    
    //等待两个线程结束
    WaitForSingleObject(s_hand, INFINITE); //无限进行检测 
    WaitForSingleObject(r_hand, INFINITE); //当前线程将等待 s_hand 对象变为可用,而且这个等待时间是无限长的,直到对象可用为止。
    
    closesocket(hsock);

    WSACleanup(); //关掉环境
}

服务器端:

#include<iostream>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string>
#include<sys/epoll.h>
#include<netinet/in.h>
#include<map>
#include<arpa/inet.h>
#include"./ser/s.h"


#define PORT 1956
#define MAX_CLIENTS 1024

using namespace std;

typedef struct Client {
	int sd;
	std::string name; 
}c;


std::map<int, c> cs;

int main(int argc,char ** argv) {
	int l_sd=socket(AF_INET,SOCK_STREAM,0);

		struct sockaddr_in laddr;
				laddr.sin_family=AF_INET;
				laddr.sin_port=htons(atoi(argv[1]));
				laddr.sin_addr.s_addr=INADDR_ANY;
				bind(l_sd,(struct sockaddr*)&laddr,(socklen_t)sizeof laddr);
				listen(l_sd,5);
	
						cout<<"Runing..."<<endl;
						struct epoll_event ev,evs[MAX_CLIENTS];//进行epoll_event的变量的初始化和其数组的初始化,数组只是用来隐含地返回发生的时间的描述符 只有在epoll_wait时才会用到数组 epoll_wait(int epfd,struct epoll_event* evs,int max_evs,int time_out)
						ev.data.fd=l_sd;//进行epoll_event变量的赋值 监听的对象,监听对象的监听的事件 l_sd  EPOLLIN 可读事件
						ev.events=EPOLLIN;
						struct epoll_event mid_ev;//临时变量epoll_evennt其实也可以不需要 因为用ev就行了 目的就是添加到epoll描述符(epoll_create 返回值)中

						int e_fd=epoll_create(2);//epoll描述符

						epoll_ctl(e_fd,EPOLL_CTL_ADD,l_sd,&ev); //增加监听的对象

						while(true){
								cout<<"Ready for waiting!"<<endl;
								int num=epoll_wait(e_fd,evs,MAX_CLIENTS,-1);//阻塞等待
								cout<<"wait is over."<<endl;

								for(int i=0;i<num;++i){
										int mid_fd=evs[i].data.fd;
										if(evs[i].data.fd==l_sd){//是监听的对象改变了  说明有新的连接
												struct sockaddr_in raddr;
												socklen_t len;
												int new_sd=accept(l_sd,(struct sockaddr*)&raddr,&len);
												char str[1024];
												inet_ntop(AF_INET,&raddr.sin_addr,str,sizeof str);
												cout<<"connected by"<<str<<" port:"<<ntohs(raddr.sin_port)<<endl;
												mid_ev.data.fd=new_sd;//将连接的描述符进行监视对象的初始化
												mid_ev.events=EPOLLIN;
												epoll_ctl(e_fd,EPOLL_CTL_ADD,new_sd,&mid_ev);//将连接的描述符进行添加到监视队列中
												c c_d;
												c_d.sd = new_sd;
												c_d.name = " ";
												cs[c_d.sd] =c_d;
										}
										else{
										cout << "Having msg..." << endl;
												char buf[1024];
												int n = read(mid_fd, buf, 1024);
												if (n == 0) {
													close(mid_fd);
													epoll_ctl(e_fd, EPOLL_CTL_DEL, mid_fd,0);
													cs.erase(mid_fd);
												}
										else
										{
												std::string msg(buf, n);
												if (cs[mid_fd].name == " ") {
														cs[mid_fd].name = msg;
												}
												else  { //speak message
														std::string name = cs[mid_fd].name;
														//send the msg to others all
														for (auto& x : cs) {
																if (x.first != mid_fd) {
																		write(x.first, ('[' + name + ']' + ": " + msg).c_str(),msg.size() + sizeof name + 4);
																}
														}
												}
										}
								}
						}
				}
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值