基于socket和线程实现一个简单的群聊功能。
编译方式
g++ -o chat_server chat_server.cpp -lpthread
g++ -o chat_client chat_client.cpp -lpthread
chat_server.cpp
监听客户端请求,当新增客户端时启动一个新的线程来通信,接收到数据后广播给其他客户端。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVER_PORT 8801
#define MAX_CONNECT 128
#define BUFFER_SIZE 2048
int chat_sockfd_idx = 0, chat_user_total = 1;
int chat_sockfd_list[MAX_CONNECT] = {0};
struct chat_item
{
int sockfd;
char name[32];
char ip[15];
};
void *chat_login(void * args);
void chat_send(char *msg, int sockfd = 0);
void chat_exit(chat_item * ci);
int main(int argc, char **argv)
{
int srv_sockfd, cli_sockfd, sock_opt = 1;
if((srv_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(1);
}
struct sockaddr_in server_sockaddr, client_sockaddr;
server_sockaddr.sin_family = AF_INET;
server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
server_sockaddr.sin_port = htons(SERVER_PORT);
setsockopt(srv_sockfd, SOL_SOCKET, SO_REUSEADDR, &sock_opt, sizeof(sock_opt));
if(bind(srv_sockfd, (struct sockaddr *)&server_sockaddr, sizeof(server_sockaddr)) == -1) {
perror("bind error");
exit(1);
}
if(listen(srv_sockfd, MAX_CONNECT) == -1) {
perror("listen error");
exit(1);
}
pthread_t pt;
socklen_t len = sizeof(client_sockaddr);
pthread_attr_t attr;
pthread_attr_init(& attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
printf("chatroom init successnn");
while(true) {
cli_sockfd = accept(srv_sockfd, (struct sockaddr *)&client_sockaddr, &len);
if(cli_sockfd == -1) {
continue;
}
chat_item ci;
ci.sockfd = cli_sockfd;
strcpy(ci.ip, inet_ntoa(client_sockaddr.sin_addr));
sprintf(ci.name, "guest_%d", chat_user_total++);
if(pthread_create(& pt, &attr, chat_login, (void *) &ci)) {
perror("pthread error");
continue;
}
chat_sockfd_list[chat_sockfd_idx++] = cli_sockfd;
printf("%s login, ip:%sn", ci.name, ci.ip);
}
pthread_attr_destroy (&attr);
close(srv_sockfd);
return 0;
}
void *chat_login(void *args)
{
chat_item ci = *((chat_item *) args);
char buffer[BUFFER_SIZE] = {0};
sprintf(buffer, "n", ci.name);
chat_send(buffer, 0);
memset(buffer, 0, sizeof(buffer));
char data[BUFFER_SIZE] = {0};
while(true) {
memset(buffer, 0, sizeof(buffer));
memset(data, 0, sizeof(data));
int len = recv(ci.sockfd, buffer, sizeof(buffer), 0);
if(len == 0 || strcmp(buffer, "exitn") == 0) {
chat_exit(&ci);
break;
}
if(strlen(buffer) == 0) {
continue;
}
time_t t = time(0);
char tmp[64] = {0};
strftime(tmp, sizeof(tmp), "%H:%M:%S", localtime(&t));
sprintf(data, "%s(%s) %sn", ci.name, ci.ip, tmp);
strcat(data, buffer);
chat_send(data, ci.sockfd);
}
}
void chat_send(char *msg, int from_sockfd)
{
for(int i=0; i
continue;
}
if(chat_sockfd_list[i] == 0) {
continue;
}
send(chat_sockfd_list[i], msg, strlen(msg), 0);
}
}
void chat_exit(chat_item * ci)
{
char buffer[BUFFER_SIZE] = {0};
sprintf(buffer, "", ci->name, ci->ip);
chat_send(buffer, ci->sockfd);
printf("%s logout, ip:%sn", ci->name, ci->ip);
int j = 0;
for(int i=0; i
close(ci->sockfd);
ci->sockfd = 0;
chat_sockfd_list[i] = 0;
j = i;
break;
}
}
memmove(chat_sockfd_list, chat_sockfd_list + j + 1, chat_sockfd_idx - j - 1);
chat_sockfd_idx--;
}
chat_client.cpp
启动两个线程一个用来接收数据,一个用来输入信息并发送。当输入exit并回车或者服务端断开时结束线程。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVER_HOST "127.0.0.1"
#define SERVER_PORT 8801
#define BUFFER_SIZE 2048
int cli_sockfd;
pthread_t trd_show, trd_push;
void *show_msg(void * args);
void *push_msg(void * args);
void bye(int sig);
int main(int argc, char **argv)
{
signal(SIGINT, bye);
if((cli_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket error");
exit(1);
}
struct sockaddr_in server_sockaddr;
server_sockaddr.sin_family = AF_INET;
server_sockaddr.sin_addr.s_addr = inet_addr(SERVER_HOST);
server_sockaddr.sin_port = htons(SERVER_PORT);
if(connect(cli_sockfd, (struct sockaddr *)&server_sockaddr, sizeof(server_sockaddr)) == -1) {
perror("connect error");
exit(1);
}
pthread_attr_t attr;
pthread_attr_init(& attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&trd_show, &attr, show_msg, &cli_sockfd);
pthread_create(&trd_push, &attr, push_msg, &cli_sockfd);
pthread_attr_destroy (&attr);
pthread_exit(NULL);
return 0;
}
void *show_msg(void * args)
{
int sockfd = *((int*)args);
char buffer[BUFFER_SIZE] = {0};
while(true) {
int len = recv(sockfd, buffer, sizeof(buffer), 0);
if(len <= 0) {
break;
}
printf("%sn", buffer);
memset(buffer, 0, sizeof(buffer));
}
bye(0);
}
void *push_msg(void * args)
{
int sockfd = *((int*)args);
char sdffer[BUFFER_SIZE] = {0};
while(fgets(sdffer, sizeof(sdffer), stdin)) {
if(strcmp(sdffer, "n") == 0) {
continue;
}
if(strcmp(sdffer, "exitn") == 0) {
break;
}
printf("n");
send(sockfd, sdffer, sizeof(sdffer), 0);
memset(sdffer, 0, sizeof(sdffer));
}
bye(0);
}
void bye(int sig)
{
printf("nByeBye.nn", sig);
pthread_cancel(trd_push);
pthread_cancel(trd_show);
close(cli_sockfd);
}
--EOF--
茶杯头甜蜜终章dlc 官方手机版v1.0.0.3
下载火柴人传说暗影格斗内置菜单 最新版v3.0.1
下载荒野乱斗测试服 安卓版v61.10.3
下载荒野乱斗彩虹服 安卓版v61.10.3
下载寒霜启示录 安卓版v1.25.10
寒霜启示录是一款生存模拟游戏,不少玩家可能对于末日都有着自己
末日城堡免广告版 安卓最新版v0.7.1
末日城堡免广告版是一款非常好玩的模拟经营类游戏,内部可以不看
甜蜜人生模拟器 最新版v1.4.5
甜蜜人生模拟器是一款非常好玩的模拟恋爱手游,玩家在这里能够对
武器锻造师内置功能菜单 v10.4
武器锻造师内置菜单版是游戏的破解版本,在该版本中为玩家提供了
开放空间overfield 安卓版v1.0.5
开放空间Overfield是一款箱庭养成经营手游,让你在广阔