您的位置:首页 > 理论基础 > 计算机网络

基于tcp的小型服务器(多线程多进程)

2017-06-27 16:41 302 查看
在深入理解计算机系统第11章中,讲到一个echo服务器,基于tcp字节流的小型服务器,利用socket套接字接口,完成了一个简易的服务器。

具体细节可参考网络编程(编写一个小型服务器)–csapp11章

不过这个小型服务器有一个难以接受的缺点在于:它是单进程的服务器,也就是它只能服务一个客户,我们先不去讨论这个功能强弱,我们知道在现实生活中,一个网站,一个服务器必须要能够服务多个用户,如果只能服务一个用户,那简直不可想象。

之所以只能服务一个用户,是由于设计时,只有一个执行流(也就是单进程),串行化完成任务,说白也就是当一个客户发起请求,该服务器去相应然后服务客户,客户退出才能服务其他人。

因此,想要实现并发性,服务多个用户,那需要实现多进程或者多线程服务器,即一个主线程(主进程)去执行等待客户,另一个去服务客户。

思路很简单,那么问题来了:如何选择多线程还是多进程服务器?这时候,我们就要看看它们利弊了。

线程是CPU调度的基本单位,它是一个执行流的不同分支,运行在进程的地址空间之中,共享进程的资源,它强调共享性,因此无论是创建线程,切换线程,终止线程,退出线程代价要小得多,效率相对来说要高很多,在并发问题中,多线程确实是一个提高效率的好办法。

而对于进程,由于进程是分配资源的基本单位,每个进程都具有自己独立的进程地址空间,创建,切换,终止,退出等等效率不如线程快,那么是不是就用多线程好呢?未必,进程强调独立性,就带来一个好处,稳定性,一个进程如果崩溃不会影响其他进程,而线程就不同了,多个线程运行在同一个进程的地址空间之中,因此一旦有一个线程崩溃了,其他线程也会崩溃,事实上,对于一个公司来说,服务器线程是绝对不能挂的(挂了也要立即重启),因此选择多进程也是可以考虑的。

综上考虑,我们各实现一份多进程,多线程的服务器,其实代码主逻辑有很多类似之处。具体如下:

图示如下:



多进程服务器

tcp_server

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<sys/wait.h>

void usage(const char* arg)
{
printf("correct usage : %s [ip] [port]\n", arg);
}

int start_up(const char* ip, int port)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
fprintf(stderr, "socket failure\n");
exit(-1);
}

struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(port);
inet_aton(ip, &local.sin_addr);
//local.sin_addr.s_addr = inet_addr(ip);

int opt = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
{
fprintf(stderr, "setsockopt failure\n");
exit(-1);
}

int flag = bind(sock, (struct sockaddr*)&local, sizeof(local));
if (flag == -1)
{
fprintf(stderr, "bind failure\n");
exit(-1);
}
flag = listen(sock, 6);
if (flag == -1)
{
fprintf(stderr, "listen failure\n");
exit(-1);
}
return sock;
}

int main(int argc, char* argv[])
{
// ./tcp_server ip port
if (argc != 3)
{
usage(argv[0]);
exit(1);
}

int listen_sock = start_up(argv[1], atoi(argv[2]));

while (1)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
// int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int new_fd = accept(listen_sock, (struct sockaddr*)&client, &len);
if (new_fd == -1)
{
//fprintf(stderr, "accept failure\n");
continue;
}

pid_t pid = fork();
if (pid < 0)
{
fprintf(stderr, "fork failure\n");
close(new_fd);
}
else if (pid == 0)//child
{
close(listen_sock);
if (fork() > 0)
{
exit(0);
}
else
{
printf("%s:%d connects the server\n", inet_ntoa(client.sin_addr), client.sin_port);
while (1)
{
char buf[1024];
ssize_t read_size = read(new_fd, buf, sizeof(buf)-1);
if (read_size > 0)
{
buf[read_size] = '\0';
printf("client : %s\n", buf);
write(new_fd, buf, strlen(buf));
}
else
{
printf("client quits...\n");
break;
}
}
close(new_fd);
}
}
else //father
{
close(new_fd);
waitpid(-1, NULL, WNOHANG);
}
}

return 0;
}


tcp_client

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>

void usage(const char* arg)
{
printf("correct usage: %s [remote_ip] [remote_port]\n", arg);
}

int open_clientfd(const char* ip, int port)
{
int clientfd = socket(AF_INET, SOCK_STREAM, 0);
if (clientfd == -1)
{
fprintf(stderr, "socket failure\n");
exit(-1);
}

struct sockaddr_in remote;
remote.sin_family = AF_INET;
remote.sin_port = htons(port);
inet_aton(ip, &remote.sin_addr);

int flag = connect(clientfd, (struct sockaddr*)&remote, sizeof(remote));
if (flag == -1)
{
fprintf(stderr, "connect failure\n");
exit(-1);
}
return clientfd;

}

int main(int argc, char* argv[])
{
if (argc != 3)
{
usage(argv[0]);
exit(1);
}

int sock_fd = open_clientfd(argv[1], atoi(argv[2]));

while (1)
{
char buf[1024];
printf("please enter # ");
fflush(stdout);
ssize_t s = read(0, buf, sizeof(buf)-1);
if (s > 0)
{
buf[s-1] = '\0';
write(sock_fd, buf, strlen(buf));
s = read(sock_fd, buf, strlen(buf));
if (s > 0)
{
printf("server echo # %s\n", buf);
}

//printf("server # %s\n", buf);
}
}

return 0;
}


makefile

.PHONY:all
all:tcp_server tcp_client
tcp_server:tcp_server.c
gcc -o tcp_server tcp_server.c -static
tcp_client:tcp_client.c
gcc -o tcp_client tcp_client.c
.PHONY:clean
clean:
rm -f tcp_client tcp_server


多线程服务器

tcp_server

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>

void usage(const char* arg)
{
printf("correct usage : %s [ip] [port]\n", arg);
}

int start_up(const char* ip, int port)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
fprintf(stderr, "socket failure\n");
exit(-1);
}

// int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
// sockaddr* 相当于 void*
//
// 利用grep -ER 'struct sockaddr_in {' /usr/include 查找
//
// struct sockaddr_in {
//    __kernel_sa_family_t  sin_family; /* Address family       */
//    __be16        sin_port;   /* Port number          */
//    struct in_addr    sin_addr;   /* Internet address     */
//
//    /* Pad to size of `struct sockaddr'. */
//    unsigned char     __pad[__SOCK_SIZE__ - sizeof(short int) -
//              sizeof(unsigned short int) - sizeof(struct in_addr)];
// };
//
// struct in_addr {
//     __be32  s_addr;
// };

int opt = 1;
if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
{
fprintf(stderr, "setsockopt failure\n");
exit(-1);
}

struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(port);
//local.sin_addr.s_addr = inet_addr(ip);
inet_aton(ip, &local.sin_addr);

int flag = bind(sock, (struct sockaddr*)&local, sizeof(local));
if (flag == -1)
{
fprintf(stderr, "bind failure\n");
exit(-1);
}
flag = listen(sock, 6);
if (flag == -1)
{
fprintf(stderr, "listen failure\n");
exit(-1);
}

return sock;
}

void* thread_run(void* arg)
{
pthread_detach(pthread_self());
int new_fd = (int)arg;
char buf[1024];
memset(buf, 0, sizeof(buf));
while (1)
{
ssize_t s = read(new_fd, buf, sizeof(buf)-1);
if (s > 0)
{
buf[s] = '\0';
printf("client : %s\n", buf);
write(new_fd, buf, strlen(buf));
}
else
{
printf("client quits...\n");
break;
}
}
}

int main(int argc, char* argv[])
{
// ./tcp_server ip port
if (argc != 3)
{
usage(argv[0]);
exit(-1);
}

int listen_sock = start_up(argv[1], atoi(argv[2]));

while (1)
{
struct sockaddr_in client;
socklen_t len = sizeof(client);
// int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int new_fd = accept(listen_sock, (struct sockaddr*)&client, &len);
if (new_fd == -1)
{
//fprintf(stderr, "accept failure\n");
continue;
}

pthread_t tid;
pthread_create(&tid, NULL, thread_run, (void*)new_fd);
//pthread_detach(tid);
}
return 0;
}


tcp_client

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<unistd.h>

void usage(const char* arg)
{
printf("correct usage: %s [remote_ip] [remote_port]\n", arg);
}

int main(int argc, char* argv[])
{
if (argc != 3)
{
usage(argv[0]);
}

int sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
fprintf(stderr, "socket failure\n");
exit(-1);
}

// int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
//

// struct sockaddr_in {
//    __kernel_sa_family_t  sin_family; /* Address family       */
//    __be16        sin_port;   /* Port number          */
//    struct in_addr    sin_addr;   /* Internet address     */
//
//    /* Pad to size of `struct sockaddr'. */
//    unsigned char     __pad[__SOCK_SIZE__ - sizeof(short int) -
//              sizeof(unsigned short int) - sizeof(struct in_addr)];
// };
//
// struct in_addr {
//     __be32  s_addr;
// };
struct sockaddr_in remote;
remote.sin_family = AF_INET;
remote.sin_port = htons(atoi(argv[2]));
//remote.sin_addr.s_addr = inet_addr(argv[1]);
inet_aton(argv[1], &remote.sin_addr);

int flag = connect(sock, (struct sockaddr*)&remote, sizeof(remote));
if (flag == -1)
{
fprintf(stderr, "connect failure\n");
exit(-1);
}

while (1)
{
char buf[1024];
printf("please enter # ");
fflush(stdout);
ssize_t s = read(0, buf, sizeof(buf)-1);
if (s > 0)
{
buf[s-1] = '\0';
write(sock, buf, strlen(buf));

s = read(sock, buf, strlen(buf));
if (s > 0)
{
printf("server echo # %s\n", buf);
}
}
}

return 0;
}


makefile

.PHONY:all
all:tcp_server tcp_client
tcp_server:tcp_server.c
gcc -o $@ $^ -static -lpthread
tcp_client:tcp_client.c
gcc -o $@ $^
.PHONY:clean
clean:
rm -f tcp_client tcp_server
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: