文章目录提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
- 前言
- 一、基本功能
- 二、两种实现方式
- 1.通过数组实现
- 2.通过结构体实现
- 总结
前言
提示:这里可以添加本文要记录的大概内容:
这段时间做了一个比较简单的即时通信软件,就把这个过程记录一下吧,一方面可以加深一下自己对这个项目的印象,另一方面也希望可以帮助到各位正在学习这一块内容的博友!!!
提示:以下是本篇文章正文内容,下面案例可供参考
一、基本功能登录、群聊
二、两种实现方式 1.通过数组实现代码如下(示例):
头文件: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:%sn", 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:%st port:%dn", 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:%dn", 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:%dn", 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; } 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("接收到第一个元素:%dn", rec_buf[0]); switch (rec_buf[0]) { case 1: memcpy(rec_name_buf, rec_buf + 1, 15); rec_name_buf[15] = ' '; break; case 2: memcpy(rec_name_buf + 16, rec_buf + 16, 15); rec_name_buf[31] = ' '; printf("服务器收到信息%s %sn", &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); } } }
客户端
#include2.通过结构体实现#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:%sn", 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 %sn", &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:%sn", 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:%st port:%dn", 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:%dn", 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:%dn", 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; } 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("接收到第一个元素:%dn", 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] = ' '; printf("服务器收到信息%s %sn", 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:%sn", 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 %sn", 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、逻辑更容易辨识,增加可读性。
总结
提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅实现登录及群聊功能,随学习深入增加更多功能。



