linux socket编程记录

server(多线程)

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>

//client socket
int client_fd=0;
//server socket
int server_fd=0;
//
struct sockaddr_in server_addr;
//
pthread_mutex_t lock1=PTHREAD_MUTEX_INITIALIZER;
//
static char instr[100]={0};

//定义客户端连接维护表
typedef struct
{
	int isUsed;//if already used
	int thread;//thread-fd
}linkState_t;
//维护<=2个客户端
linkState_t thread[2]={0};
//已连接socket维护
typedef struct
{
	int index;//thread-index
	int fd;//linked socket-fd
}peerInfo_t;
peerInfo_t peerInfo={0};
//互斥锁1
pthread_mutex_t lock_server=PTHREAD_MUTEX_INITIALIZER;

/* 服务器连接处理线程
 * param:in-
 * ret:
 * */
void *server_handler(void *in)
{
	peerInfo_t _peer={0};
	int _fd=0;
	char recv_buf[1024]={0};
	int _rlen=0;

	if(in == NULL){
		pthread_exit(NULL);
	}
	_peer=*(peerInfo_t*)in;
	while(1){
		if((_rlen=recv(_peer.fd, recv_buf, sizeof(recv_buf), 0)) > 0){
			if(!strncmp(recv_buf, "exit", strlen(recv_buf))){
				pthread_mutex_lock(&lock_server);
				thread[_peer.index].isUsed=0;
				pthread_mutex_unlock(&lock_server);
				break;
			}else{
				printf("%d received:%s\n", _peer.fd, recv_buf);
			}
		}else{
			printf("recv failed!%s\n", strerror(errno));
			pthread_mutex_lock(&lock_server);
			thread[_peer.index].isUsed=0;
			pthread_mutex_unlock(&lock_server);
			break;
		}
	}
	close(_fd);
	pthread_exit(NULL);
}
/*
 * main thread
 * */
int main(int argc, char *argv[])
{
	struct sockaddr_in listen_addr;

	//create thread
	pthread_t sender;
	int err_thread=0;
	int err_socket=0;
	pthread_attr_t attr;

	//忽略SIGPIPE信号,否则向已断开的socket读写数据时程序会直接退出
	signal(SIGPIPE, SIG_IGN);

#if 1
	memset(&listen_addr, 0, sizeof(listen_addr));
	listen_addr.sin_family=AF_INET;
	listen_addr.sin_port=htons(12345);
	listen_addr.sin_addr.s_addr=htonl(INADDR_ANY);

	if((server_fd=socket(AF_INET, SOCK_STREAM, 0)) < 0){
		printf("socket create failed!%s\n", strerror(errno));
		exit(0);
	}
	printf("socket created...\n");
	if(bind(server_fd, &listen_addr, sizeof(listen_addr)) < 0){
		printf("socket bind failed!%s\n", strerror(errno));
		exit(0);
	}
	printf("bind successed...\n");
	if(listen(server_fd, 10) < 0){
		printf("socket listen failed!%s\n", strerror(errno));
		exit(0);
	}
	printf("listen successed...\n");
	//
	int si=0;
	pthread_attr_t _attr_thread;
	struct sockaddr_in peer_addr;
	int peer_fd=0;
	int thread_index=0;
	while(1){
_search:
		//find valid thread-fd
		thread_index=-1;
		int _thread_num=sizeof(thread)/sizeof(linkState_t);
//		printf("thread_num:%d\n", _thread_num);
		for(si=0;si<sizeof(thread)/sizeof(linkState_t);si++){
//			printf("thread[%d].isUsed:%d\n", si, thread[si].isUsed);
			pthread_mutex_lock(&lock_server);
			int _isUsed=thread[si].isUsed;
			pthread_mutex_unlock(&lock_server);
			if(_isUsed == 0){
				thread_index=si;
				break;
			}
		}
		//have no thread-index valid
		if(si >= _thread_num){
			goto _search;
		}
		while(1);
		//
		printf("ready to accept a new client!\n");
		if((peer_fd = accept(server_fd, (struct sockaddr*)NULL, NULL)) < 0){
			printf("accept failed!%s\n", strerror(errno));
			continue;
		}
		printf("accept a new client!\n");
		//create a new thread to handle message
		peerInfo.index=thread_index;
		peerInfo.fd=peer_fd;
		pthread_attr_init(&_attr_thread);
		pthread_attr_setdetachstate(&_attr_thread, PTHREAD_CREATE_DETACHED);
		if(pthread_create(&thread[thread_index].thread, NULL, (void*)server_handler, (void*)&peerInfo) != 0){
			printf("server thread create failed!\n");
			continue;
		}
		pthread_mutex_lock(&lock_server);
		thread[thread_index].isUsed=1;
		pthread_mutex_unlock(&lock_server);
	}

	pthread_attr_destroy(&_attr_thread);
	close(server_fd);

	printf("main thread exit...\n");

	return 0;
#endif

return 0;
}
  • 思路:创建socket、bind、listen,之后进入死循环执行accept。
  • 方法:系统维护一个线程状态表,表的容量即为服务器与客户端可同时建立的连接数量。执行accept函数前先遍历线程状态表,找出空闲(isUsed==0)的元素,并返回其下标值,如果遍历结束后未发现可用元素,则不往下执行accept函数,继续重复遍历动作。accept执行成功后,将返回的已连接socket放进peerInfo_t结构体并将线程状态表中对应位置的isUsed标志置位,创建线程,并将peerInfo_t结构体信息传入,以便线程结束时能正确清空线程状态表中对应位置的isUsed标志!

疑惑

  • 经测试,成功执行完listen后不执行accept函数,在linux下通过TCP测试工具的客户端也能够成功连接到服务器,发送数据也提示成功,但是服务器不会收到数据。

client(多线程)

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <error.h>
#include <string.h>
#include <pthread.h>
#include <signal.h>

//client socket
int client_fd=0;
//server socket
int server_fd=0;
//
struct sockaddr_in server_addr;
//
pthread_mutex_t lock1=PTHREAD_MUTEX_INITIALIZER;
//
static char instr[100]={0};
/* 连接到服务器
 * 
 * */
int _connect2server(void)
{
	int err=0;

	client_fd=socket(AF_INET, SOCK_STREAM, 0);
	if(client_fd < 0){
		printf("socket create failed!\n");
		exit(0);
	}

	memset(&server_addr, 0, sizeof(struct sockaddr_in));
	server_addr.sin_family=AF_INET;
	server_addr.sin_port=htons(12345);
//	inet_pton(AF_INET, "192.168.99.103", &server_addr.sin_addr.s_addr);
	inet_pton(AF_INET, "192.168.0.149", &server_addr.sin_addr.s_addr);

	if((err = connect(client_fd, &server_addr, sizeof(server_addr))) < 0){
		printf("connect failed!error:%s\n", strerror(err));
		return -1;
	}
	return 0;
}
/* 
 *  data-send thread
 * */
void *send_thread(void *inptr)
{
	for(;;){
		int err=0;
		if(_connect2server() != 0){
			sleep(1);
			continue;
		}
		char print_str[100]={0};
		sprintf(print_str, "%s", "default");
		for(;;){
			pthread_mutex_lock(&lock1);
			if(strncmp(print_str, instr, sizeof(print_str))){
				memset(print_str, 0, sizeof(print_str));
				strncpy(print_str, instr, strlen(instr));
			}
			pthread_mutex_unlock(&lock1);

			if((err=send(client_fd, print_str, strlen(print_str), 0)) < 0){
				shutdown(client_fd, SHUT_RDWR);
				close(client_fd);
				printf("send failed!error:%s\n", strerror(err));
				//
				while(_connect2server() != 0){
					sleep(1);
				}
			}
			sleep(1);
		}
	}
}
/*
 *
 * */
int main(int argc, char *argv[])
{
	//create thread
	pthread_t sender;
	int err_thread=0;
	pthread_attr_t attr;
	
	//忽略SIGPIPE信号,否则向已断开的socket读写数据时程序会直接退出
	signal(SIGPIPE, SIG_IGN);

	//创建数据发送线程
	pthread_attr_init(&attr);
	pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
	if((err_thread=pthread_create(&sender, &attr, send_thread, NULL)) != 0){
		printf("thread create failed!%s\n", strerror(err_thread));
		pthread_attr_destroy(&attr);
		exit(0);
	}
	pthread_attr_destroy(&attr);

	char _cmd[100]={0};
	//等待用户输入
	while(scanf("%s", _cmd)!=EOF){
		if(!strcmp(_cmd, "quit")){
			printf("quit...\n");
			break;
		}
		pthread_mutex_lock(&lock1);
		//改变发送内容
		sprintf(instr, "%s", _cmd);
		pthread_mutex_unlock(&lock1);
	}

//	pthread_join(sender, NULL);

	printf("main thread exit.\n");

return 0;
}
  • 思路:创建一个发送线程,负责数据发送,然后在主线程中获取用户输入,改变发送内容。
  • 实现:main函数创建一个发送线程,负责定时发送数据给服务器,发送前会比较发送缓存和全局变量内容是否一致,不一致则通过互斥锁更新发送缓存。用户在主线程中获取用户输入,通过互斥锁改变全局发送内容变量。

疑惑

  • shutdown和close是否同时使用?
    官网可以看出,shutdown函数应该只是关闭了socket的接收或发送功能,最后还是要通过close函数销毁socket描述符。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值