C 语言编写聊天室一、需求二、知识点1、TCP/IP协议(1)C/S模型(2)常规步骤(3)阻塞与非阻塞 socket(4)epoll2、文件操作3、数据库三、实现1、思路2、代码(1)utility.h(2)server.c(3)client.c3、运行结果参考
一、需求
基于TCP编写,一个聊天室C/S。
基本要求
支持多个用户接入,实现聊天室的基本功能
使用epoll机制实现并发、增加效率
使用fork创建两个进程
将聊天信息写到管道(pipe),并发送给父进程
使用epoll机制接受服务端发来的信息,并显示给用户,使用户看到其他用户的聊天信息
注
暂且不含线程池、多线程编程、超时重传、确认收包等
后续待加功能
实现简单的文件传输。
用户注册和登陆
二、知识点
1、TCP/IP协议
(1)C/S模型
(2)常规步骤
TCP服务端
1、使用 socket()创建 TCP 套接字(socket)
2、将创建的套接字绑定到一个本地地址和端口上(Bind)
3、将套接字设为监听模式,准备接收客户端请求(listen)
4、等待客户请求到来: 当请求到来后,接受连接请求,返回一个对应于此次连接的新的套接字(accept)
5、用 accept 返回的套接字和客户端进行通信(使用write()/send()或send()/recv() )
6、返回,等待另一个客户请求
7、关闭套接字
//server.cpp代码(通信模块):
//服务端地址 ip地址 + 端口号
struct sockaddr_in serverAddr;
serverAddr.sin_family = PF_INET;
serverAddr.sin_port = htons(SERVER_PORT);
serverAddr.sin_addr.s_addr = inet_addr(SERVER_HOST);
//服务端创建监听socket
int listener = socket(PF_INET, SOCK_STREAM, 0);
if(listener < 0) { perror("listener"); exit(-1);}
printf("listen socket created \n");
//将服务端地址与监听socket绑定
if( bind(listener, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) {
perror("bind error");
exit(-1);
}
//开始监听
int ret = listen(listener, 5);
if(ret < 0) { perror("listen error"); exit(-1);}
printf("Start to listen: %s\n", SERVER_HOST);
TCP客户端
1、创建套接字(socket)
2、使用 connect() 建立到达服务器的连接(connect)
3、客户端进行通信(使用 write()/send() 或 send()/recv() )
4、使用 close() 关闭客户连接
//client.cpp代码(通信模块):
//客户要连接的服务端地址( ip地址 + 端口号)
struct sockaddr_in serverAddr;
serverAddr.sin_family = PF_INET;
serverAddr.sin_port = htons(SERVER_PORT);
serverAddr.sin_addr.s_addr = inet_addr(SERVER_IP);
// 创建套接字(socket)
int sock = socket(PF_INET, SOCK_STREAM, 0);
if(sock < 0) { perror("sock error"); exit(-1); }
//向服务器发出连接请求(connect)
if(connect(sock, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) {
perror("connect error");
exit(-1);
}
(3)阻塞与非阻塞 socket
分类阻塞非阻塞
概念对一个文件描述符指定的文件或设备, 一般有两种工作方式: 阻塞与非阻塞方式
当试图对该文件描述符进行读写时,如果当时没有数据可读,或者暂时不可写,程序就进入等待状态,直到有东西可读或者可写为止如果没有数据可读,或者不可写,读写函数马上返回,而不会等待
举例比如说小明去找一个女神聊天,女神却不在
如果小明舍不得走,只能在女神大门口死等着,当然小明可以休息。当女神来了,她会把你唤醒(囧,因为挡着她门了),这就是阻塞方式如果小明舍不得走,只能在女神大门口死等着,当然小明可以休息。当女神来了,她会把你唤醒(囧,因为挡着她门了),这就是阻塞方式
区别等待立即返回
本项目采用更高效的做法,所以应该将socket设置为非阻塞方式。这样能充分利用服务器资源,效率得到了很大提高本项目采用更高效的做法,所以应该将socket设置为非阻塞方式。这样能充分利用服务器资源,效率得到了很大提高
//utility.h代码(设置非阻塞函数模块):
//将文件描述符设置为非阻塞方式(利用fcntl函数)
int setnonblocking(int sockfd)
{
fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFD, 0)| O_NONBLOCK);
return 0;
}
(4)epoll
当服务端的在线人数越来越多,会导致系统资源吃紧,I/O 效率越来越慢,这时候就应该考虑 epoll 了。epoll 是 Linux 内核为处理大批句柄而作改进的 poll ,是 Linux 特有的 I/O 函数。
其特点