码迷,mamicode.com
首页 > 编程语言 > 详细

Linux 下基于多线程服务器/客服端聊天程序源码

时间:2015-03-01 14:32:06      阅读:228      评论:0      收藏:0      [点我收藏+]

标签:linux   多线程   socket   聊天   

Linux 下基于多线程服务器/客服端聊天程序,采用阻塞的socket技术,和多线程技术实现。

客服端程序:client.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#define BUFFSIZE 1024
#define ERRORCODE -1

static void *thread_send(void *arg)
{
	char buf[BUFFSIZE];
	int sd = *(int *) arg;
	while (1)
	{
		memset(buf, 0, sizeof(buf));
		read(STDIN_FILENO, buf, sizeof(buf));
		if (send(sd, buf, strlen(buf), 0) == -1)
		{
			printf("send error:%s \n", strerror(errno));
			break;
		}
	}
	return NULL;
}
static void* thread_recv(void *arg)
{
	char buf[BUFFSIZE];
	int sd = *(int *) arg;
	while (1)
	{
		memset(buf, 0, sizeof(buf));
		int rv = recv(sd, buf, sizeof(buf), 0);
		if (rv <= 0)
		{
			if(rv == 0) //server socket关闭情况
			{
				printf("server have already full !\n");
				exit(0);//退出整个客服端
			}
			printf("recv error:%s \n", strerror(errno));
			break;
		}
		printf("%s", buf);//输出接收到的内容
	}
	return NULL;
}
int run_client(char *ip_str, int port)
{
	int client_sd;
	int con_rv;
	pthread_t thrd1, thrd2;
	struct sockaddr_in client_sockaddr; //定义IP地址结构

	client_sd = socket(AF_INET, SOCK_STREAM, 0);
	if (client_sd == -1)
	{
		printf("socket create error:%s \n", strerror(errno));
		return ERRORCODE;
	}

	memset(&client_sockaddr, 0, sizeof(client_sockaddr));
	client_sockaddr.sin_port = htons(port); //指定一个端口号并将hosts字节型传化成Inet型字节型(大端或或者小端问题)
	client_sockaddr.sin_family = AF_INET;	//设置结构类型为TCP/IP
	client_sockaddr.sin_addr.s_addr = inet_addr(ip_str);
	//将字符串的ip地址转换成int型,客服端要连接的ip地址
	con_rv = connect(client_sd, (struct sockaddr*) &client_sockaddr,
			sizeof(client_sockaddr));
	//struct sockaddr 是很早以前定义的 struct sockaddr_in 是后定义的,目前用的比较多
	//调用connect连接到指定的ip地址和端口号,建立连接后通过socket描述符通信
	if (con_rv == -1)
	{
		printf("connect error:%s \n", strerror(errno));
		return ERRORCODE;
	}
	if (pthread_create(&thrd1, NULL, thread_send, &client_sd) != 0)
	{
		printf("thread error:%s \n", strerror(errno));
		return ERRORCODE;

	}
	if (pthread_create(&thrd2, NULL, thread_recv, &client_sd) != 0)
	{
		printf("thread error:%s \n", strerror(errno));
		return ERRORCODE;
	}
	pthread_join(thrd2, NULL);// 等待线程退出
	pthread_join(thrd1, NULL);
	close(client_sd);
	return 0;
}
int main(int argc, char *argv[])
{
	if (argc < 3)
	{
		printf("Usage:ip port,example:127.0.0.1 8080 \n");
		return ERRORCODE;
	}
	int port = atoi(argv[2]);
	char *ip_str = argv[1];
	run_client(ip_str,port);
	return 0;
}

服务端的程序:server.c

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#define MAXCONN  2
#define ERRORCODE -1
#define BUFFSIZE 1024

int count_connect = 0;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

struct pthread_socket
{
    int socket_d;
    pthread_t thrd;
};
static void *thread_send(void *arg)
{
    char buf[BUFFSIZE];
    int sd = *(int *) arg;
    memset(buf, 0, sizeof(buf));
    strcpy(buf, "hello,welcome to you! \n");
    if (send(sd, buf, strlen(buf), 0) == -1)
    {
        printf("send error:%s \n", strerror(errno));
        return NULL;
    }
    while (1)
    {
        memset(buf, 0, sizeof(buf));
        read(STDIN_FILENO, buf, sizeof(buf));
        if (send(sd, buf, strlen(buf), 0) == -1)
        {
            printf("send error:%s \n", strerror(errno));
            break;
        }
    }
    return NULL;
}

static void* thread_recv(void *arg)
{
    char buf[BUFFSIZE];
    struct pthread_socket *pt = (struct pthread_socket *) arg;
    int sd = pt->socket_d;
    pthread_t thrd = pt->thrd;
    while (1)
    {
        memset(buf, 0, sizeof(buf));
        int rv = recv(sd, buf, sizeof(buf), 0); //是阻塞的
        if (rv < 0)
        {
            printf("recv error:%s \n", strerror(errno));
            break;
        }
        if (rv == 0) // 这种情况说明client已经关闭socket连接
        {
            break;
        }
        printf("%s", buf); //输出接受到内容
    }
    pthread_cancel(thrd);
    pthread_mutex_lock(&mutex);
    count_connect--;
    pthread_mutex_unlock(&mutex);
    close(sd);
    return NULL;
}

static int create_listen(int port)
{

    int listen_st;
    struct sockaddr_in sockaddr; //定义IP地址结构
    int on = 1;
    listen_st = socket(AF_INET, SOCK_STREAM, 0); //初始化socket
    if (listen_st == -1)
    {
        printf("socket create error:%s \n", strerror(errno));
        return ERRORCODE;
    }
    if (setsockopt(listen_st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) //设置ip地址可重用
    {
        printf("setsockopt error:%s \n", strerror(errno));
        return ERRORCODE;
    }
    sockaddr.sin_port = htons(port); //指定一个端口号并将hosts字节型传化成Inet型字节型(大端或或者小端问题)
    sockaddr.sin_family = AF_INET;    //设置结构类型为TCP/IP
    sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);    //服务端是等待别人来连,不需要找谁的ip
    //这里写一个长量INADDR_ANY表示server上所有ip,这个一个server可能有多个ip地址,因为可能有多块网卡
    if (bind(listen_st, (struct sockaddr *) &sockaddr, sizeof(sockaddr)) == -1)
    {
        printf("bind error:%s \n", strerror(errno));
        return ERRORCODE;
    }

    if (listen(listen_st, 5) == -1) //     服务端开始监听
    {
        printf("listen error:%s \n", strerror(errno));
        return ERRORCODE;
    }
    return listen_st;
}

int accept_socket(int listen_st)
{
    int accept_st;
    struct sockaddr_in accept_sockaddr; //定义accept IP地址结构
    socklen_t addrlen = sizeof(accept_sockaddr);
    memset(&accept_sockaddr, 0, addrlen);
    accept_st = accept(listen_st, (struct sockaddr*) &accept_sockaddr,
            &addrlen);
    //accept 会阻塞直到客户端连接连过来 服务端这个socket只负责listen 是不是有客服端连接过来了
    //是通过accept返回socket通信的
    if (accept_st == -1)
    {
        printf("accept error:%s \n", strerror(errno));
        return ERRORCODE;
    }
    printf("accpet ip:%s \n", inet_ntoa(accept_sockaddr.sin_addr));
    return accept_st;

}
int run_server(int port)
{
    int listen_st = create_listen(port);    //创建监听socket
    pthread_t send_thrd, recv_thrd;
    struct pthread_socket ps;
    int accept_st;
    if (listen_st == -1)
    {
        return ERRORCODE;
    }
    printf("server start \n");
    while (1)
    {
        accept_st = accept_socket(listen_st); //获取连接的的socket
        if (accept_st == -1)
        {
            return ERRORCODE;
        }
        if (count_connect >= MAXCONN)
        {
            printf("connect have already be full! \n");
            close(accept_st);
            continue;
        }
        pthread_mutex_lock(&mutex);
        count_connect++;
        pthread_mutex_unlock(&mutex);
        if (pthread_create(&send_thrd, NULL, thread_send, &accept_st) != 0) //创建发送信息线程
        {
            printf("create thread error:%s \n", strerror(errno));
            break;

        }

        pthread_detach(send_thrd);        //设置线程可分离性,这样的话主线程就不用join
        ps.socket_d = accept_st;
        ps.thrd = send_thrd;
        if (pthread_create(&recv_thrd, NULL, thread_recv, &ps) != 0)//创建接收信息线程
        {
            printf("create thread error:%s \n", strerror(errno));
            break;
        }
        pthread_detach(recv_thrd); //设置线程为可分离,这样的话,就不用pthread_join
    }
    close(accept_st);
    close(listen_st);
    return 0;
}
//server main
int main(int argc, char *argv[])
{
    if (argc < 2)
    {
        printf("Usage:port,example:8080 \n");
        return -1;
    }
    int port = atoi(argv[1]);
    if (port == 0)
    {
        printf("port error! \n");
    } else
    {
        run_server(port);
    }
    return 0;
}
Makefile 文件

.SUFFIXES:.c .o
CC = gcc
SRCS1 = client.c
SRCS2 = server.c
OBJS1 = $(SRCS1:.c=.o)
OBJS2 = $(SRCS2:.c=.o)
EXEC1 = client
EXEC2 = server

start:$(OBJS1) $(OBJS2)
	$(CC) -g -o $(EXEC1) $(OBJS1) -lpthread
	$(CC) -g -o $(EXEC2) $(OBJS2) -lpthread
.c.o:
	$(CC) -g -o $@ -c $<
clean:
	rm -f $(OBJS1)
	rm -f $(OBJS2)

编译并运行server
技术分享

服务器/客服端 实现收发

技术分享

在windows上通过telnet 连接

技术分享

这里172.19.198.109是我的服务器的Ip地址

技术分享

Linux 下基于多线程服务器/客服端聊天程序源码

标签:linux   多线程   socket   聊天   

原文地址:http://blog.csdn.net/huangshanchun/article/details/44002203

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!