【摘要】 介绍Linux下网络编程、线程编程,select机制,利用子线程响应TCP服务器的请求。
TCP服务器创建流程:
1.创建套接字
2.绑定IP地址和端口号(创建服务器)
3.设置监听的数量(限制最大可以连接的客户端数量)
4.等待客户端连接
5.实现基本通信
TCP客户端创建流程
1.创建套接字
2.连接服务器
3.实现基本通信
任务1:网络编程
练习:
1.实现TCP服务器与TCP客户端之间的基本通信,收发数据 (按照上课的思路流程看函数文档)
2.实现TCP服务器与TCP客户端之间的文件传输。(单个文件传输)
验证方式:(1) 同一台电脑演示 (2)同桌之间演示
考虑的问题:
(1)网络的传输环境,考虑应答问题
(2)数据丢包之后如何处理? 可以重发
(3)超时处理
(4)服务器与客户端之间连接断开处理。(客户端和服务器两边都需要重新连接)
文件传输可以在广告机中使用。
(扩展要求): 显示接收进度百分比,显示接收的文件名称,推荐: 定义结构体(使用数据结构)
3.(扩展)实现TCP服务器与TCP客户端之间的目录传输。
4.(扩展)实现网络聊天室(模仿QQ发送消息的效果)
一般情况下,推荐最大每次传输的字节数不超过1024字节。
任务2:线程编程
#include
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
Compile and link with -pthread.
-lpthread
理解: 一个线程就是一个while(1)。
[root@wbyq linux-share-dir]# gcc app.c
/tmp/cccOs4TK.o: In function `main:
app.c:(.text+0x69): undefined reference to `pthread_create
app.c:(.text+0x8d): undefined reference to `pthread_create
collect2: ld 返回 1
[root@wbyq linux-share-dir]# gcc app.c -lpthread
练习:
1.学习线程的基本使用
复制#include #include void *start_1(void *arg) { while(1) { printf(“123\n”); sleep(1); } } void *start_2(void *arg) { while(1) { printf(“456\n”); sleep(1); } } int main(int argc,char *argv[]) { pthread_t thread_1; pthread_t thread_2; pthread_create(&thread_1,NULL,start_1,NULL); pthread_create(&thread_2,NULL,start_2,NULL); while(1) { printf(“789\n”); sleep(1); } return 0; }2.实现一个服务器实现多个客户端的连接,实现通信。
思路: 一个客户端就是一个独立的线程。
3.扩展练习: 实现服务器同时对多个客户端进行文件发送。
(1)服务器连接上一个客户端就创建一个线程。
(2)线程的函数需要写几个? 1个
1个函数需要考虑的问题: 函数的可重入性能!
需要考虑到资源抢占! 使用信号量!
(抢答器)
设置线程分离属性:
复制#include #include char str1[]=”123456″; char str2[]=”abcdef”; void *start_1(void *arg) { printf(“arg1=%s\n”,arg); sleep(1); } void *start_2(void *arg) { printf(“arg2=%s\n”,arg); sleep(2); } int main(int argc,char *argv[]) { pthread_t thread_1; pthread_t thread_2; pthread_create(&thread_1,NULL,start_1,”线程1的参数传递测试”); pthread_create(&thread_2,NULL,start_2,”线程2的参数传递测试”); pthread_detach(thread_1); //设置线程的分离属性 pthread_detach(thread_2); //设置线程的分离属性 while(1) { } return 0; }任务3: select阻塞轮询机制
(1)服务器什么时候收到数据? read
(2)客户端什么时候收到数据? Read
(3)客户端如何判断已经与服务器断开连接?
使用select机制 ,当select函数返回值为1,read函数为0就表示断开
(4)服务器如何检测客户端已经断开连接?
采用心跳包的模式: 规定客户端每5秒钟发送一个特定的数据给服务器。
IO 多路复用是指内核一旦发现进程指定的一个或者多个 IO 条件准备读取,它就通知该进程。
IO 条件:
(1)网络编程中的读写
(2)标准输出输入中的读写
复制#include #include #include #include select:同时可以监控多个文件描述符。 int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout); 参数: int nfds :最大的文件描述符+1 fd_set *readfds :读事件发生 fd_set *writefds :写事件发生 fd_set *exceptfds:出现问题 struct timeval *timeout:轮询的时间。 填NULL表示无限阻塞。 结构体里的成员填0,表示不阻塞 结构体里的成员填>0,正常的阻塞时间 返回值: 0表示没有任何事件发生,负数表示失败。>0表示发生对应的事件。 void FD_CLR(int fd, fd_set *set); //清除指定文件描述符 int FD_ISSET(int fd, fd_set *set); //检测指定的文件描述符是否发生了事件 void FD_SET(int fd, fd_set *set); //添加指定的文件描述符到fd描述符集合(多次调用) void FD_ZERO(fd_set *set); //清除整个文件描述符集合 struct timeval { long tv_sec; /* seconds */ long tv_usec; /* microseconds */ };TCP服务器端处理:
复制#include #include /* See NOTES */ #include #include //使用大小端转换函数 #include #include #include #include #include #include //函数声明 void *start_routine_1(void *dev); void *start_routine_2(void *dev); typedef void *(*start_routine) (void *); start_routine fun[]={start_routine_1,start_routine_2}; pthread_t thread_id[2]; //存放线程的标识符 int clientfd[2]; //保存TCP客户端的网络套接字 struct sockaddr_in client_address[2]; //存放客户端的信息 socklen_t address_len[2]; //存放客户端结构体信息的长度 /*服务器端口号定义*/ #define P_host 8080 /*TCP服务器代码*/ int main(int argc,char *argv[]) { int socketfd; struct sockaddr_in server_address; //存放服务器的IP地址信息 memset(&server_address,0,sizeof(struct sockaddr_in)); //初始化内存空间 memset(client_address,0,sizeof(struct sockaddr_in)*2); //初始化内存空间 server_address.sin_family=PF_INET; //IPV4协议 server_address.sin_port=htons(P_host); //端口号赋值 server_address.sin_addr.s_addr=INADDR_ANY; //本地IP地址 /*1 .创建套接字*/ socketfd=socket(PF_INET,SOCK_STREAM,0); if(socketfd<0) { printf(“服务器网络套接字创建失败!\n”); return -1; } /*2. 绑定端口,创建服务器*/ if(bind(socketfd,(const struct sockaddr *)&server_address,sizeof(struct sockaddr))!=0) { printf(“服务器绑定端口失败!\n”); return -1; } /*3. 设监听的端口数量*/ if(listen(socketfd,10)!=0) { printf(“服务器端口监听失败!\n”); return -1; } int i; for(i=0;i<2;i++) { address_len[i]=sizeof(struct sockaddr); //计算结构体大小 20 /*4. 等待客户端连接*/ if((clientfd[i]=accept(socketfd,(struct sockaddr *)&client_address[i],&address_len[i]))<0) { printf(“等待客户端连接失败!\n”); break; } //创建线程 if(pthread_create(&thread_id[i],NULL,fun[i],NULL)!=0) { printf(“线程_%d_创建失败!\n”,i); } } while(1) { } //阻塞方式等待线程的结束 pthread_join(thread_id[0],NULL); pthread_join(thread_id[1],NULL); return 0; } //线程1 void *start_routine_1(void *dev) { while(1) { printf(“TCP客户端1连接!\n”); sleep(2); } //终止线程 pthread_exit(NULL); } //线程2 void *start_routine_2(void *dev) { while(1) { printf(“TCP客户端2连接!\n”); sleep(2); } //终止线程 pthread_exit(NULL); }免责声明:文章内容来自互联网,本站不对其真实性负责,也不承担任何法律责任,如有侵权等情况,请与本站联系删除。
转载请注明出处:Linux开发_采用线程处理网络请求-linux线程的实现 https://www.yhzz.com.cn/a/7616.html