赞
踩
同类文章:
基于Linux的SOCKET编程之TCP半双工Client-Server聊天程序
基于Linux的Socket编程之TCP全双工Server-Client聊天程序
高并发服务器编程之多进程并发服务器
多进程并发与多线程并发实现过程差不多,只是多线程的同步、资源回收与多进程还是有很多区别的。多进程不需要记录子进程的信息,而多线程需要记录。
或许需要将子线程设置为分离态(pthread_detach()的效率,不如在创建线程之前就设置好attr属性高)。当然我们也可以在主线程中创建一个线程专门用来回收用于通信的线程,这点和多进程基本相同,而我们采用创建线程之前就设置好attr属性为分离状态的方法回收结束的子线程。
在多进程中说过C/S心跳机制,多线程也可以使用,但是需要注意:本地127.0.0.1测试时,不会经过网卡,在经过过滤器时便直接接到目的端口。所以说单机无法测试C/S心跳机制。
multithread.h:
/*multithread.h*/
#ifndef _MULTI_THREAD_H_
#define _MULTI_THREAD_H_
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#define BUF_SIZE 1024
#endif
(1)、server.c:
/*server.c*/
#include<multithread.h>
#include<server.h>
void sys_err(const char * ptr_err)
{
perror(ptr_err);
exit(EXIT_FAILURE);
}
void socket_server_create(const char * ipaddr, const char * port)
{
struct sockaddr_in seraddr;
int listenfd, ret;
if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
sys_err("socket create");
seraddr.sin_family = AF_INET;
seraddr.sin_addr.s_addr = inet_addr(ipaddr);
seraddr.sin_port = htons(atoi(port));
if( (ret = bind(listenfd, (struct sockaddr*)&seraddr, sizeof(seraddr))) < 0)
sys_err("bind server address");
if((ret = listen(listenfd, BACKLOG_THREAD)) < 0)
sys_err("listen");
accept_conn(listenfd);/*处理客户端链接的接收工作*/
close(listenfd);
}
void accept_conn(const int listenfd)
{
int connfd, i = 0;
pthread_t tid;
socklen_t addrlen;
pthread_attr_t attr;//线程状态信息
struct thread_info thread[MAX_THREAD_NUM];//存储线程信息,用于函数指针传参
struct sockaddr_in cliaddr;//客户端信息
pthread_attr_init(&attr);/*初始化线程属性*/
/*设置为分离状态,也可以在线程处理函数中,用pthread_detach()进行分离状态设置,但是效率稍差*/
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
while(1){
addrlen = sizeof(cliaddr);
again:
if((connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &addrlen)) < 0){
if(errno == EINTR || errno == ECONNABORTED)
goto again;
else
sys_err("accept");
}
/*保存连接到的客户端信息到结构体中以便传参*/
thread[i].connfd = connfd;
thread[i].cliaddr = cliaddr;
/*创建线程并且传递接收到的cilent信息到线程处理函数*/
pthread_create(&tid, &attr, deal_conn, (void *)&thread[i]);
pthread_attr_destroy(&attr);//线程资源使用完后释放资源
i++;
if(i == MAX_THREAD_NUM){//超过线程总数限制,服务器主线程结束
printf("Too many connect!\n");
pthread_exit((void *)(-1));
}
}
}
void * deal_conn(void *arg)
{
struct thread_info * conn = (struct thread_info *)(arg);//无类型指针转换为自定义客户端信息类型指针
int ret;
char recvbuf[BUF_SIZE] = {};//接受缓冲区
char sendbuf[BUF_SIZE] = {};//回射缓冲区
char ipaddr[IPADDR_SIZE] = {};
int port = ntohs((conn->cliaddr).sin_port);//客户端端口
strcpy(ipaddr, inet_ntoa((conn->cliaddr).sin_addr));//客户端IP地址
int fd = open("sersock.log", O_RDWR);
lseek(fd, 0, SEEK_END);
sprintf(recvbuf, "%s:%d\n", ipaddr, port);
if( (ret = write(fd, recvbuf, strlen(recvbuf))) <0 )//将客户端登录状态写入日志文件
sys_err("write sersock.log");
close(fd);
printf("%s:%d is connect success!\n", ipaddr, port);
while(1){
bzero(recvbuf, strlen(recvbuf));
bzero(sendbuf, strlen(sendbuf));
if((ret = read(conn->connfd, recvbuf, sizeof(recvbuf))) == 0){/*读取客户端发送信息*/
printf("The client %s:%d is over!\n", ipaddr, port);
break;
}
sprintf(sendbuf, "Server recv messge:%s", recvbuf);//拼接一部分回射提示信息
if( (ret = write(conn->connfd, sendbuf, strlen(sendbuf))) < 0)/*回射*/
sys_err("write");
}
close(conn->connfd);
pthread_exit(NULL);/*不需要返回值*/
}
(2)、server.h:
/*server.h*/
#ifndef _SERVER_H_
#define _SERVER_H_
#include<multithread.h>
#include<pthread.h>
#include<fcntl.h>
#include<errno.h>
#define BACKLOG_THREAD 32
#define MAX_THREAD_NUM 256
#define IPADDR_SIZE 15
struct thread_info{
struct sockaddr_in cliaddr;
int connfd;
};
void socket_server_create(const char *, const char *);
void accept_conn(const int);
void * deal_conn(void *);
#endif
(3)、server_main.c:
/*server_main.c*/
#include<multithread.h>
#include<server.h>
int main(int argc, char **argv)
{
if(argc < 3 ){
printf("Too few parameter!\n");
exit(EXIT_FAILURE);
}
socket_server_create(argv[1], argv[2]);
return 0;
}
(4)、Makefile文件:
CPPFLAGS= -I ../ -I ./
CFLAGS= -g -Wall
#指定线程函数链接库
LDFLAGS= -lpthread
CC=gcc
src = $(wildcard *.c)
obj = $(patsubst %.c,%.o,$(src))
target = server
$(target):$(obj)
$(CC) $^ $(LDFLAGS) -o $@
%.o:%.c
$(CC) -c $< $(CFLAGS) $(CPPFLAGS) -o $@
.PHONY:clean
clean:
-rm -f server
-rm -f *.o
(1)、client.c:
/*client.c*/
#include<multithread.h>
#include<client.h>
void sys_err(const char * ptr_err)
{
perror(ptr_err);
exit(EXIT_FAILURE);
}
void socket_client_create(const char * ipaddr, const char * port)
{
struct sockaddr_in serveraddr;
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = inet_addr(ipaddr);
serveraddr.sin_port = htons(atoi(port));
int ret = connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if(ret < 0)
sys_err("connect");
deal_connect(sockfd);
close(sockfd);
}
void deal_connect(const int sockfd)
{
char sendbuf[BUF_SIZE] = {};
char recvbuf[BUF_SIZE] = {};
int ret;
while( fgets(sendbuf, sizeof(sendbuf), stdin) ){
if((ret = write(sockfd, sendbuf, strlen(sendbuf))) < 0)/*发送信息*/
sys_err("write sockfd");
if((ret = read(sockfd, recvbuf, sizeof(recvbuf))) < 0)/*接收回射信息*/
sys_err("read");
if((ret = write(STDOUT_FILENO, recvbuf, ret)) < 0)/*写到stdout*/
sys_err("write stdout");
bzero(sendbuf, strlen(sendbuf));
bzero(recvbuf, strlen(recvbuf));
}
}
(2)、client.h:
/*client.h*/
#ifndef _CLIENT_H_
#define _CLIENT_H_
#include<multithread.h>
void socket_client_create(const char *, const char *);
void deal_connect(const int);
#endif
(3)、client_main.c:
/*client_main.c*/
#include<multithread.h>
#include<client.h>
int main(int argc, char ** argv)
{
if(argc < 3){
printf("Too few parameter!\n");
exit(EXIT_FAILURE);
}
socket_client_create(argv[1], argv[2]);
return 0;
}
(4)、Makefile文件:
CPPFLAGS= -I ../ -I ./
CFLAGS= -g -Wall
LDFLAGS=
CC=gcc
src = $(wildcard *.c)
obj = $(patsubst %.c,%.o,$(src))
target = client
$(target):$(obj)
$(CC) $^ $(LDFLAGS) -o $@
%.o:%.c
$(CC) -c $< $(CFLAGS) $(CPPFLAGS) -o $@
.PHONY:clean
clean:
-rm -f client
-rm -f *.o
缺点:未处理线程的同步问题(对日志文件的加锁)。
1、测试客户端登录与新信息接收功能,以及查看日志文件的记录信息:
2、查看线程、进程间关系,以及进程的tcp连接状态:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。