网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#include “cJSON.h”
#include <stdbool.h>
#define MAXSIZE 100
void *client_thread(void *arg);
sem_t sm;//定义一个信号量
struct ClientInfo{
int sockfd;//套接字
char id[16];
};
/*定义一个结构体数组
作用:来一个客户端,就把客户端的信息存在这个数组里面
memset()对其初始化*/
struct ClientInfo cinfo[MAXSIZE];
int main(void)
{
//初始化信号量:wq
sem_init(&sm, 0, 0);
//1.创建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)
{
perror("socket fail");
return -1;
}
//2.绑定
struct sockaddr_in addr; //定一个存储地址,端口号的替代结构体
//struct sockaddr\_in \*p = (struct sockaddr\_in\*)&addr;
memset(&addr, 0, sizeof(addr)); //初始化为0
addr.sin_family = AF_INET;//初始化地址族IPV4
addr.sin_port = htons(8989); //设置端口号(网络字节序号)
addr.sin_addr.s_addr = INADDR_ANY;//初始化绑定地址, 用INADDR\_ANY--表示绑定本机地址
int ret = bind(sockfd, (struct sockaddr\*)&addr, sizeof(addr));
if(ret < 0)
{
perror("bind fail:");
return -1;
}
//3.监听
ret = listen(sockfd, 5);
if(ret < 0)
{
perror("listen error");
return -1;
}
//4.接受链接, 没有客户端链接的时候->此函数阻塞
struct sockaddr_in clientaddr; //保存客户端地址
socklen_t len = sizeof(clientaddr);//保存地址长度
//初始化客户端信息结构体数组
memset(cinfo, 0, sizeof(cinfo));//所有数据都清零
//主线程只负责接受客户端链接,不收发数据->收发数据在线程中实现
while(1)
{
int clientfd = accept(sockfd, (struct sockaddr\*)&clientaddr, &len);
if(clientfd < 0)
{
perror("accept error");
return -1;
}
//创建一个线程
pthread_t id = 0;
pthread\_create(&id, NULL, client_thread, (void \*)&clientfd);
//分离线程
pthread\_detach(id);
//把客户端套接字描述符保存(链表, 数组)
//把套接字保存在最小未使用的数组元素中
for(int i=0; i<MAXSIZE; i++)
{
if(cinfo[i].sockfd == 0)
{
cinfo[i].sockfd = clientfd;//保存客户端套接字
break;
}
}
//获取信号量资源
sem\_wait(&sm);//目的是等待->线程执行完int clientfd = \*((int \*)arg)这句再循环上面的内容
}
sem\_destroy(&sm);//销毁信号量
//关闭套接字
close(sockfd);
return 0;
}
/*
json数据格式:通用
作用:解析{“id”:“99999”, “to”:“88888”, data:“hello world”}此对象
这对象的99999/888888/helloworld等字符串
(用字符串方法解析比较麻烦)
*/
bool parse_data(char *srcData, char *fromid, char *toid, char *data)
{
//字符串转cjson对象
cJSON *root = cJSON_Parse(srcData);
if(root == NULL) return false;
//解析to
cJSON *toObj = cJSON_GetObjectItem(root,“to”); //根据键获取对应的值
printf(“toId:%s\n”, toObj->valuestring);
sprintf(toid, “%s”, toObj->valuestring);
//解析id
cJSON \*idObj = cJSON\_GetObjectItem(root,"id"); //根据键获取对应的值
printf("Id:%s\n", idObj->valuestring);
sprintf(fromid,"%s", idObj->valuestring);
//解析to
cJSON \*dataObj = cJSON\_GetObjectItem(root,"data"); //根据键获取对应的值
printf("data:%s\n", dataObj->valuestring);
sprintf(data, "%s", dataObj->valuestring);
cJSON\_Delete(root);//释放
return true;
}
//线程-收发数据
void *client_thread(void *arg)
{
int clientfd = *((int *)arg); //传套接字过来,再转int型
//释放信号量资源
sem_post(&sm);
char recvbuffer[1024]={0};
char fromid[16]; //保存发送方id
char toid[16]; //保存接收方id
char data[128];//要发送的数据
while(1)
{
//第一次读取时候给服务器上报id(登录)->存起来和套接字绑定在一起
//{“id”:“99999”, “to”:“88888”, data:“hello world”};
int ret = read(clientfd, recvbuffer, 1024);
if(ret <= 0)
{
printf(“客户端%d掉线\n”, clientfd);
//把链表或数组中的套接字描述符要清理
break;
}
//解析
parse\_data(recvbuffer, fromid, toid, data);
//注册或者登陆要获取套接字里的id信息->从数组中查到套接字,所以对应的id
//当第一次链接的时候id不存在要自己添加
for(int i=0; i<MAXSIZE; i++)
{
if(cinfo[i].sockfd == clientfd && strlen(cinfo[i].id)==0)
{
strcpy(cinfo[i].id, fromid);
//发送反馈信息{type:0};
break;
}
}
//通过to对应的id号找到对应用套接字->套接字才能发数据
for(int i=0; i<MAXSIZE; i++)
{
if(cinfo[i].sockfd != 0 && strcmp(cinfo[i].id, toid)==0)
{
char tmpbuffer[1024]={0};
sprintf(tmpbuffer, "{\"type\":\"2\",\"from\":\"%s\",\"data\":\"%s\"}", fromid, data);
//发送数据
write(cinfo[i].sockfd, tmpbuffer, strlen(tmpbuffer)+1);
//发送反馈信息{type:1}
break;
}
}
printf("[clientfd=%d]%s\n",clientfd, recvbuffer);
//解析接收到的数据, 从链表或数组中找到对应用套接字描述符再发送数据
}
}
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!