TCP 并发服务器本来准备写在网络骇客入门之 TCP 编程后面的, 但是因为代码有点长, 所以就单独写了一篇
注意:
因为浏览器发送的数据比较多, 所以作为本服务器的接受缓冲区 recv_buf 要大点, 至少 512 字节, 建议 1024 字节
如果接收不全的话, 无论如何都不能将网页传给浏览器, 这个 bug 卡了我一晚上, 所以记得特别清楚.
创建线程时传给线程的参数注意写连接套接字的值, 先转换为 (void*) 类型,
pthread_create(&pth,NULL,msg_echo,(void *)connfd);
再在线程里转回 (int) 型
int connfd = (int)arg;
不能写地址, 防止如果同时有多个请求时 connfd 的值变得太快, 在子线程取地址取出来之前值就变了
1. 头文件
- #include <stdio.h>
- #include <unistd.h>
- #include <string.h>
- #include <stdlib.h>
- #include <arpa/inet.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <pthread.h>
- #include <fcntl.h>
- // 子线程处理浏览器的网页请求
- void *msg_echo(void *arg)
- {
- int connfd = (int)arg;
- int ret_read=0;
- int ret_recv=0;
- char recv_buf[1024]="";
- char read_buf[1024]="";
- char send_buf[1024]="";
- int fd;
- char filename[50] = "html/";
- // 请求成功返回
- char head[]="HTTP/1.1 200 OK\r\n" \
- "Content-Type: text/html\r\n" \
- "\r\n";
- // 请求失败返回
- char err[]= "HTTP/1.1 404 Not Found\r\n" \
- "Content-Type: text/html\r\n" \
- "\r\n" \
- "<HTML><BODY>File not found</BODY></HTML>";
- printf("connfd=%d\n",connfd);
- // 接收请求数据
- ret_recv = recv(connfd,recv_buf,sizeof(recv_buf),0);
- printf("ret_recv:%d\n",ret_recv);
- // 读取网页文件名
- sscanf(recv_buf+4, "%[^ ]", (filename + 5));
- printf("filename:%s\n",filename);
- // 打开网页文件
- fd = open(filename, O_RDONLY);
- if(fd <0)
- {
- perror("open");
- send(connfd, err, strlen(err), 0);
- close(connfd);
- return NULL;
- }
- // 将网页文件发给浏览器
- send(connfd,head,strlen(head),0);
- while((ret_read = read(fd,read_buf,sizeof(read_buf)))>0)
- {
- // printf("%s\n",read_buf);
- send(connfd,read_buf,ret_read,0);
- }
- close(connfd);
- close(fd);
- }
2.main 函数
- int main(int argc, char *argv[])
- {
- unsigned int port=8000;// 设置端口
- if(argc> 1)// 可指定端口
- {
- port = atoi(argv[1]);
- }
- int sockfd;
- struct sockaddr_in my_addr;
- // 结构体
- memset(&my_addr,0,sizeof(my_addr));
- my_addr.sin_family = AF_INET;
- my_addr.sin_port = htons(port);
- my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
- // 套接字
- sockfd = socket(AF_INET, SOCK_STREAM, 0);
- if(sockfd < 0)
- {
- perror("socket");
- exit(-1);
- }
- // 绑定端口
- int err = bind(sockfd,(struct sockaddr*)&my_addr,sizeof(my_addr));
- if(err != 0)
- {
- perror("bind");
- close(sockfd);
- exit(-1);
- }
- // 监听端口
- err = listen(sockfd,10);
- if(err != 0)
- {
- perror("listen");
- close(sockfd);
- exit(-1);
- }
- printf("listen at %d\n",port);
- // 多线程处理连接请求
- while(1)
- {
- int connfd;// 连接套接字
- struct sockaddr_in client_addr;
- char cli_ip[INET_ADDRSTRLEN]="";
- socklen_t cliaddr_len = sizeof(client_addr);
- // 接受请求
- connfd = accept(sockfd,(struct sockaddr *)&client_addr,&cliaddr_len);
- if(connfd < 0)
- {
- perror("accept");
- }
- // 输出连接者信息
- inet_ntop(AF_INET,&client_addr.sin_addr,cli_ip,INET_ADDRSTRLEN);
- printf("accepted--ip:%s port:%d\n",cli_ip,ntohs(client_addr.sin_port));
- // 创建线程
- pthread_t pth;
- pthread_create(&pth,NULL,msg_echo,(void *)connfd);
- pthread_detach(pth);
- }
- close(sockfd);
- return 0;
- }
来源: http://www.bubuko.com/infodetail-2554149.html