当前位置: 首页 > article >正文

Linux下的socket编程

概述

下面是一个通用的server端程序源码,用于实现两个client之间的通信。

功能

1、接收user的命令cmd消息,并将cmd消息发送到dev;

2、接收dev的应答ack消息,并将ack消息发送到user;

架构实现

通过6个线程实现。

thread_init_user

用于创建user socket,并创建接收user cmd的线程recv_cmd_from_user 和将ack发送给user的线程send_ack_to_user

recv_cmd_from_user

用于接收user客户端的cmd,并激活send_ack_to_user线程

send_ack_to_user

将user cmd发送到设备

thread_init_dev

用于创建dev socket,并创建将cmd发送给dev的线程send_cmd_to_dev 和接收dev ack的线程recv_ack_from_dev

send_cmd_to_dev

用于将user客户端的cmd 发送到dev客户端

recv_ack_from_dev

用于接收dev客户端的ack,并激活send_ack_to_user

注意要点

客户端断开后,server端相应的线程会收到长度为0的信息,收到此消息后,要退出这个线程,同时通知这个客户端的发送线程退出。

源码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <termios.h>
#include <pthread.h>
#include <termios.h>
#include <string.h>
#include <dlfcn.h>
#include <sys/types.h>
#include <sys/time.h>
#include <dirent.h>
#include <signal.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
#include <netinet/tcp.h>
#include <string.h>
#include <stdbool.h>
#include <linux/rtnetlink.h>
#include <netinet/ether.h>
#include <semaphore.h>

#define FRAME_LEN_MAX 256

static sem_t sem_recv_user;
static sem_t sem_send_dev;
static sem_t sem_recv_ack_start;
static sem_t sem_recv_ack_end;

static unsigned char g_cmd_frame[FRAME_LEN_MAX];
static int g_cmd_frame_len;
static unsigned char g_ack_frame[FRAME_LEN_MAX];
static int g_ack_frame_len;

static bool g_user_thread_exit;
static bool g_dev_thread_exit;

static void *recv_cmd_from_user(void *arg)
{
	int fd = 0;
	int ret = 0;

	if (!arg) {
		printf("[%s] arg is NULL!\n", __FUNCTION__);
		return NULL;
	}

	fd = *(int *)arg;
	if (fd < 0) {
		printf("[%s] invailed arg!\n", __FUNCTION__);
		return NULL;
	}
	printf("[%s] thread run\n", __FUNCTION__);

	while (1) {
		ret = recv(fd, g_cmd_frame, FRAME_LEN_MAX, 0);
		printf("[%s] recv ret:%d!\n", __FUNCTION__, ret);
		if(ret <= 0) {
            //当用户客户端断开时,会走这里,本进程退出,同时发送信号量通知另个一线程退出
			g_user_thread_exit = 1;
			sem_post(&sem_recv_ack_start);
			break;
		}
		g_cmd_frame_len = ret;
		sem_post(&sem_recv_user);
	}

	close(fd);
	pthread_exit(NULL);
}

static void *send_cmd_to_dev(void *arg)
{
	int fd = 0;
	int ret = 0;

	if (!arg) {
		printf("[%s] arg is NULL!\n", __FUNCTION__);
		return NULL;
	}

	fd = *(int *)arg;
	if (fd < 0) {
		printf("[%s] invailed arg!\n", __FUNCTION__);
		return NULL;
	}
	printf("[%s] thread run\n", __FUNCTION__);

	while (1) {
		sem_wait(&sem_recv_user);
		if(g_dev_thread_exit) {
			break;
		}
		ret = send(fd, g_cmd_frame, g_cmd_frame_len, 0);
		printf("[%s] send to client:%d ret:%d!\n", __FUNCTION__, fd, ret);
		if(ret <= 0) {
			break;
		}
		sem_post(&sem_send_dev);
	}

	close(fd);
	pthread_exit(NULL);
}

static void *recv_ack_from_dev(void *arg)
{
	int fd = 0;
	int ret = 0;

	if (!arg) {
		printf("[%s] arg is NULL!\n", __FUNCTION__);
		return NULL;
	}

	fd = *(int *)arg;
	if (fd < 0) {
		printf("[%s] invailed arg!\n", __FUNCTION__);
		return NULL;
	}
	printf("[%s] thread run\n", __FUNCTION__);

	while (1) {
		sem_wait(&sem_send_dev);//dev客户端主动发送命令会被阻塞,直到user客户端发送命令
		sem_post(&sem_recv_ack_start);
		ret = recv(fd, g_ack_frame, FRAME_LEN_MAX);
		printf("[%s] recv ret:%d!\n", __FUNCTION__, ret);
		if(ret <= 0) {
			g_dev_thread_exit = 1;
			sem_post(&sem_recv_user);
			break;
		}
		g_ack_frame_len = ret;
		sem_post(&sem_recv_ack_end);
	}
	
	close(fd);
	pthread_exit(NULL);
}

static void *send_ack_to_user(void *arg)
{
	int fd = 0;
	int ret = 0;

	if (!arg) {
		printf("[%s] arg is NULL!\n", __FUNCTION__);
		return NULL;
	}

	fd = *(int *)arg;
	if (fd < 0) {
		printf("[%s] invailed arg!\n", __FUNCTION__);
		return NULL;
	}
	printf("[%s] thread run\n", __FUNCTION__);

	while (1) {
		struct timespec ts;
		sem_wait(&sem_recv_ack_start);
		if(g_user_thread_exit) {
			break;
		}
#if 0
        //对应答时间有要求,可以打开这里
		if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
				printf("[%s] ERROR:get current time failed\n",__FUNCTION__);
		ts.tv_nsec += 10 * MS_TO_NS;
		ret = sem_timedwait(&sem_recv_dev_end, &ts);
#else
		sem_wait(&sem_recv_dev_end);
#endif
		ret = send(fd, g_ack_frame, g_ack_frame_len);
		printf("[%s] send to client:%d ret:%d!\n", __FUNCTION__, fd, ret);
		if (ret <= 0) {
			break;
		}
	}
	close(fd);
	pthread_exit(NULL);
}

void *thread_init_user(void *arg)
{
	struct sockaddr_in	server_addr;
	struct sockaddr_in  client_addr;
	pthread_t tid_rx;
	pthread_t tid_tx;
	int server_fd = 0;
	int client_fd = 0;
	int ret = 0;
	int addr_size = sizeof(client_addr);
	int port = *(int *)arg;

	server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(server_fd < 0){
        printf("[%s] socket failed! port:%d\n", __FUNCTION__, port);
        return NULL;
    }
	printf("[%s] socket sucess! server_fd:%d, port:%d\n", __FUNCTION__,server_fd, port);

    bzero( &server_addr, sizeof(struct sockaddr_in) );
    server_addr.sin_family		= AF_INET;
    server_addr.sin_addr.s_addr = inet_addr((const char*)IP_ADDR_DFLT);
    server_addr.sin_port		= htons(port);
    if(bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        printf("[%s] bind failed!\n", __FUNCTION__);
        return NULL;
    }
	printf("[%s] bind sucess! server_fd:%d\n", __FUNCTION__,server_fd);

    if (listen(server_fd, 10) == -1) {
        printf("[%s] listen failed!\n", __FUNCTION__);
        return NULL;
    }
	printf("[%s] listen sucess! server_fd:%d\n\n", __FUNCTION__,server_fd);

	set_socket_alive(server_fd);

	while (server_fd != -1) {
		client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addr_size);
		if (client_fd < 0) {
			printf("[%s] accept failed: %d!\n", __FUNCTION__, client_fd);
			sleep(1);
			continue;
		}
		g_user_thread_exit = 0;
		printf("[%s] client: %d connected!\n", __FUNCTION__, client_fd);
		ret = pthread_create(&tid_rx, NULL, recv_cmd_from_user, &client_fd);
		if(ret != 0) {
			continue;
		}
		pthread_detach(tid_rx);

		ret = pthread_create(&tid_tx, NULL, send_ack_to_user, &client_fd);
		if(ret != 0) {
			continue;
		}
		pthread_detach(tid_tx);
	}

	return NULL;
}

void *thread_init_dev(void *arg)
{
	struct sockaddr_in	server_addr;
	struct sockaddr_in  client_addr;
	pthread_t tid_rx;
	pthread_t tid_tx;
	int server_fd = 0;
	int client_fd = 0;
	int ret = 0;
	int addr_size = sizeof(client_addr);
	int port = *(int *)arg;

	server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if(server_fd < 0){
        printf("[%s] socket failed!\n", __FUNCTION__);
        return NULL;
    }
	printf("[%s] socket sucess! server_fd:%d, port:%d\n", __FUNCTION__,server_fd, port);

    bzero( &server_addr, sizeof(struct sockaddr_in) );
    server_addr.sin_family		= AF_INET;
    server_addr.sin_addr.s_addr = inet_addr((const char*)IP_ADDR_DFLT);
    server_addr.sin_port		= htons(port);
    if(bind(server_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        printf("[%s] bind failed!\n", __FUNCTION__);
        return NULL;
    }
	printf("[%s] bind sucess! server_fd:%d\n", __FUNCTION__,server_fd);

    if (listen(server_fd, 10) == -1) {
        printf("[%s] listen failed!\n", __FUNCTION__);
        return NULL;
    }
	printf("[%s] listen sucess! server_fd:%d\n\n", __FUNCTION__,server_fd);

	set_socket_alive(server_fd);

	while (server_fd != -1) {
		client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addr_size);
		if (client_fd < 0) {
			printf("[%s] accept failed: %d!\n", __FUNCTION__, client_fd);
			sleep(1);
			continue;
		}
		g_dev_thread_exit = 0;
		printf("[%s] client: %d connected!\n", __FUNCTION__, client_fd);
		ret = pthread_create(&tid_rx, NULL, recv_ack_from_dev, &client_fd);
		if(ret != 0) {
			continue;
		}
		pthread_detach(tid_rx);

		ret = pthread_create(&tid_tx, NULL, send_cmd_to_dev, &client_fd);
		if(ret != 0) {
			continue;
		}
		pthread_detach(tid_tx);
	}

	return NULL;
}


int main(int argc, char *argv[])
{
	int user_port = PORT_USER;
	int dev_port = PORT_DEV;
	pthread_t thread_user;
	pthread_t thread_dev;
	pthread_attr_t attr;

	if(argc >= 2)
		user_port = auto_convert_0x(argv[1]);

	if(argc >= 3)
		dev_port = auto_convert_0x(argv[2]);

	printf("[%s]user_port:%d, dev_port:%d\n", __FUNCTION__, user_port, dev_port);

	sem_init(&sem_recv_user,0,0);
	sem_init(&sem_send_dev,0,0);
	sem_init(&sem_recv_ack_start,0,0);
	sem_init(&sem_recv_ack_end,0,0);

	pthread_attr_init (&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

	if (0 != pthread_create(&thread_user, &attr, thread_init_user, (void *)&user_port)) {
		printf("[%s] thread_user init failed\n", __FUNCTION__);
		return 0;
	}

	sleep(1);
	if (0 != pthread_create(&thread_dev, &attr, thread_init_dev, (void *)&dev_port)) {
		printf("[%s] thread_dev init failed\n", __FUNCTION__);
		return 0;
	}

	while(1) {
		sleep(UINT32_MAX);
	}

    return 0;
}

参考文档

linux下的Socket网络编程教程_linux socket 编程示例-CSDN博客


http://www.kler.cn/a/381321.html

相关文章:

  • 深度学习基础知识-损失函数
  • vue2和vue3在html中引用组件component方式不一样
  • 《AI产品经理手册》——解锁AI时代的商业密钥
  • 【docker】docker 环境配置及安装
  • 缓存、注解、分页
  • C++线程异步
  • LeetCode算法(二叉树)
  • vueui vxe-form 分享实现表单项的联动禁用,配置式表单方式的用法
  • 论文概览 |《IJGIS》2024.09 Vol.38 issue9
  • JavaScript基础语法部分-黑马跟课笔记
  • 在Vue和OpenLayers中使用移动传感器实现飞机航线飞行模拟
  • React第十三章(useTransition)
  • python之数据结构与算法(数据结构篇)-- 队列
  • 【青牛科技】GC3909替代A3909/ALLEGRO在摇头机、舞台灯、打印机和白色家电等产品上的应用分析
  • 半波正弦信号的FFT变换
  • vue用jenkins 打包项目项目关闭eslint检查
  • (四)、Manticore Search学习笔记之本地表介绍
  • 华为 HarmonyOS NEXT 原生应用开发: Video实现在线离线视频播放、以及实现控制器控制视频操作。
  • springboot 写真促销系统-计算机设计毕业源码88753
  • electron 中 ipcRenderer 作用
  • HTML5的文本样式
  • 基于 JAVASSM(Java + Spring + Spring MVC + MyBatis)框架开发一个医院挂号系统
  • 加强版 第五节图像处理与视频分析
  • 使用kettle同步数据流程
  • 视频一键转换3D:Autodesk 发布 Video to 3D Scene
  • 跟李沐学AI:BERT