[Windows网络编程课设]南工程聊天室服务器源码

时间:2022-12-28 08:31:54

南工程聊天室服务器源码

MyMsg.h:

#pragma once
/************************************************************
* 文件名:MyMsg.h
* 描述: 服务器和客户端共用的头文件,消息定义
*************************************************************/
#ifndef _MYMSG_H_
#define _MYMSG_H_

enum {
LOGIN_MSG, //登录
LOGIN_MSG_RES, //登录响应
QUERY_FQQ, //查询单个好友QQ
QUERY_FQQ_RES, //查询单个好友QQ的响应
TALK_MSG, //聊天
REQUEST_ALL_FRD, //请求所有qq好友信息
APPLY_ALL_FRD //应答所有qq好友信息
};

struct LoginMsg {
unsigned char id; //消息编号
char qq[6]; //登录ID号
};

struct LoginMsgResponse {
unsigned char id; //消息编号
unsigned char isOK;
char reason[100]; //如果失败,则在此写入失败原因
};

struct FriendQqMsg {
unsigned char id; //消息编号
char qq[6];
};

struct FriendQqMsgResponse {
unsigned char id; //消息编号
unsigned char isOK;
};

struct TalkMsg {
unsigned char id; //消息编号
char qq[6];
char fqq[6];
char info[200];
};

struct RequestAllFriend {
unsigned char id; //消息编号
};

struct AllFriendMsg {
unsigned char id; //消息编号
char qq[6];
};

#endif

MyTcp.cpp

/************************************************************
* 文件名:MyTCP.cpp
* 描述: TCP/IP通信socket封装
*************************************************************/
#include <stdio.h>
#include <WinSock2.h>
#include "MyTools.h"
#pragma comment (lib, "ws2_32.lib")

#define PORT 5001
int ls; //侦听套接字
int flag;//记录服务器或是客户端的标志

/* 描述: 初始化服务器或客户端, 该函数在使用中必需首先调用,客户端和服务器 */
int initSock(int IsServer)
{
WSADATA data;

if (WSAStartup(1, &data) <0)
printMsgExit("call WSAStartup() failure!");

ls = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ls < 0)
printMsgExit("创建套接字失败!");

flag = IsServer;
if (IsServer != 0)
{//服务器
struct sockaddr_in servAddr;
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
servAddr.sin_port = htons(PORT);

if (bind(ls, (struct sockaddr *)&servAddr, sizeof(servAddr))<0) {
printMsg("bind套接字失败!\n");
return -1;
}

if (listen(ls, 10)<0) {
printMsg("listen套接字失败!\n");
return -1;
}
}

return 0;
}

/* 描述: 仅用于客户端, 连接服务器 */
int tcpConnect(const char *serverIP, unsigned short port)
{
struct sockaddr_in servAddr;

memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr(serverIP);
servAddr.sin_port = htons(port);

if (connect(ls, (struct sockaddr *)&servAddr, sizeof(servAddr))<0)
{
printMsgExit("连接服务器失败!\n");
}

return ls;
}

/* 描述: 发送数据 */
int tcpSend(unsigned int sock, const char *sendBuf, int sendBufLen)
{
int len = send(sock, sendBuf, sendBufLen, 0);
return len;
}

/* 描述: 接收数据 */
int tcpRecv(unsigned int sock, char *recvBuf, int recvBufLen)
{
int len = recv(sock, recvBuf, recvBufLen, 0);
return len;
}

void tcpClose(unsigned int sock)
{
closesocket(sock);
WSACleanup();
}


MyTcp.h

#pragma once
/************************************************************
* 文件名:MyTCP.h
* 描述: TCP通信头文件
*************************************************************/
#ifndef _MYTCP_H_
#define _MYTCP_H_

int initSock(int IsServer);
int tcpConnect(const char *serverIP, unsigned short port);
int tcpAccept();
int tcpSend(unsigned int sock, const char *sendBuf, int sendBufLen);
int tcpRecv(unsigned int sock, char *recvBuf, int recvBufLen);
void tcpClose(unsigned int sock);

#endif

MyTools.cpp

/************************************************************
* 文件名:MyTools.cpp
* 描述: 共用的文件,工具函数定义
*************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "MyTools.h"

extern int flag;

void printMsg(const char * msg) {
if (flag)
printf("\t【南工服务器豪华版】%s", msg);
else
printf("\t【南工客户端豪华版】%s", msg);
}

void printInt(int value) {
if (flag)
printf("\t【南工服务器豪华版】%d", value);
else
printf("\t【南工客户端豪华版】%d", value);
}

void printMsgExit(const char * msg) {
printMsg(msg);
exit(0);
}

void printIntExit(int value) {
printInt(value);
exit(0);
}

MyTools.h

#pragma once
/************************************************************
* 文件名:MyTools.h
* 描述:工具函数定义
*************************************************************/
#ifndef _MYTOOLS_H_
#define _MYTOOLS_H_
void printMsgExit(const char * msg);
void printMsg(const char * msg);
void printInt(int value);
void printIntExit(int value);
#endif

TalkClient.cpp

/************************************************************
* 文件名:TalkClient.cpp
* 描述: 客户端入口,界面操作
*************************************************************/
#include <stdio.h>
#include <Windows.h>

#include "TalkClientService.h"

void displayMenu();
void exitProc();
void defProc();
void procMenu();

char serverip[6];

int main(int argc, char *argv[])
{
if (argc < 2)
{
printf("请输入服务器的IP地址。。。\n");
return 0;
}
strcpy(serverip, argv[1]);
displayMenu();
procMenu();
while (1) Sleep(10000);
}

char ch; //菜单选择
void displayMenu()
{
static int t = 1;
if (t++ == 1)
{
printf("\n\n\n\n\n");
printf("\n\t @ @ ");
printf("\n\t @ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @ ");
printf("\n\t @ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @ ");
printf("\n\t @ ** ** ********** ** ** ******** ** ** @ ");
printf("\n\t @ ** ** ** ** ** ** ** ** ** @ ");
printf("\n\t @ ** ** ** ** ** ** ** ** ** @ ");
printf("\n\t @ ********** ********** ** ** ** ** ** ** @ ");
printf("\n\t @ ** ** ** ** ** ** ** ** ** @ ");
printf("\n\t @ ** ** ** ** ** ** ** @ ");
printf("\n\t @ ** ** ********** ******** ******** ******** @@ @@ @ ");
printf("\n\t @ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @ ");
printf("\n\t @ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^@ ");
printf("\n\t @ @ ");

printf("\n -----By Hins ");
printf("\n\n\n\n\n\t\tPress 'Enter' to enter the menu......"); /*按任一键进入主菜单*/
}
getchar(); /*从键盘读取一个字符,但不显示于屏幕*/
system("cls"); /*清屏*/
printf("\n");
printf("\n");
printf("\n");
printf("\t************************************************\n");
printf("\t* *\n");
printf("\t* 南工2016聊天客户端程序 *\n");
printf("\t* Version 2.1 豪华版 *\n");
printf("\t* *\n");
printf("\t* *\n");
printf("\t* *\n");
printf("\t* 1) 登录(L) *\n");
printf("\t* 2) 退出(e) *\n");
printf("\t* by---HinsShwan *\n");
printf("\t************************************************\n");
printf("\n");
connectServer();
printf("\t您的选择是:");
ch = getchar();
}

void procMenu()
{
switch (ch)
{
case 'l':
case 'L':
case '1':
statusConnected();
break;

case 'e':
case 'E':
case '2':
exitProc();
break;

default:
defProc();
}
}

void exitProc()
{
printf("\t客户端程序已经终止!\n");
exit(0);
}

void defProc()
{
printf("\t 输入字符错误!\n");
}

TalkClientService.cpp

/************************************************************
* 文件名:TalkClientService.cpp
* 描述: 客户端业务实现
*************************************************************/
#include "MyTCP.h"
#include "MyTools.h"
#include "TalkClientService.h"
void myRecvThread();
#define PORT 5001


extern char serverip[6];

char qq[6] = { 0 };
char fqq[6] = { 0 };
int sock;

int status = STUS_START;

void mainProc()
{
while (1)
{

switch (status)
{
case STUS_CONNECTED:
statusConnected();
break;

case STUS_WAIT_LOGIN_RES:
printMsg("等待服务器响应..........\n");
Sleep(1000);
break;

case STUS_LOGINED:
statusLogined();
break;

case STUS_WAIT_QUERY_FQQ_RES:
printMsg("等待服务器响应..........\n");
Sleep(1000);
break;

case STUS_TALK:
statusTalk();
break;
case STUS_WAIT_QUERY_ALLQQ_RES:
printMsg("等待服务器响应..........\n");
Sleep(1000);
break;
}
}
}

void statusTalk()
{
printMsg("退出聊天(Exit) 我说>>");
setbuf(stdin, NULL); //清空键盘缓冲区

char buf[1024] = { 0 };
gets_s(buf);
if (strcmp(buf, "Exit") == 0)
{
printMsg("客户端已经退出!\n");
statusLogined();
}

else
{
struct TalkMsg msg;

msg.id = TALK_MSG;
strcpy(msg.qq, qq);
strcpy(msg.fqq, fqq);
strcpy(msg.info, buf);
status = 5;
tcpSend(sock, (const char *)&msg, sizeof(msg));
}

return;
}

void statusLogined()
{
char buf0[10] = { 0 };
int rt = 0;
while (rt == 0)
{
memset(buf0, 0, sizeof(buf0));
printMsg("显示在线好友(L)|找好友聊天(T)|退出(E) 默认(T)>>");
setbuf(stdin, NULL); //清空键盘缓冲区
gets_s(buf0);
if (strcmp(buf0, "T") == 0 || strcmp(buf0, "t") == 0)
{
printMsg("选择聊天好友......\n");
while (1)
{
printMsg("输入聊天好友QQ号(最多不超过5位): 退出请输入“quit”");
char tmp[1024];
scanf("%s", tmp);
if (strlen(tmp) > 5)
{
printMsg("您输入的qq号长度大于5个,请重新输入\n");
}
else if (strcmp(tmp, qq) == 0)
{
printMsg("您输入的qq号是自己号码,难道您要自言自语吗?::) 请重新输入\n");
}
else if (strcmp(tmp, "quit") == 0)
break;
else
{ //发送要聊天的好友QQ号给服务器
struct FriendQqMsg msg;
msg.id = QUERY_FQQ;
strcpy(msg.qq, tmp);
strcpy(fqq, tmp);
status = STUS_WAIT_QUERY_FQQ_RES;
tcpSend(sock, (const char *)&msg, sizeof(msg));
rt = 1;
break;
}
}
}
else if (strcmp(buf0, "L") == 0 || strcmp(buf0, "l") == 0)
{
char buf2[10] = { 0 };
memset(buf2, 0, sizeof(buf2));
struct RequestAllFriend res;
res.id = REQUEST_ALL_FRD;
memcpy(buf2, &res, sizeof(res));
tcpSend(sock, (const char*)buf2, sizeof(buf2));
status = STUS_WAIT_QUERY_ALLQQ_RES;
Sleep(100);
}
else if (strcmp(buf0, "E") == 0 || strcmp(buf0, "e") == 0)
{
printMsg("客户端已经退出!\n");
exit(0);
}
else
{
printMsg("输入内容错误(手抖了吧-_-),请重新输入\n");

}


};

}

void connectServer()
{
printMsg("开始连接服务器......\n");
initSock(0); //客户端
sock = tcpConnect(serverip, PORT);
printMsg("连接服务器成功!\n");

//创建一个线程用于接收服务器发送的信息
DWORD threadId;
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)myRecvThread, NULL, 0, &threadId);
}

//创建接收线程专用于接收并处理服务器返回的收数据
void myRecvThread()
{
char buf[1024] = { 0 };
while (1)
{
memset(buf, 0, sizeof(buf));
tcpRecv(sock, buf, sizeof(buf));
unsigned char id = (unsigned char)buf[0];
switch (id)
{
case LOGIN_MSG_RES:
procLoginMsgResponse((struct LoginMsgResponse *)buf);
break;

case QUERY_FQQ_RES:
procFriendQqMsgResponse((struct FriendQqMsgResponse *)buf);
break;

case TALK_MSG:
procTalkMsg((struct TalkMsg *)buf);
break;
case APPLY_ALL_FRD:
procApplyAllFriend((struct AllFriendMsg *)buf);
break;

}
}
sprintf(buf, "结束myRecvThread线程:%d.........\n");
printMsg(buf);
exit(0);
}

void statusConnected()
{
printMsg("\n\t请输入登录信息\n");

while (1)
{
printMsg("本机的QQ号(最多不超过5位数字):");
char tmp[1024];
scanf("%s", tmp);
int i;
if (strlen(tmp)>5)
{
printMsg("您输入的qq号长度大于5个,请重新输入\n");
continue;
}

for (i = 0;i<strlen(tmp);i++)
{
if (tmp[i] >= '0' && tmp[i] <= '9')
{
continue;
}
else
break;
}

if (i == strlen(tmp))
{
strcpy(qq, tmp);
break;

}
else
{
printMsg("输入信息有误,请重新输入\n");
continue;
}

}

struct LoginMsg msg;
msg.id = LOGIN_MSG;
status = STUS_WAIT_LOGIN_RES;
strcpy(msg.qq, qq);
tcpSend(sock, (const char *)&msg, sizeof(msg));
mainProc(); //进入主处理程序
return;
}

void procLoginMsgResponse(struct LoginMsgResponse *msg)
{
if (msg->isOK == 1)
{
printMsg("登录成功!\n");
status = STUS_LOGINED;//已登录状态
}
else
{
printMsg("登录失败!\n");
printMsgExit(msg->reason);
printf("\n");
}
}

void procFriendQqMsgResponse(struct FriendQqMsgResponse *msg)
{
if (msg->isOK == 1)
{
printMsg("好友目前在线,可以开始聊天...\n");
status = STUS_TALK;
}
else
{
printMsg("好友不存在啊,再换个人试试!\n");
status = STUS_LOGINED;
}
}

void procTalkMsg(struct TalkMsg *msg)
{
char buf[1024] = { 0 };

if (strcmp(msg->qq, fqq) == 0)
{//表示是自己发起的聊天,仅仅显示信息即可
printf("\n");
sprintf(buf, "%s说>>%s", fqq, msg->info);
printMsg(buf);
}
else
{//是陌生人发起的聊天
printf("\n");
sprintf(buf, "%s说>>%s", msg->qq, msg->info);
printMsg(buf);
}
}

void procApplyAllFriend(struct AllFriendMsg *msg)
{

char buf[100] = { 0 };
sprintf(buf, "%s", msg->qq);
printMsg("在线的QQ好友: \n");
printMsg(buf);
printf("\n");
status = STUS_LOGINED;
}


TalkClientService.h

#pragma once
/************************************************************
* 文件名:TalkClientService.h
* 描述: 客户端业务层函数声明
*************************************************************/
#ifndef _TALKCLIENTSERVICE_H_
#define _TALKCLIENTSERVICE_H_
#include <Windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "MyMsg.h"

void connectServer();
void procLoginMsgResponse(struct LoginMsgResponse *msg);
void procFriendQqMsgResponse(struct FriendQqMsgResponse *msg);
void procTalkMsg(struct TalkMsg *msg);
void statusConnected();
void statusLogined();
void statusTalk();
void procApplyAllFriend(struct AllFriendMsg *msg);

/** 客户端状态 **/
enum {
STUS_START,
STUS_CONNECTED,
STUS_WAIT_LOGIN_RES,
STUS_LOGINED,
STUS_WAIT_QUERY_FQQ_RES,
STUS_TALK,
STUS_WAIT_QUERY_ALLQQ_RES
};

#endif