网络编程大作业-聊天室

前言:

大二时候完成的网络编程大作业,用C\C++语言写的一款简易聊天室。目前一个服务器只能连两个客户端,两个以上的我搞不出来。也可以在三台电脑操作,只要输入服务器的IP地址就行了。
使用的编译器:VS2010、VS2019

演示一下:

左边的是服务器,中间的是客户端1:zhang san,右边是客户端2:li si。
在这里插入图片描述

服务器源码:

#include "WinSock2.h"    // winsock2.h是套接字接口。
#include "process.h"     //process.h 是包含用于和宏指令的作用声明与螺纹和过程一起使用的C标头文件。
#include "stdio.h"       //被包含的文件通常是由系统提供的,其扩展名为.h,而stdio为standard input output的缩写,意为“标准输入输出”
#include "stdlib.h"      //stdlib 头文件里包含了C语言的一些函数,该文件包含了的C语言标准库函数的定义
#include "conio.h"      //将conio.h包含入你的程序,使你可以引用其中声明的函数。conio.h不是C标准库中的头文件。
#pragma comment(lib,"ws2_32.lib")   // ws2_32.lib是套接字实现。
#define SEND_OVER 1                //已经转发消息
#define SEND_YET 0                 //还没转发消息
int Status = SEND_YET;             //状态设为未发送sockaddr_in ClientAddr = { 0 };      //客户端地址
HANDLE g_hRecv1 = NULL;   //handle为句柄,用表示对象   void *g_hRecv1=NULL;
HANDLE g_hRecv2 = NULL;//客户端信息结构体struct Client_inf
{SOCKET sClient;      //客户端套接字char buf[512];       //数据缓冲区char userName[20];   //客户端用户名char IP[20];         //客户端IPUINT_PTR flag;       //标记客户端,用来区分不同的客户端,typedef unsigned int_w64 UINT_PTR,是为解决32位与64位编译器的兼容性而设置的关键字
};typedef Client_inf Client;Client ClientSock[2] = { 0 };         //创建一个客户端结构体
SOCKET ServerSocket=INVALID_SOCKET;unsigned __stdcall ThreadSend(void* param);    //声明发送数据的线程函数
unsigned __stdcall ThreadRecv(void* param);     //声明接收数据的线程函数
unsigned __stdcall ThreadAccept(void* param);   //声明接受请求的线程函数int main(void)
{int ret,len;WSADATA data ;struct sockaddr_in ServerAddr;       //服务端地址unsigned short SERVERPORT = 6666;     //服务器监听端口//初始化套接字ret = WSAStartup(MAKEWORD(2,2),&data);//WSAStartup函数的返回值是0表示成功if (SOCKET_ERROR == ret){printf("WSAstartup error!\n");return -1;}//创建套接字ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (INVALID_SOCKET == ServerSocket){printf("创建socket失败!\n");return -2;}//设置服务器地址ServerAddr.sin_family = AF_INET;//连接方式ServerAddr.sin_port = htons(SERVERPORT);//服务器监听端口ServerAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);//任何客户端都能连接这个服务器//绑定服务器ret = bind(ServerSocket, (struct sockaddr*)&ServerAddr, sizeof(sockaddr));//bind是一组用于函数绑定的模板。if (SOCKET_ERROR == ret){printf("bind error!\n");closesocket(ServerSocket);WSACleanup();return -3;}//设置监听客户端连接数ret =listen(ServerSocket,2);//listen是创建一个套接口并监听申请的连接.if (SOCKET_ERROR == ret){printf("listen error!\n");closesocket(ServerSocket);WSACleanup();return -4;}printf("开启聊天成功,等待用户连接....\n");_beginthreadex(NULL, 0, ThreadAccept, NULL, 0, 0);    //启动接受连接线程int k=0;while(k<100)   //让主线程休眠,不让它关闭TCP连接.{Sleep(10000000);k++;}//关闭套接字for (int j = 0;j < 2;j++){if (ClientSock[j].sClient != INVALID_SOCKET)closesocket(ClientSock[j].sClient);}closesocket(ServerSocket);WSACleanup();return 0;
}//发送数据线程函数的定义
unsigned __stdcall ThreadSend(void* param)    //param形参
{int ret = 0;int flag = *(int*)param;        //int*是把param从void*强制转化为int*,要不然取值的时候系统会不知所措,外面的*就是取值操作,取param这个地址里面保存的整型值SOCKET client = INVALID_SOCKET;         //创建一个临时套接字来存放要转发的客户端套接字char temp[512] = { 0 };             //创建一个临时的数据缓冲区,用来存放接收到的数据memcpy(temp,  ClientSock[!flag].buf, sizeof(temp));   //拷贝sprintf( ClientSock[flag].buf, "%s: %s", ClientSock[!flag].userName, temp);//添加一个用户名头if (strlen(temp) != 0 && Status == SEND_YET&&temp!="\n"&&(*temp!=' ')) //如果数据不为空且还没转发则转发{ret = send( ClientSock[flag].sClient, ClientSock[flag].buf, sizeof( ClientSock[flag].buf), 0);}//send()是一个计算机函数,功能是向一个已经连接的socket发送数据,如果无错误,返回值为所发送数据的总数,否则返回SOCKET_ERROR。if (SOCKET_ERROR == ret){ return 1;}Status = SEND_OVER;  //转发成功后设置状态为已转发return 0;
}//接受数据线程函数的定义
unsigned __stdcall ThreadRecv(void* param)
{SOCKET client = INVALID_SOCKET;    //客户端套接字int flag = 0; if (*(int*)param ==  ClientSock[0].flag)      //判断是哪个客户端发来的消息{client =  ClientSock[0].sClient;flag = 0;}  else if (*(int*)param ==  ClientSock[1].flag){client =  ClientSock[1].sClient;flag = 1;}char temp[512] = { 0 }; //临时数据缓冲区while (1){memset(temp, 0, sizeof(temp));int ret = recv(client, temp, sizeof(temp), 0); //接收数据if (SOCKET_ERROR == ret)continue;Status = SEND_YET;                //设置转发状态为未转发if(client==ClientSock[0].sClient )    {                                    //设置防止出现自己给自己发消息的BUGflag=1;}else{flag=0;}  memcpy( ClientSock[!flag].buf, temp, sizeof( ClientSock[!flag].buf));_beginthreadex(NULL, 0, ThreadSend, &flag, 0, NULL); //开启一个转发线程,flag标记着要转发给哪个客户端}return 0;
}//接受请求线程函数的定义
unsigned __stdcall ThreadAccept(void* param)
{int i = 0;int temp1 = 0, temp2 = 0;{while (i < 2)      //表明只允许两个客户端连接{if (ClientSock[i].flag != 0){++i;continue;}//如果有客户端申请连接就接受连接int len= sizeof(ClientAddr);int ret = ClientSock[i].sClient = accept(ServerSocket, (SOCKADDR*)&ClientAddr, &len);//accept()是在一个套接口接受的一个连接。if (INVALID_SOCKET == ret){printf("accept error!\n");closesocket(ServerSocket);WSACleanup();return -1;}recv(ClientSock[i].sClient, ClientSock[i].userName, sizeof(ClientSock[i].userName), 0); //接收用户名printf(" 成功连上聊天用户!IP:%s ,Port: %d,UerName: %s\n",inet_ntoa(ClientAddr.sin_addr), htons(ClientAddr.sin_port), ClientSock[i].userName);memcpy(ClientSock[i].IP, inet_ntoa(ClientAddr.sin_addr), sizeof(ClientSock[i].IP)); //记录客户端IPClientSock[i].flag = ClientSock[i].sClient; //不同的socke有不同UINT_PTR类型的数字来标识i++;}i = 0;if (ClientSock[0].flag != 0 && ClientSock[1].flag != 0 )         //当两个用户都连接上服务器后才进行消息转发{if (ClientSock[0].flag != temp1)   //每次断开一个连接后再次连上会新开一个线程,导致cpu使用率上升,所以要关掉旧的,,int temp1 = 0, temp2 = 0;{if (g_hRecv1)         //这里关闭了线程句柄CloseHandle(g_hRecv1);g_hRecv1 = (HANDLE)_beginthreadex(NULL, 0, ThreadRecv, &ClientSock[0].flag, 0, NULL); //开启2个接收消息的线程}  if (ClientSock[1].flag != temp2){if (g_hRecv2)CloseHandle(g_hRecv2);g_hRecv2 = (HANDLE)_beginthreadex(NULL, 0, ThreadRecv, &ClientSock[1].flag, 0, NULL);}    }temp1 = ClientSock[0].flag; //防止ThreadRecv线程多次开启temp2 = ClientSock[1].flag;Sleep(2000);}return 0;}

客户端源码:

#include "WinSock2.h"    // winsock2.h是套接字接口。
#include "process.h"    //process.h 是包含用于和宏指令的作用声明与螺纹和过程一起使用的C标头文件。
#include "stdio.h"     //被包含的文件通常是由系统提供的,其扩展名为.h,而stdio为standard input output的缩写,意为“标准输入输出”
#include "stdlib.h"    //stdlib 头文件里包含了C语言的一些函数,该文件包含了的C语言标准库函数的定义
#include "conio.h"     //将conio.h包含入你的程序,使你可以引用其中声明的函数。conio.h不是C标准库中的头文件。
#pragma comment(lib,"ws2_32.lib")     // ws2_32.lib是套接字实现。
#define RECV_OVER 1
#define RECV_YET 0
char userName[20] = { 0 };
int Status = RECV_YET;
unsigned __stdcall ThreadRecv(void* param);  //接受数据的线程
unsigned __stdcall ThreadSend(void* param);  //发送数据的线程
int main(void)
{WSADATA wsaData = { 0 };      SOCKET ClientSocket = INVALID_SOCKET;   //客户端套接字sockaddr_in ServerAddr = { 0 };     //服务端地址,SOCKADDR_IN为结构体,可以小写unsigned short SERVERPORT = 6666; 
//初始化套接字WSADATA data;int ret=WSAStartup(MAKEWORD(2,2),&data);   //WSAStartup函数的返回值是0表示成功 if (SOCKET_ERROR==ret)        {printf("WSAStartup 启动错误!\n");               return -1;}//创建套接字ClientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if ( INVALID_SOCKET == ClientSocket){printf("socket创建失败!\n");WSACleanup();return -1;}//输入服务器IPprintf("请输入服务器的IP:");char IP[32] = { 0 };gets_s(IP,31);//设置服务器地址ServerAddr.sin_family = AF_INET;  ServerAddr.sin_port = htons(SERVERPORT);           //服务器端口ServerAddr.sin_addr.S_un.S_addr = inet_addr(IP);   //服务器地址printf("正在努力连接服务器.....\n");//连接服务器ret=connect(ClientSocket,(sockaddr*)&ServerAddr,sizeof(ServerAddr));if ( SOCKET_ERROR ==ret)   {printf("connect连接服务器失败!\n");closesocket(ClientSocket);WSACleanup();return -1;}printf("成功连上服务器 IP:%s Port:%d\n",IP,htons(ServerAddr.sin_port));printf("欢迎登录66微聊聊天室!\n");printf("请输入你的名字: ");gets_s(userName,20);send(ClientSocket, userName, sizeof(userName), 0);printf("\n\n");_beginthreadex(NULL, 0, ThreadRecv, &ClientSocket, 0, NULL); //启动接收和发送消息线程_beginthreadex(NULL, 0, ThreadSend, &ClientSocket, 0, NULL);for (int k = 0;k < 1000;k++)         // 让主线程休眠,不让它关闭TCP连接{Sleep(10000000);}closesocket(ClientSocket);WSACleanup();return 0;
}unsigned __stdcall ThreadRecv(void* param)
{char buf[512] = { 0 };while (1){int ret = recv(*(SOCKET*)param, buf, sizeof(buf), 0);   //先强行转化为SOCKET*,不然void*直接引用会出错if (SOCKET_ERROR == ret){Sleep(500);          //将进程挂起一段时间,即停下来0.5s再继续continue;}if (strlen(buf) != 0)    //判断缓冲区是不是有数据,有就打印出来!{printf("%s\n", buf);Status = RECV_OVER;     //把状态设为已接收}elseSleep(100);  }return 0;
}unsigned __stdcall ThreadSend(void* param)
{char buf[512] = { 0 };int ret = 0;while (1){int c = getch();                       //在头文件conio.h有定义,此函数是一个不回显函数,当用户按下某个字符时,此函数自动读取,无需按回车if(c == 72 || c == 0 || c == 68)       //遇到这几个值getch就会自动跳过continue;        printf("%s: ", userName);gets_s(buf,511);                       //此函数在stdio.h中定义,第二参数就是允许输入长度,留一位补零,否则溢出ret = send(*(SOCKET*)param, buf, sizeof(buf), 0);if (ret == SOCKET_ERROR)if (ret == SOCKET_ERROR){return 1;}if(strcmp(buf,"bye") == 0||strcmp(buf,"再见")==0) {printf("您已退出聊天室!\n");break;}}return 0;
}

最后:
需要代码的可以自行下载。下载链接
下载操作:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/55855.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

第7课 阿布拉卡达布拉《小学生C++趣味编程》

#include<iostream> using namespace std; int main() {int a,b,t; a10;b20; cout<<"a"<<a<<" b"<<b<<endl; //输出交换前a,b的值 ta;ab;bt;cout<<"a"<<a<<" b"<<b&l…

新学期Flag已立,期待期末给自己一份满意的答卷

新的学期开始了&#xff0c;在此借助CSDN的此次征文活动&#xff0c;为自己制定新学期计划&#xff0c;立下小小的flag&#xff0c;希望在新的学期里和大家一起加油&#xff01; 一、自我介绍 研一新生一枚&#xff0c;目前正在学习神经网络以及Python&#xff0c;同时对前端…

小学生C++趣味编程 2023 01 05 测试

2023 01 05 测试题 初赛 一、单项选择题&#xff08;共20 题&#xff0c;每题 1.5 分&#xff0c;共计 30 分&#xff1b;每题有且仅有一个正确选项&#xff09; 1. 一个 32 位整型变量占用&#xff08; &#xff09;个字节。 A. 4 B. 8 C. 32 D. 128 2. 二进制数 11.…

《新学期,新Flag》乘风破浪

一、自我介绍 经常摆烂的大三学长&#xff0c;计算机专业&#xff0c;准备努力往上爬&#xff0c;一步一步的超越我前面的人&#xff0c;把他们狠狠的甩到后面。 二、新学期目标 1&#xff0c;好好学习 制定学习目标/找一个学习的榜样 熟悉各种编程软件的打开和关闭&#x1f…

小学生C++趣味编程 2023 01 08 测试

单项选择题 &#xff08;共20题&#xff0c;每题1.5分&#xff0c;共计30分。每题有且仅有一个正确答案.&#xff09;。 9. 设字符串S”Olympic”&#xff0c;S的非空子串的数目是&#xff08; &#xff09;。 A. 28 B. 29 C. 16 D. 17 10&#xff0e;W…

荣耀X40,X30,X20,X10,荣耀70,荣耀60 安装谷歌套件谷歌服务框架GMS,Google Play商店

你可能认为华为和荣耀分家之后&#xff0c;荣耀手机就可以安装Play商店,安装服务框架谷歌。但实际上即使荣耀手机和华为分家之后&#xff0c;也是不可以继续使用Play商店&#xff0c;荣耀最新发售的X40(GT,i,se,max)&#xff0c;包括以前的X30&#xff0c;X20&#xff0c;X10&a…

最新2021修复安装了谷歌框架GMS后华为/荣耀手机FCM推送服务仍不能用的问题(Microsoft Authenticator微软验证器在安装了Google服务后还是提示不可用)

简介 Firebase Cloud Messaging (FCM) 是 Google Cloud Messaging (GCM) 的升级版&#xff0c;是一种便于在移动应用和服务器应用程序之间进行消息传递的云推送服务。详细了解 目前大部分华为/荣耀手机在安装了谷歌框架GMS后&#xff0c;FCM推送服务仍然处于不可用状态&#…

支持华为荣耀HarmonyOS鸿蒙2.0系统安装谷歌服务框架play商店GMS的工具介绍,也开始支持其他安卓设备安装谷歌套件

华谷套件支持华为荣耀几乎全系列HarmonyOS鸿蒙2.0系统安装谷歌服务框架play商店GMS,主要支持华为荣耀安装华谷套件,现在也开始支持其他安卓国产手机小米,vivo,oppo,魅族,联想…,支持mate30/40 pro系列,p10,p20,p40,p50,xs2,nova5 6 7 8,9,play,荣耀30/40/50/magic系列/9x/10…解…

荣耀畅玩7x(BND-TL10移动版)刷LineageOS 19 GSI

BND-TL10刷 Lineage OS 19 引言步骤1.升级当前手机官方系统到最高支持的版本2.准备LineageOS rom3.刷入LineageOS4.结束&#xff0c;Happy 分享本文相关资源 引言 Generic System Image(GSI&#xff1a;通用系统映像)&#xff0c;相关知识请百度 本文将带领你在荣耀畅玩7x手机…

华为或者荣耀手机使用谷歌有Google弹窗该怎么办?

完成安装后。首先 进入设置 —应用管理 —搜索Google 点开右上角点击显示应用程序 这里我们只需要对Google服务框架&#xff0c;商店&#xff0c;服务这三个进行设置。 在断网的情况下&#xff0c;注意一定要在断网的情况下进行下面的操作。 Google服务框架和Googlepaly商店里…

【华为鸿蒙3.0/荣耀安卓12使用VMOS Pro的激活方式 方法二】

关于华为鸿蒙3.0/荣耀安卓12如何解除进程限制&#xff0c;这里提供以下教程供小伙伴们参考。 需要准备&#xff1a;电脑一台&#xff0c;数据线一根。 第一步&#xff1a;安装华为手机助手/荣耀手机助理&#xff0c;并通过激活程序包&#xff08;vmos_activation.zip&#xff0…

华为荣耀所有系列手机,如何安装谷歌应用框架?

以前一直用的小米Note&#xff0c;安装谷歌应用框架只能够刷海外版的MiUI&#xff0c;非常的耗费时间。 本来一直还想用小米来着&#xff0c;因为不知道华为可以如此轻松的安装谷歌应用&#xff0c;当得知可以配置时&#xff0c;毫不犹豫的买了华为&#xff0c;赞&#xff01; …

Charles手机安装教程(荣耀手机亲测)

Android安装Charles证书&#xff08;荣耀手机测试&#xff09; 1、 通过Charles获取证书Charles的Help选项–>SSL proxying–>Save Charles Root Certificate出现弹框 注意保存文件的格式&#xff01; 把证书(即 crt 文件)传到手机存储&#xff08;QQ,微信等方式&…

Postgre SQL连接服务器失败

首先这是登陆postgre sql时提示的错误信息&#xff1a; psql: 无法联接到服务器: Connection refused (0x0000274D/10061) 服务器是否在主机 "localhost"(::1) 上运行并且准备接受在端口5432 上的 TCP/IP 联接?无法联接到服务器: Connection refused (0x0000…

chatgpt赋能python:Python快捷指令–提升你的编程效率

Python快捷指令 – 提升你的编程效率 在Python编程中&#xff0c;快捷指令是一个非常重要的概念。快捷指令是指通过简单的键盘操作来执行特定的功能或命令&#xff0c;从而提高编程效率。在这篇文章中&#xff0c;我们将介绍一些最常用和最有用的Python快捷指令&#xff0c;帮…

GitHub Copilot 键盘快捷( 试用 )

使用 根据注释生成代码根据上下文自动填充代码代码翻译算法生成测试用例结果输出代码优化可以使用 q: 向Copilot提问&#xff0c;就像使用ChatGPT一样&#xff0c;可以帮助我们阅读代码 这些只是gpt的一部分能力&#xff0c;通过这些应用场景熟悉gpt,然后再扩展使用场景&#…

python制作超炫流星雨代码,python制作超炫流星雨表

这篇文章主要介绍了python制作超炫流星雨表白源代码&#xff0c;具有一定借鉴价值&#xff0c;需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获&#xff0c;下面让小编带着大家一起了解一下。 1、小学题一个轮一个星叫什么 一个轮一个星叫做流星雨。流星雨又称为流星…

我的作文题目是——《我的区长父亲》

1、你有哪些印象深刻的高考记忆&#xff1f; 只记得当时早交了卷&#xff0c;想早点回家&#xff0c;结果只能出考场&#xff0c;不能出楼&#xff0c;就在楼道里溜达&#xff0c;迎面遇见了班主任&#xff0c;“哟&#xff0c;你咋还出来了”&#xff0c;我说&#xff1a;我想…

计算机一级证书英文 简历,通用于计算机英文简历范文

通用于计算机英文简历范文 2019-08-23 版权声明 举报文章 Sep.xxxx—Jul.xxxx Computer Science Dept. of Computer College, Northeast Normal University. Courses taken include: data structure, principle of database, principle of compiling, principle of computers, …

OpenAI GPT-4发布总结

OpenAI官方发布了GPT-4模型&#xff0c;GPT-4是一个大型多模态模型&#xff0c;支持输入文本图片&#xff0c;输出还是文字形式&#xff0c;GPT-4比任何 GPT-3.5 模型都更强大&#xff0c;能够执行更复杂的任务&#xff0c;并针对聊天进行了优化。 GPT-4目前处于测试阶段&…