TCP聊天室

一.服务器代码

#include "hv/hloop.h"
#include "hv/hsocket.h"
#include "hv/hbase.h"
#include "list.h"

struct chatroom_s {
	hloop_t* loop;//事件循环结构体指针
	hio_t* listenio;//监听io结构体
	int roomid;//房间号
	struct list_head conns;//链表存储房间中的连接
};
struct chatroom_s chatroom;

struct connection_s {
   	hio_t* connio;// 连接io结构体指针
   	char addr[SOCKADDR_STRLEN];// 套接字地址
   	struct list_node node;// 链表结点
};

void boardcast_talk(struct chatroom_s *room, char *msg, int msglen, hio_t* talk_io)
{
	struct list_node* node;
   	struct connection_s* cur;
	list_for_each (node, &room->conns) {
       		cur = list_entry(node, struct connection_s, node);
		if(cur->connio != talk_io)
			hio_write(cur->connio, msg, msglen);
	}
}

void boardcast(struct chatroom_s *room, char *msg, int msglen)
{
	struct list_node* node;
   	struct connection_s* cur;
	list_for_each (node, &room->conns) {
       		cur = list_entry(node, struct connection_s, node);
		hio_write(cur->connio, msg, msglen);
	}
}

void on_recv(hio_t *io, void *buf, int readbytes)
{
	char localaddrstr[SOCKADDR_STRLEN] = {0};
   	char peeraddrstr[SOCKADDR_STRLEN] = {0};
   	printf("[%s] <=> [%s]\n",
           SOCKADDR_STR(hio_localaddr(io), localaddrstr),
           SOCKADDR_STR(hio_peeraddr(io), peeraddrstr));
   	printf("< %.*s", readbytes, (char*)buf);
	
	//广播成员说的话
	char msg[256] = {0};
   	int msglen = 0;
	struct connection_s* conn = (struct connection_s*)hevent_userdata(io);
	msglen = snprintf(msg, sizeof(msg), "成员[%s]:%.*s", conn->addr, readbytes, (char*)buf);
	boardcast_talk(&chatroom, msg, msglen, conn->connio);
}

void leave(struct chatroom_s* room, struct connection_s* conn) {
   	// 从链表里删除
   	list_del(&conn->node);

   	// 广播有成员离开聊天室
   	char msg[256] = {0};
   	int msglen = snprintf(msg, sizeof(msg), "成员[%s]离开房间[%d]\r\n", conn->addr, room->roomid);
   	boardcast(room, msg, msglen);
}

static void on_close(hio_t* io) {
   	printf("关闭描述符%d,错误=%d\n", hio_fd(io), hio_error(io));

   	struct connection_s* conn = (struct connection_s*)hevent_userdata(io);
   	if (conn) {
   	    hevent_set_userdata(io, NULL);

    	    // 断连离开聊天室
    	    leave(&chatroom, conn);
   	    HV_FREE(conn);
   	}
}

void on_accept(hio_t *io)
{
	//获取服务器和客户端IP地址	
	char localaddrstr[SOCKADDR_STRLEN] = {0};
   	char peeraddrstr[SOCKADDR_STRLEN] = {0};
	printf("建立连接(连接描述符:%d):%s=====>%s\r\n\r\n",hio_fd(io) , SOCKADDR_STR(hio_localaddr(io), localaddrstr), SOCKADDR_STR(hio_peeraddr(io), peeraddrstr));
		
	hio_setcb_close(io, on_close);
	hio_setcb_read(io, on_recv);
	hio_read(io);
	
	struct connection_s *conn = NULL;
	HV_ALLOC_SIZEOF(conn);
	conn->connio = io;
	strcpy(conn->addr, peeraddrstr);
	hevent_set_userdata(io, conn);
	list_add(&conn->node, &chatroom.conns);
	
	//向新来的成员发送当前聊天室成员列表
	char msg[256] = {0};
   	int msglen = 0;
	struct list_node* node;
   	struct connection_s* cur;
	msglen = snprintf(msg, sizeof(msg), "房间[%06d] 成员列表:\r\n", chatroom.roomid);
	hio_write(io, msg, msglen);
	list_for_each (node, &chatroom.conns) {
       		cur = list_entry(node, struct connection_s, node);
		msglen = snprintf(msg, sizeof(msg), "%s\r\n", cur->addr);
		hio_write(io, msg, msglen);
	}
	hio_write(io, "\r\n", 2);
	
	//向全体成员广播新成员加入
	msglen = snprintf(msg, sizeof(msg), "新成员加入房间[%06d]:%s\r\n\r\n",chatroom.roomid, conn->addr);
	boardcast(&chatroom, msg, msglen);
	
}

int main(int argc, char **argv)
{
	if(argc <2)
	{
		printf("Please input the port");
		return -1;
	}
	int port = atoi(argv[1]);
	
	//创建按事件循环
	hloop_t* loop = hloop_new(0);
	
	//创建TCP服务器,设置accept回调函数
	hio_t* listenio = hloop_create_tcp_server(loop, "0.0.0.0", port, on_accept);//监听所有IP地址,端口号为8080
	
	chatroom.loop = loop;
	chatroom.listenio = listenio;
	chatroom.roomid = 1;
	list_init(&chatroom.conns);
	
	//循环事件体
	hloop_run(loop);
	//释放事件体
	hloop_free(&loop);
	return 0;
}

二.客户端代码

#include "hv/hloop.h"
#include "hv/hsocket.h"
#include "hv/hbase.h"
#include "hv/htime.h"
#include "list.h"
#include <string.h>

typedef struct client_s{
	hio_t *io;
} client_t;
client_t client;

void on_recv(hio_t *io, void *buf, int readbytes)
{
	printf("%.*s\n", readbytes, (char*)buf);
}

static void on_close(hio_t* io) {
}

void on_connect(hio_t *io)
{
	hio_setcb_close(io, on_close);
	hio_setcb_read(io, on_recv);
	hio_read(io);
	client.io = io;
}

void on_cin(hio_t *io, void *str, int readbytes)
{
    	// 发送输入的字符串
    	hio_write(client.io, str, readbytes);
	fflush(stdin);
	fflush(stdout);
}

int main(int argc, char **argv)
{
	if(argc <2)
	{
		printf("Please input the port");
		return -1;
	}
	int port = atoi(argv[1]);
	
	//创建按事件循环
	hloop_t* loop = hloop_new(0);
	
	//创建读取终端输入的描述符输入事件
	hio_t* io = hio_get(loop, 0);
	hio_read(io);
	hio_setcb_read(io, on_cin);
	
	//创建TCP服务器,设置accept回调函数
	hio_t* listenio = hloop_create_tcp_client(loop, "127.0.0.1", port, on_connect);//监听所有IP地址,端口号为8080
	
	//循环事件体
	hloop_run(loop);
	//释放事件体
	hloop_free(&loop);
	return 0;
}

三.运行结果截图

 

 

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、实验目的 1.掌握通信规范的制定及实现。 2.练习较复杂的网络编程,能够把协议设计思想应用到现实应用中。 二、实验内容和要求 1.进一步熟悉VC++6编程环境; 2.利用VC++6进行较复杂的网络编程,完成网络聊天室的设计及编写; 三、实验(设计)仪器设备和材料 1.计算机及操作系统:PC机,Windows; 2.网络环境:可以访问互联网; 四、 TCP/IP程序设计基础 基于TCP/IP的通信基本上都是利用SOCKET套接字进行数据通讯,程序一般分为服务器端和用户端两部分。设计思路(VC6.0下): 第一部分 服务器端 一、创建服务器套接字(create)。 二、服务器套接字进行信息绑定(bind),并开始监听连接(listen)。 三、接受来自用户端的连接请求(accept)。 四、开始数据传输(send/receive)。 五、关闭套接字(closesocket)。 第二部分 客户端 一、创建客户套接字(create)。 二、与远程服务器进行连接(connect),如被接受则创建接收进程。 三、开始数据传输(send/receive)。 四、关闭套接字(closesocket)。 CSocket的编程步骤:(注意我们一定要在创建MFC程序第二步的时候选上Windows Socket选项,其中ServerSocket是服务器端用到的,ClientSocket是客户端用的。) (1)构造CSocket对象,如下例: CSocket ServerSocket; CSocket ClientSocket; (2)CSocket对象的Create函数用来创建Windows Socket,Create()函数会自行调用Bind()函数将此Socket绑定到指定的地址上面。如下例: ServerSocket.Create(823); //服务器端需要指定一个端口号,我们用823。 ClientSocket.Create(); //客户端不用指定端口号。 (3)现在已经创建完基本的Socket对象了,现在我们来启动它,对于服务器端,我们需要这个Socket不停的监听是否有来自于网络上的连接请求,如下例: ServerSocket.Listen(5);//参数5是表示我们的待处理Socket队列中最多能有几个Socket。 (4)对于客户端我们就要实行连接了,具体实现如下例: ClientSocket.Connect(CString SerAddress,Unsinged int SerPort);//其中SerAddress是服务器的IP地址,SerPort是端口号。 (5)服务器是怎么来接受这份连接的呢?它会进一步调用Accept(ReceiveSocket)来接收它,而此时服务器端还须建立一个新的CSocket对象,用它来和客户端进行交流。如下例: CSocket ReceiveSocket; ServerSocket.Accept(ReceiveSocket); (6)如果想在两个程序之间接收或发送信息,MFC也提供了相应的函数。如下例: ServerSocket.Receive(String,Buffer); //String是你要发送的字符串,Buffer是发送字符串的缓冲区大小。ServerSocket.Send(String,Butter);//String是你要接收的字符串,Buffer是接收字符串的缓冲区大小。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值