多线程并发服务器编程
2016-04-14 10:52
330 查看
多线程并发服务器编程
一、实验目的理解线程的创建和终止方法;
学会编写基本的多线程并发服务器程序和客户程序;
理解多线程与多进程的区别。
二、实验平台
ubuntu-8.04操作系统
三、实验内容
编写多线程并发服务器程序和客户程序,具体功能如下:
1、服务器等待接收客户的连接请求,一旦连接成功则显示客户地址,接着接收客户端的名称并显示;然后接收来自该客户的字符串,每当收到一个字符串时,显示该字符串,并将字符串按照恺撒密码的加密方式(K=3)进行加密,再将加密后的字符发回客户端;之后,继续等待接收该客户的信息,直到客户关闭连接。要求服务器具有同时处理多个客户请求的能力。
2、客户首先与相应的服务器建立连接;接着接收用户输入的客户端名称,并将其发送给服务器;然后继续接收用户输入的字符串,再将字符串发送给服务器,同时接收服务器发回的加密后的字符串并显示。之后,继续等待用户输入字符串,直到用户输入Ctrl+D,客户关闭连接并退出。
四、实验原理
多进程方式使用fork生成子进程存在一些问题。首先,fork占用大量的资源,内存映像要从父进程拷贝到子进程,所有描述符要在子进程中复制;其次,fork子进程后,需要用进程间通信在父进程和子进程间传递信息,从子进程返回信息给父进程需要做较多的工作。多线程有助于解决以上两个问题。
线程是进程内的独立执行实体和调度单元,又称为“轻量级”进程(lightwightprocess);创建线程比进程快10~100倍。一个进程内的所有线程共享相同的内存空间、全局变量等信息(这种机制又带来了同步问题)。
1、pthread_create()函数
pthread_create()函数用于创建新线程。当一个程序开始运行时,系统产生一个称为初始线程或主线程的单个线程,额外的线程需要由pthread_create()函数创建。
------------------------------------------------------------------- #include<pthread.h> intpthread_create(pthread_t *tid, const pthread_attr_t *attr, void*(*func)(void *), void *arg); 返回:成功时为0;出错时非0 ------------------------------------------------------------------- |
由于线程的执行函数的参数和返回值类型均为void*,因此可传递和返回指向任何类型的指针。
常见的返回错误值:
EAGAIN:超过了系统线程数目的限制。
ENOMEN:没有足够的内存产生新的线程。
EINVAL:无效的属性attr值。
2、pthread_join()函数
pthread_join()函数用于等待一个线程终止。
------------------------------------------------------------------- #inlcude<pthread.h> intpthread_join(pthread_t tid, void **status); 返回:成功时为0;出错时返回正的错误码。 ------------------------------------------------------------------- |
3、pthread_detach()函数
pthread_detach()函数将指定的线程变成脱离的。
------------------------------------------------------------------- #include<pthread.h> intpthread_detach(pthread_t tid) 返回:成功时为0;出错时返回错误码。 ------------------------------------------------------------------- |
参数tid指定要设置为脱离的线程ID。
4、pthread_self()函数
每一个线程都有一个ID,pthread_self()函数返回自己的线程ID。
------------------------------------------------------------------- #include<pthread.h> pthread_tpthread_self(void); 返回:调用线程的线程id ------------------------------------------------------------------- |
pthread_detach(pthread_self());
5、pthread_exit()函数
pthread_exit()函数用于终止当前线程,并返回状态值。如果当前线程是可汇合的,将保留线程id和退出状态供pthread_join()函数调用。
------------------------------------------------------------------- #include<pthread.h> void pthread_exit(void*status); 无返回值; ------------------------------------------------------------------- |
还有其他两种方法可使线程终止:
(1)启动线程的函数pthread_create()的第3个参数返回。其返回值便是线程的终止状态;
(2)如果进程的main()函数返回,或者当前进程中,任一线程调用了exit()函数,将终止该进程中所有线程。
五、实验步骤
1、登陆进入ubuntu操作系统,新建一个文件,命名为mthread_server.c。
2、在mthread_server.c中编写相应代码并保存,作为服务器端程序。客户端程序代码同mproc_client.c一致。blog:http://blog.csdn.net/yueguanghaidao/article/details/7060350
3、打开一个“终端”,执行命令进入mthread_server.c和mproc_client.c所在目录。
4、执行命令gcc–omthread_servermthread_server.c-lpthread生成可执行文件mthread_server。
5、执行命令./mthread_server,运行服务器端。
6、打开第2个“终端”,执行命令进入mthread_server.c和mproc_client.c所在目录。
7、执行命令./mproc_client127.0.0.1,模拟客户1。
8、打开第3个“终端”,执行命令进入mthread_server.c和mproc_client.c所在目录。
9、执行命令./mproc_client127.0.0.1,模拟客户2。
10、程序运行结果如下:
服务器端:
客户1:
客户2:
11、在客户端按下Ctrl+D,关闭客户连接。
12、认真分析源代码,体会多线程并发服务器程序的编写。
六、参考程序(mthread_server.c)
[cpp] view
plain copy
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#define PORT 1234
#define BACKLOG 5
#define MAXDATASIZE 1000
void process_cli(int connfd, struct sockaddr_in client);
void *function(void* arg);
struct ARG {
int connfd;
struct sockaddr_in client;
};
main()
{
int listenfd,connfd;
pthread_t tid;
struct ARG *arg;
struct sockaddr_in server;
struct sockaddr_in client;
socklen_t len;
if ((listenfd =socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("Creatingsocket failed.");
exit(1);
}
int opt =SO_REUSEADDR;
setsockopt(listenfd,SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
bzero(&server,sizeof(server));
server.sin_family=AF_INET;
server.sin_port=htons(PORT);
server.sin_addr.s_addr= htonl (INADDR_ANY);
if (bind(listenfd,(struct sockaddr *)&server, sizeof(server)) == -1) {
perror("Bind()error.");
exit(1);
}
if(listen(listenfd,BACKLOG)== -1){
perror("listen()error\n");
exit(1);
}
len=sizeof(client);
while(1)
{
if ((connfd =accept(listenfd,(struct sockaddr *)&client,&len))==-1) {
perror("accept() error\n");
exit(1);
}
arg = (struct ARG *)malloc(sizeof(struct ARG));
arg->connfd =connfd;
memcpy((void*)&arg->client, &client, sizeof(client));
if(pthread_create(&tid, NULL, function, (void*)arg)) {
perror("Pthread_create() error");
exit(1);
}
}
close(listenfd);
}
void process_cli(int connfd, struct sockaddr_in client)
{
int num;
char recvbuf[MAXDATASIZE], sendbuf[MAXDATASIZE], cli_name[MAXDATASIZE];
printf("Yougot a connection from %s. \n ",inet_ntoa(client.sin_addr) );
num = recv(connfd,cli_name, MAXDATASIZE,0);
if (num == 0) {
close(connfd);
printf("Clientdisconnected.\n");
return;
}
cli_name[num - 1] ='\0';
printf("Client'sname is %s.\n",cli_name);
while (num =recv(connfd, recvbuf, MAXDATASIZE,0)) {
recvbuf[num] ='\0';
printf("Receivedclient( %s ) message: %s",cli_name, recvbuf);
int i;
for (i = 0; i <num - 1; i++) {
if((recvbuf[i]>='a'&&recvbuf[i]<='z')||(recvbuf[i]>='A'&&recvbuf[i]<='Z'))
{
recvbuf[i]=recvbuf[i]+ 3;
if((recvbuf[i]>'Z'&&recvbuf[i]<='Z'+3)||(recvbuf[i]>'z'))
recvbuf[i]=recvbuf[i]- 26;
}
sendbuf[i] =recvbuf[i];
}
sendbuf[num -1] = '\0';
send(connfd,sendbuf,strlen(sendbuf),0);
}
close(connfd);
}
void *function(void* arg)
{
struct ARG *info;
info = (struct ARG*)arg;
process_cli(info->connfd,info->client);
free (arg);
pthread_exit(NULL);
}
相关文章推荐
- KMP算法C代码实现
- 一、Android Studio入门——Eclipse快捷键配置
- MyBatis-Spring-SqlSessionFactoryBean
- 解决java内存溢出最佳配置
- 【SSM-SpringMVC框架】非注解的处理器适配器和映射器
- 仿百度搜索代码
- php 本地配置根目录
- c++ 计时
- JAVA抽象类与接口
- Spring单例与线程安全小结
- 转spring aop实现业务层mysql 读写分离
- Atitit. 注册表操作查询 修改 api与工具总结 java c# php js python 病毒木马的原理
- Atitit. 注册表操作查询 修改 api与工具总结 java c# php js python 病毒木马的原理
- Atitit. 注册表操作查询 修改 api与工具总结 java c# php js python 病毒木马的原理
- 01背包代码
- Github+Jekyll —— 创建个人免费博客(四)jekyll第一个页面
- numpy学习笔记一:numpy的基本用法
- QTableWidgetItem 按数字排序
- Spring AOP
- Eclipse 调试时,出现错误闪退,但是控制台没有打印错误信息