socket多人聊天程序C语言版.doc_第1页
socket多人聊天程序C语言版.doc_第2页
socket多人聊天程序C语言版.doc_第3页
socket多人聊天程序C语言版.doc_第4页
socket多人聊天程序C语言版.doc_第5页
已阅读5页,还剩10页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

socket多人聊天程序C语言版1V1实现了,1V多也就容易了。不过相对于1V1的程序,我经过大改,采用链表来动态管理。这样效率真的提升不少,至少CPU使用率稳稳的在20以下,不会飙到100了。用C语言写这个还是挺费时间的,因为什么功能函数都要自己写,不像C+有STL库可以用,MFC写就更简单了,接下来我还会更新MFC版本的多人聊天程序。好了,废话少说,进入主题。这个程序要解决的问题如下:1.CPU使用率飙升问题 用链表动态管理2.用户自定义聊天,就是想跟谁聊跟谁聊 _Client结构体中新增一个ChatName字段,用来表示要和谁聊天,这个字段很重要,因为server转发消息的时候就是按照这个字段来转发的。3.中途换人聊天,就是聊着聊着,想和别人聊,而且自己还一样能接收到其它人发的消息 这个就要小改客户端的代码了,可以在发送聊天消息之前插入一段代码,用来切换聊天用户。具体做法就是,用getch()函数读取ESC键,如果用户按了这个键,则表示想切换用户,然后会输出一行提示,请输入chat name,就是想要和谁聊天的名字,发送这个名字过去之前要加一个标识符,表示这个消息是切换聊天用户消息。然后server接收到这个消息后会判断第一个字符是不是标识符,第二个字符不能是标识符,则根据这个name来查找当前在线的用户,然后修改想切换聊天用户的ChatName为name这个用户。(可能有点绕,不懂的看代码就清晰易懂了)4.下线后提醒对方 还是老套路,只要send对方不通就当对方下线了。编写环境:WIN10,VS2015效果图:为了方便就不用虚拟机演示了,但是在虚拟机是肯定可以的,应该说只要是局域网,能互相ping通就可以使用这个程序。Server code:链表头文件:#ifndef _CLIENT_LINK_LIST_H_#define _CLIENT_LINK_LIST_H_ #include #include /客户端信息结构体typedef struct _Client SOCKET sClient; /客户端套接字 char buf128; /数据缓冲区 char userName16; /客户端用户名 char IP20; /客户端IP unsigned short Port; /客户端端口 UINT_PTR flag; /标记客户端,用来区分不同的客户端 char ChatName16; /指定要和哪个客户端聊天 _Client* next; /指向下一个结点Client, *pClient; /* function 初始化链表* return 无返回值*/void Init(); /* function 获取头节点* return 返回头节点*/pClient GetHeadNode(); /* function 添加一个客户端* param client表示一个客户端对象* return 无返回值*/void AddClient(pClient client); /* function 删除一个客户端* param flag标识一个客户端对象* return 返回true表示删除成功,false表示失败*/bool RemoveClient(UINT_PTR flag); /* function 根据name查找指定客户端* param name是指定客户端的用户名* return 返回一个client表示查找成功,返回INVALID_SOCKET表示无此用户*/SOCKET FindClient(char* name); /* function 根据SOCKET查找指定客户端* param client是指定客户端的套接字* return 返回一个pClient表示查找成功,返回NULL表示无此用户*/pClient FindClient(SOCKET client); /* function 计算客户端连接数* param client表示一个客户端对象* return 返回连接数*/int CountCon(); /* function 清空链表* return 无返回值*/void ClearClient(); /* function 检查连接状态并关闭一个连接* return 返回值*/void CheckConnection(); /* function 指定发送给哪个客户端* param FromName,发信人* param ToName, 收信人* param data, 发送的消息*/void SendData(char* FromName, char* ToName, char* data); #endif /_CLIENT_LINK_LIST_H_链表cpp文件:?#include ClientLinkList.h pClient head = (pClient)malloc(sizeof(_Client); /创建一个头结点 /* function 初始化链表* return 无返回值*/void Init() head-next = NULL; /* function 获取头节点* return 返回头节点*/pClient GetHeadNode() return head; /* function 添加一个客户端* param client表示一个客户端对象* return 无返回值*/void AddClient(pClient client) client-next = head-next; /比如:head-1-2,然后添加一个3进来后是 head-next = client; /3-1-2,head-3-1-2 /* function 删除一个客户端* param flag标识一个客户端对象* return 返回true表示删除成功,false表示失败*/bool RemoveClient(UINT_PTR flag) /从头遍历,一个个比较 pClient pCur = head-next;/pCur指向第一个结点 pClient pPre = head; /pPre指向head while (pCur) / head-1-2-3-4,要删除2,则直接让1-3 if (pCur-flag = flag) pPre-next = pCur-next; closesocket(pCur-sClient); /关闭套接字 free(pCur); /释放该结点 return true; pPre = pCur; pCur = pCur-next; return false; /* function 查找指定客户端* param name是指定客户端的用户名* return 返回socket表示查找成功,返回INVALID_SOCKET表示无此用户*/SOCKET FindClient(char* name) /从头遍历,一个个比较 pClient pCur = head; while (pCur = pCur-next) if (strcmp(pCur-userName, name) = 0) return pCur-sClient; return INVALID_SOCKET; /* function 根据SOCKET查找指定客户端* param client是指定客户端的套接字* return 返回一个pClient表示查找成功,返回NULL表示无此用户*/pClient FindClient(SOCKET client) /从头遍历,一个个比较 pClient pCur = head; while (pCur = pCur-next) if (pCur-sClient = client) return pCur; return NULL; /* function 计算客户端连接数* param client表示一个客户端对象* return 返回连接数*/int CountCon() int iCount = 0; pClient pCur = head; while (pCur = pCur-next) iCount+; return iCount; /* function 清空链表* return 无返回值*/void ClearClient() pClient pCur = head-next; pClient pPre = head; while (pCur) /head-1-2-3-4,先删除1,head-2,然后free 1 pClient p = pCur; pPre-next = p-next; free(p); pCur = pPre-next; /* function 检查连接状态并关闭一个连接* return 返回值*/void CheckConnection() pClient pclient = GetHeadNode(); while (pclient = pclient-next) if (send(pclient-sClient, , sizeof(), 0) = SOCKET_ERROR) if (pclient-sClient != 0) printf(Disconnect from IP: %s,UserName: %sn, pclient-IP, pclient-userName); char error128 = 0 ; /发送下线消息给发消息的人 sprintf(error, The %s was downline.n, pclient-userName); send(FindClient(pclient-ChatName), error, sizeof(error), 0); closesocket(pclient-sClient); /这里简单的判断:若发送消息失败,则认为连接中断(其原因有多种),关闭该套接字 RemoveClient(pclient-flag); break; /* function 指定发送给哪个客户端* param FromName,发信人* param ToName, 收信人* param data, 发送的消息*/void SendData(char* FromName, char* ToName, char* data) SOCKET client = FindClient(ToName); /查找是否有此用户 char error128 = 0 ; int ret = 0; if (client != INVALID_SOCKET & strlen(data) != 0) char buf128 = 0 ; sprintf(buf, %s: %s, FromName, data); /添加发送消息的用户名 ret = send(client, buf, sizeof(buf), 0); else/发送错误消息给发消息的人 if(client = INVALID_SOCKET) sprintf(error, The %s was downline.n, ToName); else sprintf(error, Send to %s message not allow empty, Please try again!n, ToName); send(FindClient(FromName), error, sizeof(error), 0); if (ret = SOCKET_ERROR)/发送下线消息给发消息的人 sprintf(error, The %s was downline.n, ToName); send(FindClient(FromName), error, sizeof(error), 0); server cpp:?/* #include #include #include #include ClientLinkList.h#pragma comment(lib,ws2_32.lib) SOCKET g_ServerSocket = INVALID_SOCKET; /服务端套接字SOCKADDR_IN g_ClientAddr = 0 ; /客户端地址int g_iClientAddrLen = sizeof(g_ClientAddr); typedef struct _Send char FromName16; char ToName16; char data128;Send,*pSend; /发送数据线程unsigned _stdcall ThreadSend(void* param) pSend psend = (pSend)param; /转换为Send类型 SendData(psend-FromName, psend-ToName, psend-data); /发送数据 return 0; /接受数据unsigned _stdcall ThreadRecv(void* param) int ret = 0; while (1) pClient pclient = (pClient)param; if (!pclient) return 1; ret = recv(pclient-sClient, pclient-buf, sizeof(pclient-buf), 0); if (ret = SOCKET_ERROR) return 1; if (pclient-buf0 = # & pclient-buf1 != #) /#表示用户要指定另一个用户进行聊天 SOCKET socket = FindClient(&pclient-buf1); /验证一下客户是否存在 if (socket != INVALID_SOCKET) pClient c = (pClient)malloc(sizeof(_Client); c = FindClient(socket); /只要改变ChatName,发送消息的时候就会自动发给指定的用户了 memset(pclient-ChatName, 0, sizeof(pclient-ChatName); memcpy(pclient-ChatName , c-userName,sizeof(pclient-ChatName); else send(pclient-sClient, The user have not online or not exits.,64,0); continue; pSend psend = (pSend)malloc(sizeof(_Send); /把发送人的用户名和接收消息的用户和消息赋值给结构体,然后当作参数传进发送消息进程中 memcpy(psend-FromName, pclient-userName, sizeof(psend-FromName); memcpy(psend-ToName, pclient-ChatName, sizeof(psend-ToName); memcpy(psend-data, -buf, sizeof(psend-data); _beginthreadex(NULL, 0, ThreadSend, psend, 0, NULL); Sleep(200); return 0; /开启接收消息线程void StartRecv() pClient pclient = GetHeadNode(); while (pclient = pclient-next) _beginthreadex(NULL, 0, ThreadRecv, pclient, 0, NULL); /管理连接unsigned _stdcall ThreadManager(void* param) while (1) CheckConnection(); /检查连接状况 Sleep(2000); /2s检查一次 return 0; /接受请求unsigned _stdcall ThreadAccept(void* param) _beginthreadex(NULL, 0, ThreadManager, NULL, 0, NULL); Init(); /初始化一定不要再while里面做,否则head会一直为NULL! while (1) /创建一个新的客户端对象 pClient pclient = (pClient)malloc(sizeof(_Client); /如果有客户端申请连接就接受连接 if (pclient-sClient = accept(g_ServerSocket, (SOCKADDR*)&g_ClientAddr, &g_iClientAddrLen) = INVALID_SOCKET) printf(accept failed with error code: %dn, WSAGetLastError(); closesocket(g_ServerSocket); WSACleanup(); return -1; recv(pclient-sClient, pclient-userName, sizeof(pclient-userName), 0); /接收用户名和指定聊天对象的用户名 recv(pclient-sClient, pclient-ChatName, sizeof(pclient-ChatName), 0); memcpy(pclient-IP, inet_ntoa(g_ClientAddr.sin_addr), sizeof(pclient-IP); /记录客户端IP pclient-flag = pclient-sClient; /不同的socke有不同UINT_PTR类型的数字来标识 pclient-Port = htons(g_ClientAddr.sin_port); AddClient(pclient); /把新的客户端加入链表中 printf(Successfuuly got a connection from IP:%s ,Port: %d,UerName: %s , ChatName: %sn, pclient-IP, pclient-Port, pclient-userName,pclient-ChatName); if (CountCon() = 2) /当至少两个用户都连接上服务器后才进行消息转发 StartRecv(); Sleep(2000); return 0; /启动服务器int StartServer() /存放套接字信息的结构 WSADATA wsaData = 0 ; SOCKADDR_IN ServerAddr = 0 ; /服务端地址 USHORT uPort = 18000; /服务器监听端口 /初始化套接字 if (WSAStartup(MAKEWORD(2, 2), &wsaData) printf(WSAStartup failed with error code: %dn, WSAGetLastError(); return -1; /判断版本 if (LOBYTE(wsaData.wVersion) != 2 | HIBYTE(wsaData.wVersion) != 2) printf(wVersion was not 2.2n); return -1; /创建套接字 g_ServerSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (g_ServerSocket = INVALID_SOCKET) printf(socket failed with error code: %dn, WSAGetLastError(); return -1; /设置服务器地址 ServerAddr.sin_family = AF_INET;/连接方式 ServerAddr.sin_port = htons(uPort);/服务器监听端口 ServerAddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);/任何客户端都能连接这个服务器 /绑定服务器 if (SOCKET_ERROR = bind(g_ServerSocket, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr) printf(bind failed with error code: %dn, WSAGetLastError(); closesocket(g_ServerSocket); return -1; /设置监听客户端连接数 if (SOCKET_ERROR = listen(g_ServerSocket, 20000) printf(listen failed with error code: %dn, WSAGetLastError(); closesocket(g_ServerSocket); WSACleanup(); return -1; _beginthreadex(NULL, 0, ThreadAccept, NULL, 0, 0); for (int k = 0;k 100;k+) /让主线程休眠,不让它关闭TCP连接. Sleep(10000000); /关闭套接字 ClearClient(); closesocket(g_ServerSocket); WSACleanup(); return 0; int main() StartServer(); /启动服务器 return 0;Client code:?#define _WINSOCK_DEPRECATED_NO_WARNINGS#include #include #include #include #include #pragma comment(lib,ws2_32.lib)#define RECV_OVER 1#define RECV_YET 0char userName16 = 0 ;char chatName16 = 0 ;int iStatus = RECV_YET;/接受数据unsigned _stdcall ThreadRecv(void* param) char buf128 = 0 ; while (1) int ret = recv(*(SOCKET*)param, buf, sizeof(buf), 0); if (ret = SOCKET_ERROR) Sleep(500); continue; if (strlen(buf) != 0) printf(%sn, buf); iStatus = RECV_OVER; else Sleep(100); return 0; /发送数据unsigned _stdcall ThreadSend(void* param) char buf128 = 0 ; int ret = 0; while (1) int c = getch(); if (c = 27) /ESC ASCII是27 memset(buf, 0, sizeof(buf); printf(Please input the chat name:); gets_s(buf); char b17 = 0 ; sprintf(b, #%s, buf); ret = send(*(SOCKET*)param,b , sizeof(b), 0); if (ret = SOCKET_ERROR) return 1; continue; if(c = 72 | c = 0 | c = 68)/为了显示美观,加一个无回显的读取字符函数 continue; /getch返回值我是经过实验得出如果是返回这几个值,则getch就会自动跳过,具体我也不懂。 printf(%s: , userName); gets_s(buf); ret = send(*(SOCKET*)param, buf, sizeof(buf), 0); if (ret = SOCKET_ERROR) return 1; return 0; /连接服务器int ConnectServer() WSADATA wsaData = 0 ;/存放套接字信息 SOCKET ClientSocket = INVALID_SOCKET;/客户端套接字 SOCKADDR_IN ServerAddr = 0 ;/服务端地址 USHORT uPort = 18000;/服务端端口 /初始化套接字 if (WSAStartup(MAKEWORD(2, 2), &wsaData) printf(WSAStartup failed with error code: %dn, WSAGetLastError(); return -1; /判断套接字版本 if (LOBYTE(wsaData.wVersion) != 2 | HIBYTE(wsaData.wVersion) != 2) printf(wVersion was not 2.2n); return -1; /创建套接字 ClientSocket = so

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论