提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
提示:这里可以添加本文要记录的大概内容:
这段时间做了一个比较简单的即时通信软件,就把这个过程记录一下吧,一方面可以加深一下自己对这个项目的印象,另一方面也希望可以帮助到各位正在学习这一块内容的博友!!!
提示:以下是本篇文章正文内容,下面案例可供参考
登录、群聊
代码如下(示例):
头文件:echo_server.h
#ifndef _ECHO_SERVER_H
#define _ECHO_SERVER_H
typedef struct buff
{
int num; /*已连接数量*/
int fd[128];
int port[128];
} ReceiveBuf;
ReceiveBuf receive;
typedef struct login
{
int type; /* 定义数据类型 */
char name_buf[256];
char message_buf[256];
} Login;
Login log_in;
#endif
服务器端
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "echo_server.h"
#define NUM 1024
#define SERVER_PORT 4349
#define MESSAGE 2
#define LOGIN 1
//客户端之间的数据通信--使用数组
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int perror_exit(const char *des)
{
fprintf(stderr, "%s error!reason:%s\n", des, strerror(errno));
// close(sock);
exit(1);
}
void *communication(void *client_sock_num);
int main()
{
//新建socket的文件描述符
int sock;
int bind_rec = 0;
int listen_rec = 0;
// socket地址
struct sockaddr_in server_addr;
//第一步socket函数,参数IPV4、tcp协议、默认协议
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
perror_exit("create socket");
}
//清空结构体
bzero(&server_addr, sizeof(server_addr));
//结构体传参,协议、ip地址、端口
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(SERVER_PORT);
int on = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
//绑定socket和addr
bind_rec = bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (bind_rec == -1)
{
perror_exit("bind");
}
//声明socket处于监听状态
listen(sock, 1024);
if (listen_rec == -1)
{
perror_exit("listen");
}
//服务器信息搭建好,可以等待连接并读取数据
printf("等待客户端连接!\n");
while (1)
{
//声明客户端结构体
struct sockaddr_in client;
//声明接收连接返回值变量
int client_sock;
//声明读函数返回值
int accept_num = 0;
int count = 0;
ssize_t recv_ret;
//声明接收ip缓存区数组
char client_ip[64];
//声明读取数据缓存区
socklen_t client_addr_len;
client_addr_len = sizeof(client);
memset(&receive, 0, sizeof(receive));
while (1)
{
client_sock = accept(sock, (struct sockaddr *)&client, &client_addr_len);
if (client_sock > 0)
{
printf("客户端已经连接!\n");
printf("client ip:%s\t port:%d\n", inet_ntop(AF_INET, &client.sin_addr.s_addr, client_ip, sizeof(client_ip)), ntohs(client.sin_port));
receive.fd[receive.num] = client_sock;
receive.port[receive.num] = ntohs(client.sin_port);
printf("num:%d\n", receive.num);
// recv_ret = recv(client_sock, client_name, sizeof(client_name) - 2, NULL);
pthread_mutex_init(&mutex, NULL); //创建互斥锁
pthread_t ReadThread; //
pthread_create(&ReadThread, NULL, communication, (void *)&receive.fd[receive.num]);
receive.num++;
count++;
printf("count:%d\n", count);
}
if (client_sock == -1)
{
perror_exit("accept");
}
}
close(sock);
return 0;
}
}
/* 群发消息给其他人 */
void broadcast_msg(int fd, char *data, int len)
{
int ret = 0;
for (size_t i = 0; i < receive.num; i++)
{
/* 如果是自己 不要返回重复消息给自己 */
if (fd == receive.fd[i])
{
continue;
}
/* code */
ret = send(receive.fd[i], data, len, 0);
if (ret < 0)
{
perror("send error");
}
puts("broadcase_msg success!");
}
}
void *communication(void *p_client_sock)
{
ssize_t recv_ret;
int *client_sock = (int *)p_client_sock;
char rec_buf[256];
char rec_name_buf[256];
int i;
while (1)
{
recv_ret = recv(*client_sock, rec_buf, sizeof(rec_buf) - 1, NULL);
if (recv_ret > 0)
{
// printf("%s Line %d:\n", __FILE__, __LINE__);
// printf("接收到第一个元素:%d\n", rec_buf[0]);
switch (rec_buf[0])
{
case 1:
memcpy(rec_name_buf, rec_buf + 1, 15);
rec_name_buf[15] = '\0';
break;
case 2:
memcpy(rec_name_buf + 16, rec_buf + 16, 15);
rec_name_buf[31] = '\0';
printf("服务器收到信息%s %s\n", &rec_name_buf[0], &rec_name_buf[16]);
pthread_mutex_lock(&mutex);
broadcast_msg(*client_sock, rec_name_buf, sizeof(rec_name_buf));
pthread_mutex_unlock(&mutex);
break;
default:
break;
}
// sleep(1);
}
}
}
客户端
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "echo_server.h"
#define SERVER_PORT 4349
#define SERVER_IP "192.168.1.8"
#define MESSAGE 2
#define LOGIN 1
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//客户端之间的数据通信--使用数组
void text(int a, int b, int c);
int perror_exit(const char *des)
{
fprintf(stderr, "%s error!reason:%s\n", des, strerror(errno));
// close(sock);
exit(1);
}
void *client_communication(void *q_server_sock);
int main(int argc, char *argv[])
{
//创建网络文件描述符
int sockfd = 0;
int server_sock = 0;
int read_ret = 0;
ssize_t send_ret;
//创建发送指针
char *message = NULL;
char *message1 = NULL;
//声明服务器ip数组
char server_ip[128];
//声明读取数组
char namebuf[256];
//声明socket结构体
socklen_t server_addr_len;
struct sockaddr_in server_addr;
ReceiveBuf receive;
int count = 0;
//打开网络客户端通讯端口
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
{
perror_exit("socket");
exit(1);
}
memset(&server_addr, 0, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);
server_addr.sin_port = htons(SERVER_PORT);
server_sock = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
if (server_sock < 0)
{
perror("connect");
return 1;
}
namebuf[0] = LOGIN;
printf("欢迎登录杠精艺术交流会聊天室,请输入姓名:\n");
message = &namebuf[1];
scanf("%s", message);
send_ret = send(sockfd, namebuf, sizeof(namebuf), NULL);
if (send_ret == -1)
{
perror_exit("send name");
exit(1);
}
// memset(namebuf, 0, sizeof(namebuf));
while (1)
{
pthread_mutex_init(&mutex, NULL); //创建互斥锁
pthread_t client_ReadThread; //
pthread_create(&client_ReadThread, NULL, client_communication, (void *)&sockfd);
namebuf[0] = MESSAGE;
message = &namebuf[16];
scanf("%s", message);
send_ret = send(sockfd, namebuf, sizeof(namebuf), NULL);
if (send_ret == -1)
{
perror_exit("send");
exit(1);
}
}
close(sockfd);
// return 0;
}
void *client_communication(void *q_server_sock)
{
ssize_t recv_ret;
int *server_sock = (int *)q_server_sock;
char rec_buf[256];
while (1)
{
recv_ret = recv(*server_sock, rec_buf, sizeof(rec_buf), 0);
if (recv_ret > 0)
{
printf("%s %s\n", &rec_buf[0], &rec_buf[16]);
}
else if (recv_ret == -1)
{
perror_exit("recv");
exit(1);
}
}
}
代码如下(示例):
服务器
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "echo_server.h"
#define NUM 1024
#define SERVER_PORT 4349
#define MESSAGE 2
#define LOGIN 1
//客户端之间的数据通信--使用结构体
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int perror_exit(const char *des)
{
fprintf(stderr, "%s error!reason:%s\n", des, strerror(errno));
// close(sock);
exit(1);
}
void *communication(void *client_sock_num);
int main()
{
//新建socket的文件描述符
int sock;
int bind_rec = 0;
int listen_rec = 0;
// socket地址
struct sockaddr_in server_addr;
//第一步socket函数,参数IPV4、tcp协议、默认协议
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
perror_exit("create socket");
}
//清空结构体
bzero(&server_addr, sizeof(server_addr));
//结构体传参,协议、ip地址、端口
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(SERVER_PORT);
int on = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
//绑定socket和addr
bind_rec = bind(sock, (struct sockaddr *)&server_addr, sizeof(server_addr));
if (bind_rec == -1)
{
perror_exit("bind");
}
//声明socket处于监听状态
listen(sock, 1024);
if (listen_rec == -1)
{
perror_exit("listen");
}
//服务器信息搭建好,可以等待连接并读取数据
printf("等待客户端连接!\n");
while (1)
{
//声明客户端结构体
struct sockaddr_in client;
//声明接收连接返回值变量
int client_sock;
//声明读函数返回值
int accept_num = 0;
int count = 0;
ssize_t recv_ret;
//声明接收ip缓存区数组
char client_ip[64];
//声明读取数据缓存区
socklen_t client_addr_len;
client_addr_len = sizeof(client);
memset(&receive, 0, sizeof(receive));
while (1)
{
client_sock = accept(sock, (struct sockaddr *)&client, &client_addr_len);
if (client_sock > 0)
{
printf("客户端已经连接!\n");
printf("client ip:%s\t port:%d\n", inet_ntop(AF_INET, &client.sin_addr.s_addr, client_ip, sizeof(client_ip)), ntohs(client.sin_port));
receive.fd[receive.num] = client_sock;
receive.port[receive.num] = ntohs(client.sin_port);
printf("num:%d\n", receive.num);
// recv_ret = recv(client_sock, client_name, sizeof(client_name) - 2, NULL);
pthread_mutex_init(&mutex, NULL); //创建互斥锁
pthread_t ReadThread; //
pthread_create(&ReadThread, NULL, communication, (void *)&receive.fd[receive.num]);
receive.num++;
count++;
printf("count:%d\n", count);
}
if (client_sock == -1)
{
perror_exit("accept");
}
}
close(sock);
return 0;
}
}
/* 群发消息给其他人 */
void broadcast_msg(int fd, char *data, int len)
{
int ret = 0;
for (size_t i = 0; i < receive.num; i++)
{
/* 如果是自己 不要返回重复消息给自己 */
if (fd == receive.fd[i])
{
continue;
}
/* code */
ret = send(receive.fd[i], data, len, 0);
if (ret < 0)
{
perror("send error");
}
puts("broadcase_msg success!");
}
}
void *communication(void *p_client_sock)
{
ssize_t recv_ret;
int *client_sock = (int *)p_client_sock;
Login log_in_ser;
char rec_name_buf[256];
int i;
while (1)
{
pthread_mutex_lock(&mutex);
recv_ret = recv(*client_sock, &log_in_ser, sizeof(log_in_ser), NULL);
if (recv_ret > 0)
{
// printf("%s Line %d:\n", __FILE__, __LINE__);
// printf("接收到第一个元素:%d\n", rec_buf[0]);
switch (log_in_ser.type)
{
case 1:
break;
case 2:
broadcast_msg(*client_sock, &log_in_ser, sizeof(log_in_ser));
// memcpy(rec_name_buf + 16, rec_buf + 16, 15);
// receive.log_buf[31] = '\0';
printf("服务器收到信息%s %s\n", log_in_ser.name_buf, log_in_ser.message_buf);
break;
default:
break;
}
// sleep(1);
}
pthread_mutex_unlock(&mutex);
}
}
客户端
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "echo_server.h"
#define SERVER_PORT 4349
#define SERVER_IP "192.168.1.8"
#define MESSAGE 2
#define LOGIN 1
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
//客户端之间的数据通信--使用结构体
//创建结构体,将需要的不同数据类型的参数进行整理,将一段数据根据类型进行拆分,有利于代码实现的方便性
//指针为地址,所有函数的形参中有指针均可以传各种类型的地址
//与数组实现的好处:1、省去位置偏移量的考虑;2、省去客户端、服务器多次创建缓冲区数组;3、逻辑更容易辨识,增加可读性;
void text(int a, int b, int c);
int perror_exit(const char *des)
{
fprintf(stderr, "%s error!reason:%s\n", des, strerror(errno));
// close(sock);
exit(1);
}
void *client_communication(void *q_server_sock);
int main(int argc, char *argv[])
{
//创建网络文件描述符
int sockfd = 0;
int server_sock = 0;
int read_ret = 0;
ssize_t send_ret;
//创建发送指针
char *message = NULL;
char *message1 = NULL;
//声明服务器ip数组
char server_ip[128];
//声明读取数组
char namebuf[256];
//声明socket结构体
socklen_t server_addr_len;
struct sockaddr_in server_addr;
ReceiveBuf receive;
int count = 0;
//打开网络客户端通讯端口
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd == -1)
{
perror_exit("socket");
exit(1);
}
memset(&server_addr, 0, sizeof(struct sockaddr_in));
server_addr.sin_family = AF_INET;
inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);
server_addr.sin_port = htons(SERVER_PORT);
server_sock = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr));
if (server_sock < 0)
{
perror("connect");
return 1;
}
log_in.type = LOGIN;
// log_in.name_buf[0] = log_in.type;
printf("欢迎登录杠精艺术交流会聊天室,请输入姓名:\n");
message = log_in.name_buf;
scanf("%s", message);
send_ret = send(sockfd, &log_in, sizeof(log_in), NULL);
if (send_ret == -1)
{
perror_exit("send name");
exit(1);
}
// memset(namebuf, 0, sizeof(namebuf));
while (1)
{
pthread_mutex_init(&mutex, NULL); //创建互斥锁
pthread_t client_ReadThread; //
pthread_create(&client_ReadThread, NULL, client_communication, (void *)&sockfd);
log_in.type = MESSAGE;
// log_in.message_buf[0] = log_in.type;
message = log_in.message_buf;
scanf("%s", message);
send_ret = send(sockfd, &log_in, sizeof(log_in), NULL);
if (send_ret == -1)
{
perror_exit("send");
exit(1);
}
}
close(sockfd);
// return 0;
}
void *client_communication(void *q_server_sock)
{
ssize_t recv_ret;
int *server_sock = (int *)q_server_sock;
Login log_in_client;
while (1)
{
pthread_mutex_lock(&mutex);
recv_ret = recv(*server_sock, &log_in_client, sizeof(log_in_client), 0);
if (recv_ret > 0)
{
printf("%s %s\n", log_in_client.name_buf, log_in_client.message_buf);
}
else if (recv_ret == -1)
{
perror_exit("recv");
exit(1);
}
pthread_mutex_unlock(&mutex);
}
}
使用结构体与数组实现总结:
创建结构体,将需要的不同数据类型的参数进行整理,将一段数据根据类型进行拆分,有利于代码实现的方便性。
指针为地址,所有函数的形参中有指针均可以传各种类型的地址。
使用结构体好处:1、省去位置偏移量的考虑;2、省去客户端、服务器多次创建缓冲区数组;3、逻辑更容易辨识,增加可读性。
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅实现登录及群聊功能,随学习深入增加更多功能。