server(多线程)
#include
#include
#include
#include
#include
#include
#include
#include
int client_fd=0;
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;
int thread;
}linkState_t;
linkState_t thread[2]={0};
typedef struct
{
int index;
int fd;
}peerInfo_t;
peerInfo_t peerInfo={0};
pthread_mutex_t lock_server=PTHREAD_MUTEX_INITIALIZER;
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);
}
int main(int argc, char *argv[])
{
struct sockaddr_in listen_addr;
pthread_t sender;
int err_thread=0;
int err_socket=0;
pthread_attr_t attr;
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:
thread_index=-1;
int _thread_num=sizeof(thread)/sizeof(linkState_t);
for(si=0;si<sizeof(thread)/sizeof(linkState_t);si++){
pthread_mutex_lock(&lock_server);
int _isUsed=thread[si].isUsed;
pthread_mutex_unlock(&lock_server);
if(_isUsed == 0){
thread_index=si;
break;
}
}
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");
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
#include
#include
#include
#include
#include
#include
#include
int client_fd=0;
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.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;
}
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[])
{
pthread_t sender;
int err_thread=0;
pthread_attr_t attr;
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);
}
printf("main thread exit.\n");
return 0;
}
- 思路:创建一个发送线程,负责数据发送,然后在主线程中获取用户输入,改变发送内容。
- 实现:main函数创建一个发送线程,负责定时发送数据给服务器,发送前会比较发送缓存和全局变量内容是否一致,不一致则通过互斥锁更新发送缓存。用户在主线程中获取用户输入,通过互斥锁改变全局发送内容变量。
疑惑
- shutdown和close是否同时使用?
从官网可以看出,shutdown函数应该只是关闭了socket的接收或发送功能,最后还是要通过close函数销毁socket描述符。