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地址
原文地址:http://blog.csdn.net/huangshanchun/article/details/44002203