在Linux下用多线程实现两台客户端通过服务器进行聊天,类似qq功能

pub.h文件

/*
 * pub.h
 *
 *  Created on: 2016年7月21日
 *      Author: Administrator
 */

#ifndef PUB_H_
#define PUB_H_

#include <stdio.h>
#include <stdlib.h>



void init_socket_client();
void catch_Signal(int Sign);
int signal1(int signo, void (*func)(int));
void socket_accept(int st);
int socket_create(int port);

#endif /* PUB_H_ */
pub.c

/*
 * pub.c
 *
 *  Created on: 2016年7月21日
 *      Author: Administrator
 */

#include "pub.h"


#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <error.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>

#define BUFSIZE 1024

int socket_client[2];

int socket_create(int port)
{
    int st = socket(AF_INET, SOCK_STREAM, 0);
    int on = 1;
    if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1)
    {
        printf("setsockopt is failed %s\n", strerror(errno));
        return 0;
    }
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) == -1)
    {
        printf("bind is failed %s\n", strerror(errno));
        return 0;
    }
    if (listen(st, 300) == -1)
    {
        printf("listen is failed %s\n", strerror(errno));
        return 0;
    }
    return st;
}

void catch_Signal(int Sign)
{
	switch (Sign)
	{
	case SIGINT:
		printf("signal SIGINT\n");
		break;
	}
}

int signal1(int signo, void (*func)(int))
{
	struct sigaction act, oact;
	act.sa_handler = func;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	return sigaction(signo, &act, &oact);
}

void init_socket_client()// 初始化int socket_client[2]数组
{
	memset(socket_client, 0, sizeof(socket_client));
}

// 接收来自client发送的消息,index为0,代表接收到socket_client[0]消息,然后发送给socket_client[1]发送接收到的消息
// index为1,代表收到socket_client[1]消息,然后给socket_client[0]发送消息
// buf为发送消息内容,len为发送消息长度
void deliver(int index, const char *buf, ssize_t len)
{
	ssize_t rc = 0;
	if (index == 0)// 如果index为0,代表0给1发消息
	{
		if (socket_client[1] == 0)// 1不在
		{
			printf("%d:user not online\n", index);
		}
		else
		{
			rc = send(socket_client[1], buf, len, 0);// 消息下发给1
			printf("send '%s'\nsend %u bytes\n", buf, rc);
			if (rc <= 0)
			{
				if (rc == 0)
					printf("send failed, disconn\n");
				else
					printf("send failed, %s\n", strerror(errno));
			}
		}
	}

	if (index == 1)// 如果index为1,代表1给0发消息
	{
		if (socket_client[0] == 0)// 0不在线
		{
			printf("%d:user not online\n", index);
		}
		else
		{
			rc = send(socket_client[0], buf, len, 0);// 消息下发给1
			printf("send '%s'\nsend %u bytes\n", buf, rc);
			if (rc <= 0)
			{
				if (rc == 0)
					printf("send failed, disconn\n");
				else
					printf("send failed, %s\n", strerror(errno));
			}
		}
	}
}

void socket_work(int index)
{
	char buf[BUFSIZE];
	ssize_t rc = 0;
	while (1)
	{
		memset(buf, 0, sizeof(buf));
		rc = recv(socket_client[index], buf, sizeof(buf), 0);
		if (rc <= 0)// client连接断开
		{
			if (rc == 0)
				printf("%d:recv disconn\n", index);
			else
				printf("%d:recv failed, %s\n", index, strerror(errno));
			close(socket_client[index]);
			socket_client[index] = 0;// 如果client的socket已经断开那么要置为零
			break;
		}
		else
		{
			printf("%d:recv '%s'\nrecv %u bytes\n", index, buf, rc);
			// index为0,代表接收到socket_client[0]消息,然后socket_client[1]消息BUFSIZE
			// index为1,代表接收到socket_client[1]消息,然后socket_client[0]消息BUFSIZE
			// buf为发送消息内容
			deliver(index, buf, rc);
		}
	}
}

void *socket_contrl(void *arg)// server端的线程入口函数
{
	int client_st = *(int *)arg;// 得到从accept函数返回的来自client端的socket描述符
	free((int *)arg);
	printf("contrl_thread is begin\n");
	int index = 0;
	if (socket_client[0] == 0)// 如果socket_client[0]空闲,就将来自client端的socket付给socket_client[0]
	{
		socket_client[0] = client_st;
	}
	else// 如果socket[1]空闲,九江client端的socket付给socket_client[1]
	{
		if (socket_client[1] == 0)
		{
			socket_client[1] = client_st;
			index = 1;
		}
		else// socket_clien[0]socket_client[1]都不空闲
		{
			close(client_st);// socket_clien[2]两个成员都已经在线了,拒绝其他client连接
			return NULL;
		}
	}
	// 接收来自client端的链接,index为0代表接受来自socket_client[0]的消息
	// index为1代表接收来自socket_client[1]的消息
	socket_work(index);
	printf("contrl_thread is end\n");
	return NULL;
}

// 将addr转化为字符串
void sockaddr_toa(const struct sockaddr_in *addr, char *IPAddr)
{
	unsigned char *p = (unsigned char *)&(addr->sin_addr.s_addr);
	sprintf(IPAddr, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]);
}

void socket_accept(int st)
{
	pthread_t thr_d;
	pthread_attr_t attr;
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);// 设置线程属性可分离

	struct sockaddr_in client_addr;
	socklen_t len = sizeof(client_addr);
	while (1)
	{
		memset(&client_addr, 0, sizeof(client_addr));
		int client_st = accept(st, (void *)&client_addr, &len);// accept函数阻塞,知道
		if (client_st == -1)
		{
			printf("accept failed %s\n", strerror(errno));
			break;
		}
		else
		{
			char sIP[32];
			memset(sIP, 0, sizeof(sIP));
			sockaddr_toa(&client_addr, sIP);// 将IP地址转化为字符串
			printf("accept by %s\n", sIP);

			// 这里是多线程,如果用栈变量的话,可能会出现主线程退出后栈释放掉,但是子线程还在执行的情况
			// 出了在主函数之外,都不能将栈变量传入多线程的函数中
			int *tmp = malloc(sizeof(int));
			*tmp = client_st;
			// 只要accept到来自client端的socket,就启动一个线程,线程入口函数为socket_contrl
			pthread_create(&thr_d, &attr, socket_contrl, tmp);
		}
	}
	pthread_attr_destroy(&attr);
}
qqserver.c

/*
 * qqserver.c
 *
 *  Created on: 2016年7月21日
 *      Author: Administrator
 */

#include "pub.h"

int main(int arg, char *args[])
{
	if (arg < 2)
	{
		printf("usage:qqserver port\n");
		return 0;
	}

	int iport = atoi(args[1]);
	if (iport == 0)
	{
		printf("port %d is invalid\n", iport);
		return 0;
	}

	printf("qqserver is begin\n");
	//signal1(SIGINT, catch_Signal);// 捕捉SIGINT消息
	init_socket_client();// 初始化init_socket_client[2]数组
	int st = socket_create(iport);// 建立server端socket,在iport指定的断口号上listen
	if (st == 0)
		return 0;
	socket_accept(st);
	close(st);
	printf("qqserver is end\n");
	return 0;
}
qqclient.c

/*
 * qqclient.c
 *
 *  Created on: 2016年7月21日
 *      Author: Administrator
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <pthread.h>

#define BUFSIZE 1024

void *socket_read(void *arg)
{
	int st = *(int *)arg;
	char buf[BUFSIZE];
	while (1)
	{
		memset(buf, 0, sizeof(buf));
		ssize_t rc = recv(st, buf, sizeof(buf), 0);
		if (rc <= 0)
		{
			printf("recv failed, %s\n", strerror(errno));
			break;
		}
		else
		{
			printf("recv '%s'\nrecv %u byte\n", buf, rc);
		}
	}
	return NULL;
}

void *socket_write(void *arg)
{
	int st = *(int *)arg;
	char buf[BUFSIZE];
	while (1)
	{
		memset(buf, 0, sizeof(buf));
		read(STDIN_FILENO, buf, sizeof(buf));
		int ilen = strlen(buf);
		if (buf[ilen - 1] == '\n')
		{
			buf[ilen - 1] = 0;
		}
		ssize_t rc = send(st, buf, sizeof(buf), 0);
		printf("send '%s'\nsend %u byte\n", buf, rc);
		if (rc <= 0)
		{
			printf("send failed, %s\n", strerror(errno));
		}
	}
	return NULL;
}

int socket_connect(const char *hostname, int iport)
{
	int st = socket(AF_INET, SOCK_STREAM, 0);

	struct sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(iport);
	addr.sin_addr.s_addr = inet_addr(hostname);

	if (connect(st, (struct sockaddr *)&addr, sizeof(addr)) == -1)
	{
		printf("connect failed %s\n", strerror(errno));
	}
	else
	{
		printf("connect success\n");
	}
	return st;
}


int main(int arg, char *args[])
{
	if (arg < 3)
	{
		printf("usage:qqserver port\n");
		return 0;
	}

	int iport = atoi(args[2]);
	if (iport == 0)
	{
		printf("port %d is invalid\n", iport);
		return 0;
	}
	printf("qqclient is begin\n");
	int st = socket_connect(args[1], iport);
	if (st == 0)
		return 0;

	pthread_t thr_read, thr_write;
	pthread_create(&thr_read, NULL, socket_read, &st);// 启动读socket数据线程
	pthread_create(&thr_write, NULL, socket_write, &st);// 启动写socket数据线程
	pthread_join(thr_read, NULL);
	//pthread_join(thr_write, NULL);// 如果等待thr_write退出,main函数可能被挂起
	close(st);
	printf("qqclient is end\n");
	return 0;
}
makefile

.SUFFIXES:.c .o  
  
CC=gcc  
SRCS1=qqserver.c\
	pub.c  
SRCS2=qqclient.c\
	pub.c  
  
OBJS1=$(SRCS1:.c=.o)  
OBJS2=$(SRCS2:.c=.o)  
EXEC1=qqserver  
EXEC2=qqclient  
  
  
start: $(OBJS1) $(OBJS2)  
	$(CC) -o $(EXEC1) $(OBJS1) -lpthread
	$(CC) -o $(EXEC2) $(OBJS2) -lpthread 
	@echo '----------------ok------------'  
  
.c.o:  
	$(CC) -Wall -o $@ -c $<  
  
clean:  
	rm -f $(OBJS)





  • 0
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值