Linux下多线程网络通讯--多线程网络通讯

Linux下多线程网络通讯--多线程网络通讯/要多个线程

核心知识点:

  1. 主线程和子线程
  2. 子线程的剥离
  3. 主线程使用范围 子线程使用范围
  4. 主线程的数据传到子线中
  5. 思考范围

思考范围

  1. 多线程网络通讯–>1个服务器, 多个客户端连接服务器, 在主线程里监听并连接这些客户端, 由于客户端的ip不同,所以每一个服务器和客户端通讯的socket的文件文件描述符内容是不一样的, 所以需要一个很大的数组来保存每一个客户端的ip和端口
  2. 整体可以才拆解为两个部分:1.上面的连接 2. 下面的通信部分, 由于每个客户端的ip不一样+传送的内容不一样, 需要每个客户端都有一个专门的通信函数,也就是创建出多个线程,1个线程为1个客户端服务(通信)

源代码

服务器端
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <pthread.h>

#define SIZE 512

// 信息结构体  包含Socket 和通信文件描述符fd
struct SockInfo
{
	struct sockaddr_in addr; // 包含ip和端口
	int fd;
};

// 创建N个客户端需要的信息结构体 512个客户端
struct SockInfo infos[SIZE];


void* working(void* arg);

int main()
{

	// 1.创建服务器监听socket
	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if(fd == -1)
	{
		perror("服务器创建Socket失败");
		return -1;
	}

	// 2.套接字和本地的ip绑定 IP地址和端口号
	struct sockaddr_in saddr;
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(9999);// 注意大小端转换
	saddr.sin_addr.s_addr = INADDR_ANY;
	int ret = bind(fd, (struct sockaddr*)&saddr, sizeof(saddr));
	if(ret == -1)
	{
		perror("绑定失败");
		return -1;
	}

	// 3.设置监听
	ret = listen(fd, 128);			// 最大监听128个
	if(ret == -1)
	{
		perror("监听失败");
		return -1;
	}
	
	// 初始化信息结构体数组(= ip端口组合体 + 文件描述符)
	int max = sizeof(infos) / sizeof(infos[0]);
	for(int i = 0; i < max; i++)
	{
		bzero(&infos[i],  sizeof(infos[i]));
		infos[i].fd = -1;
	}

	// 4.阻塞并且等待客户端链接==创建客户端socket
	int addrlen = sizeof(struct sockaddr_in);
	// 不断连接客户端
	while(1)
	{
		struct SockInfo* pinfo;
		for(int i = 0; i < max; i++)
		{
			if(infos[i].fd == -1)
			{
				pinfo = &infos[i];
				break;
			}
		}
		
		// cfd 通信文件描述符, 在子线程里使用
		int cfd = accept(fd, (struct sockaddr*)&pinfo->addr, &addrlen);
		pinfo->fd = cfd;
		if(cfd == -1)
		{
			perror("客户端连接错误");
			break; // 可以重新尝试连接  使用contiue
		}
		//创建子线程
		pthread_t tid;
		pthread_create(&tid, NULL, working, pinfo);
		// 分离子线程 
		pthread_detach(tid);
	}
	close(fd);

	return 0;
}


// 子线程任务函数
void* working(void* arg)
{
	struct SockInfo* pinfo = (struct SockInfo*)arg;
	char ip[32];
	printf("IP地址为: %s, 端口号: %d\n", inet_ntop(AF_INET, 
				&pinfo->addr.sin_addr.s_addr, ip, sizeof(ip)),
				ntohs(pinfo->addr.sin_port));

	// 5.通信
	while(1)
	{
		char Buff[1024];
		int len = recv(pinfo->fd, Buff, sizeof(Buff), 0);
		if(len > 0)
		{
			printf("client say: %s\n",Buff);
			send(pinfo->fd, Buff, len, 0);		// 发送数据 
		}else if(len == 0)
		{
			printf("客户端 断开连接\n");
			break;
		}
		else
		{
			perror("接收数据错误");
			break;
		}
	}

	// 6.关闭文件描述符 
	close(pinfo->fd);
	pinfo->fd = -1;
	return NULL;
}

客户端

客户端代码不变

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

int main()
{
	// 1.创建通信的套接字
	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if(fd == -1)
	{
		perror("创建通信socket失败");
		return -1;
	}

	// 2.连接服务器  绑定服务器的ip和端口
	struct sockaddr_in saddr;
	// 初始化绑定的接口体
	saddr.sin_family = AF_INET;
	saddr.sin_port = htons(9999); //注意大小端转换
	inet_pton(AF_INET, "192.168.1.1", &saddr.sin_addr.s_addr);	
	int ret = connect(fd, (struct sockaddr*)&saddr, sizeof(saddr));
	if(ret == -1)
	{
		perror("连接错误");
		return -1;
	}
	
	int number = 0;
	while(1)
	{	
		// 3.通信
		char Buff[1024];
		sprintf(Buff, "通信中..., %d \n", number++);
		// 发送数据,通信的套接字  数据  实际长度 0 
		send(fd, Buff, strlen(Buff)+1, 0);

		// 4. 接收数据前先清空数据
		memset(Buff, 0 , sizeof(Buff));
		// 接收数据
		int len = recv(fd, Buff, sizeof(Buff), 0);
		if(len > 0)
		{
			printf("Server say: %s\n", Buff);	
		}else if(len == 0)
		{
			printf("连接断开");
			break;
		}else
		{
			perror("Recv");
			break;
		}
		sleep(1);
	}

	// 5. 关闭文件描述符;
	close(fd);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值