在互联网越来越发达的今天,人们对网络的依赖越来越多,越来越离不开网 络,QQ、微软MSN、移动的Fetion等,都是做的比较成功的, 随着网络的日益普及,各种聊天工具也层出不穷,当我们学习《网络与程序设计》这门课程之 后,接下来的课程设计就是针对一个简单的网络聊天程序,利用C语言为开发工具,实现基本的通讯功能。
项目题目:socket聊天工具
功能:
1. 界面部分
主界面分别为登录(login),注册(register),离开(quit)三个选项,当用户登陆成功以后,二级界面为显示在线用户列表(show users_online),聊天(chat),返回(goback),注销(write off).
2. 功能部分
主函数使用TCP流式套接字与服务器进行通信,通信地址为本机地址,主进程负责连接服务器,另有聊天函数,在此函数中新建一个子进程用于接受来自服务器端的通信,而父进程负责发送客户端的数据到服务器,这样可以避免进程因为write函数和read函数一直阻塞,并且各个客户端发送过程中不会互相干扰.除此之外,还有注册函数和登录函数,用户注册和登录的信息会被发送到服务器,然后由服务器发送给MySQL。
1. 主函数模块
该模块负责服务器创建套接字、绑定、监听等初始化设置,以及如果有连接到达了就创建新的线程,然后在线程中处理客户端的请求。
2. 功能函数
服务器的功能是与客户端的功能大多一一对应的,例如登录、注册、聊天等,服务器根据客户机的请求提供不同的服务。此外,服务器与MySQL的交互函数也在这个模块里。
服务器与客户端都定义了两个结构体变量:
typedef struct
{
char loginid[20]; /*用户名*/
char password[20]; /*密码*/
} users_t;
typedef struct
{
int r_sockfd; //接收方的socketfd
char content[500]; //消息内容
} hdr_t;
其中,users_t存放用户的信息,包括用户的ID和这密码;hdr_t存放用户发送的消息,包括接收方的sockfd和要发送的消息内容。
1._register()
- 函数形式:void _register(int);
- 功能:处理客户的注册请求,成功则将客户发送的用户信息加入MySQL中的“users_info”表中,并发送注册成功的消息给用户;否则发送注册失败的提示信息给用户。
2._login()
- 函数形式:users_t _login(int);
- 功能:处理客户的登录请求,成功则将客户信息加入MySQL中的“users_online”表中,并发送登录成功的消息给用户;否则发送登录失败的提示信息给用户。
- 返回值:成功则返回包含客户信息的users_t结构体,否则返回NULL。
3._chat()
- 函数形式:void _chat(int);
- 功能:转发客户的消息,以实现聊天功能。
4._online()
- 函数形式:void _online(int);
- 功能:显示在线用户的列表。
5._quit()
- 函数形式:void _quit(int);
- 功能:用户退出。
6.write_off()
- 函数形式:void write_off(int);
- 功能:用户注销。
7.gif_handle_client()
- 函数形式:void gif_handle_client(int *);
- 功能:线程处理函数。创建新线程后线程的处理函数,根据用户输入的不同命令执行不同的动作。
8.sql_manage()
- 函数形式:void sql_manage(void)
- 功能:数据库处理函数,包括数据库的初始化、连接等行为。
1._chat()
使用了fork()函数创建一个子进程,当fork函数的返回值pid为0时,即代表子进程启动,这个子进程用于接受服务器端的数据,这里定义了一个字符数组缓冲区recvbuf来接受服务器端的数据,用read函数来将套接字缓冲区中的数据读入到recvbuf中,read函数还需要判断一下返回值的类型,返回-1代表客户端读取失败,返回0代表服务器关闭,返回其他值则代表正常接收数据。然后用fputs函数将缓冲区中的数据打印到客户端屏幕上。父进程用于发送客户端数据到服务器,这里定义一个发送缓冲区sendbuf,从客户端标准输入中接受数据到sendbuf中,然后在while循环中使用send函数将sendbuf中的数据复制到客户端与服务器连接的套接字缓冲区中。最后,如果while循环退出,则代表客户端的服务器父进程退出,这时因为父子进程中的变量和内存不共享,所以需要用一个信号函数通知子进程客户端已关闭。
2._login()
首先定义一个字符型的数组buf用于存储用户的登录信息,然后使用memcpy函数将用户信息结构体user中的信息拷贝到buf中,最后,需要用send将buf中的用户信息复制到客户端套接字缓冲区中,服务器端接收到后会与服务器的数据库中的已注册用户信息进行比较,如果数据库中有相应的用户,则服务器会将此用户信息做一系列处理,比如加入到数据库的在线用户表中,然后给客户端返回一个登陆成功的信息,客户端在login函数中使用recv函数接受此信息,并将信息打印出来反馈给客户,表示客户已经连接成功,这里服务器返回一个字符串:login successfully!,所以我们在登录成功后会在客户端看到服务器返回的字符串。
3. mysystem()
客户端定义了三个主界面功能,分别是login,register,quit。
其中,login表示用户登录,要求用户输入已经注册过的用户名和密码进行登录。如果没有注册的话,需要用户自己用register注册一个新的用户名和密码。register表示用户注册,进入后会要求用户输入相应的用户名和密码进行新用户的注册,如果服务器的数据库中没有注册过这个用户,就会将这个新用户的信息存储在用户索引数据库中。quit表示客户端用户退出。
在用户使用login后,客户顿会进入另外一个界面,即用户登录二级界面,里面包括四个二级界面功能,分别是:show users_online, chat, goback, write off, file translation。 其中show users_online表示显示在线用户列表,会将当前所有的在线用户打印到客户端的屏幕上,chat表示与其他客户端进行通信,goback对返回到上一级目录,write off会将当前登录用户注销,file translation表示文件传输功能,可以实现两个客户端之间的文件传输,或者可以将文件上传到服务器端。客户端使用这个功能后,会进入另外一个文件传输界面,主要包括一下功能:list,get,put,quit。其中list表示显示当前目录的所有文件,get表示从服务器获取文件,put表示传送文件到服务器端,quit表示离开当前界面。这里每一个界面功能都有一个相应的函数与之对应,如chat功能会对应一个函数,为chat函数,这样就可以实现界面与相应功能的连接。
4._register()
首先定义一个buf缓冲区,用于存储用户注册信息,之后由用户输入注册的用户名和密码,再使用send函数将用户名和密码发送到服务器端,和登录操作一样,服务器接收到用户的注册请求后会做相关的处理,然后返回一个套接字描述符给客户端,表示用户注册成功,之后客户端将这个文件描述符答应道屏幕上呈现给用户。
5.文件传输函数---translation file();
子函数1:int list_handler();
功能:list指令函数,打印服务端的文件
具体实现:
判断用户输入的字符串是否为“list”,如果是那么就使用recv函数接受服务器端的文件列表,然后打印到客户端程序。
子函数2:int get_file(char *filename);
功能:下载文件,传入文件名,文件不存在则报错
具体实现:
char buff[MAXSENDSIZE]; 接受文件缓冲区
Message msg; 消息结构体,存储用户输入命令
int nrd = 0;
int nwr = 0;
int fdwr; 套接字
char flag;
首先使用access函数检测文件是否存在,如果存在进行下一步,使用open函数打开文件准备写入,发送指令。
子函数3:int put_file(char *filename);
功能:向服务端发送文件
具体实现:
char buff[MAXSENDSIZE];发送文件缓冲区
Message msg; 发送的消息结构体、
int fdrd; 文件描述符
int nrd = 0;
首先比对用户输入是否和put一致,若是一致,则进行下一步比对文件是否存在,不存在就退出,如果存在,进行下一步,使用open打开文件,将文件内容通过send发行到服务器具体代码见文件传输文件。
1. 编译:在Linux环境下编译,需要引用mysqlclient和pthread库,因此编译命令为:
gcc server.c -o server -lpthread -lmysqlclient1
2. 执行时,MySQL可能需要root权限,故执行命令为:
sudo ./server1
1. 编译:仅有一个文件client.c,在当前目录下,故直接使用命令:
gcc client.c -o client -lpthread1
2. 进行编译.生成可执行文件:client, 再使用命令:
./client
3. 运行客户端程序即可.