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博客

相关推荐
风静如云36 分钟前
OpenBMC:BmcWeb定义service
linux
sszdzq1 小时前
Docker
运维·docker·容器
book01211 小时前
MySql数据库运维学习笔记
运维·数据库·mysql
leoufung1 小时前
VIM FZF 安裝和使用
linux·编辑器·vim
bugtraq20212 小时前
XiaoMi Mi5(gemini) 刷入Ubuntu Touch 16.04——安卓手机刷入Linux
linux·运维·ubuntu
xmweisi2 小时前
【华为】报文统计的技术NetStream
运维·服务器·网络·华为认证
VVVVWeiYee2 小时前
BGP配置华为——路径优选验证
运维·网络·华为·信息与通信
陆鳐LuLu2 小时前
日志管理利器:基于 ELK 的日志收集、存储与可视化实战
运维·elk·jenkins
CodeWithMe3 小时前
[ Vim ] 常用命令 and 配置
linux·编辑器·vim
DC_BLOG3 小时前
Linux-GlusterFS进阶分布式卷
linux·运维·服务器·分布式