TCP併發伺服器的程式設計實現
TCP併發伺服器的程式設計實現
1. 基於TCP的伺服器程式設計模型
- 建立通訊端點(套接字),返回該端點的檔案描述符 sfd socket(2)
2 )將sfd和本地的ip地址和埠號繫結 bind(2);
3 )將sfd設定為被動連線狀態,監聽客戶端的到來,如果有客戶段的到來,將其放入到未決連線佇列中.listen(2)
while{
4 從未決連線中取出一個處理,返回一個新的連線描述符 如果未決連線為空,阻塞等待 cfd = accept(2)
5 從連線描述符中讀取客戶段的請求資料到buf中 read(2)
6 處理buf中的資料
7 將處理的結果寫給客戶端 write(2)
8 關閉本次連線close(cfd)
}
2. TCP併發伺服器分三個部分實現:
1 網路程式設計部分:
網路程式設計部分封裝為一個原始檔和標頭檔案,分別是t_net.h t_net.c
t_net.h部分原始碼
#ifndef __T_NET_H__
#define __T_NET_H__
#include <sys/socket.h>
#include <sys/types.h>
typedef struct sockaddr SA;
typedef struct sockaddr_in SA4;
//socket bind
//這裡將套接字的建立過程和繫結過程封裝為一個函式
int bind_sock(int domain,int type,u_int16_t port);
//呼叫這個函式可以從未連線佇列中取出一個進行處理.不會顯示
//客戶端的ip地址
int n_acpt(int fd);
//對比上一個函式,可以顯示客戶端的ip地址
int h_acpt(int fd);
//將套接字的建立 繫結 監聽封裝到一個函式中
int s_listen(int domain,int type,u_int16_t port,int b);
#endif //__T_NET_H__
編寫完標頭檔案,可以把標頭檔案移動到/usr/local/include目錄下.這樣include可以使用<>
t_net.c原始碼
#include <t_net.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <errno.h>
int bind_sock(int domain,int type,u_int16_t port){
//建立一個socket裝置,返回該裝置的檔案描述符
SA4 serv;
int sfd = socket(domain,type,0);
if(sfd == -1){
printf("%s\n",strerror(errno));
return -1;
}
//將sfd繫結繫結本地地址
serv.sin_family = AF_INET;
//主機位元組序到網路位元組序
serv.sin_port = htons(port);
serv.sin_addr.s_addr = htonl(INADDR_ANY);
int b = bind(sfd,(SA*)&serv,sizeof(serv));
if(b == -1){
printf("%s\n",strerror(errno));
return -1;
}
return sfd;
//不顯示顯示客戶端的IP
int n_acpt(int fd){
int cfd = accept(fd,NULL,NULL);
if(cfd == -1){
printf("%s\n",strerror(errno));
}
return cfd;
}
//顯示客戶端的IP地址
int h_acpt(int fd){
SA4 cli;
char ip[32];
socklen_t len = sizeof(cli);
int cfd = accept(fd,(SA*)&cli,&len);
if(cfd == -1){
printf("%s\n",strerror(errno));
return -1;
}
printf("%s\n",inet_ntop(AF_INET,&cli.sin_addr,ip,32));
return cfd;
}
編寫完原始檔,也可把原始檔封裝成動態庫
具體步驟如下:
1)生成與檔案無關的可執行檔案
gcc - c -fPIC t_net.c
2)打包動態庫
gcc -shared -o libt_net.so t_net.o
3)將動態庫移動到/lib目錄下.
在編譯的時候加上-lt_net即可
2 伺服器的資料處理
#include <unistd.h>
#include <ctype.h>
#include <string.h>
int t_main(int cfd){
//讀取客戶端的請求訊息,read阻塞
char buf[128];
while(1){
int r = read(cfd,buf,128);
for(int i = 0;i < r ;i++)
buf[i] = toupper(buf[i]);
write(cfd,buf,r);
if(strcmp(buf,"BYEBYE") == 0) break;
}
return 0;
}
伺服器的資料處理部分很簡單,就是使用read函式讀取客戶段中的資料.並將服務端中的資料寫回給客戶端
3) 併發部分的實現
當一個客戶端與伺服器建立連線之後,在斷開連線之前.伺服器無法從未決連線佇列中與其他的客戶端建立連線.因此,需要一定手段來實現併發.
有三種方式:多執行緒 多程式 多路複用 epoll
這裡使用的是多程式
父程式的任務
① 負責從未決連線佇列中取出一個進行連線處理,返回一個連線描述符.
②建立子程式,子程式繼承父程式的檔案描述符.
③ 關閉連線描述符.
④負責回收子程式的資源.
子程式負責的任務
① 關閉裝置描述符
② 使用連線描述符處理客戶的業務
③處理完畢,關閉連線描述符
④ exit(0)
相關程式碼
#include <t_net.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <errno.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <stdlib.h>
#include <signal.h>
//子程式終止時給父程式傳送SIGCHLD訊號
void handle(int n){
//回收子程式的資源
wait(NULL);
return ;
}
extern int t_main(int cfd);
int main(void){
//建立一個socket裝置,返回該裝置的檔案描述符
signal(SIGCHLD,handle);
char buffer[128];
SA4 cli;
int s_fd = s_listen(AF_INET,SOCK_STREAM,8000,5);
if(s_fd == -1){
return -1;
}
while(1){
int cfd = h_acpt(s_fd);
if(cfd == -1) return -1;
pid_t pid = fork();
//父子程式是非同步的
if(pid == -1){
printf("%s\n",strerror(errno));
return -1;
}
if(pid == 0){
close(s_fd);
t_main(cfd);
close(cfd);
exit(0);
}
else{
close(cfd);
//阻塞等待
//waitpid(-1,NULL,WNOHANG);
}
}
close(s_fd);
return 0;
}
這裡使用一個訊號函式,在子程式終止的時候會發射SIGCHILD訊號,通知父程式回收子程式的資源.
3 TCP客戶端的程式設計模型
1)建立socket裝置socket(2);
2)繫結IP地址和埠號bind(2);
3)和伺服器建立連線
4)迴圈處理資料 write(2) read(2)
5)關閉本次連線 close(2)
相關程式碼:
#include <t_net.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <errno.h>
int main(int argc,char* argv[]){
//建立套接字
char msg[128];
if(argc < 2){
printf("引數過少\n");
return 0;
}
int cfd = socket(AF_INET,SOCK_STREAM,0);
if(cfd == -1){
printf("%s\n",strerror(errno));
return 0;
}
//向伺服器發起請求
SA4 addr;
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
//埠號
addr.sin_port = htons(8000);
char *ipaddress = argv[1];
struct in_addr ip;
inet_pton(AF_INET,ipaddress,&ip);
addr.sin_addr = ip;
int ret = connect(cfd,(SA*)&addr,sizeof(SA));
if(ret == 0){
printf("連線成功...\n");
}
else{
printf("連線失敗...\n");
return 0;
}
while(1){
gets(msg);
write(cfd,msg,sizeof(msg));
char rbuf[128];
int r = read(cfd,rbuf,sizeof(rbuf));
printf("message from server:\n");
printf("%s\n",rbuf);
if(strcmp(rbuf,"BYEBYE") == 0) break;
}
close(cfd);
return 0;
}
相關文章
- Golang 併發程式設計(channel實現)Golang程式設計
- socket程式設計實現tcp伺服器_C/C++程式設計TCP伺服器C++
- 《java併發程式設計的藝術》併發底層實現原理Java程式設計
- 【併發程式設計】Future模式及JDK中的實現程式設計模式JDK
- 【Linux系統程式設計】libevent庫實現簡易tcp伺服器Linux程式設計TCP伺服器
- Java併發程式設計 - 第十一章 Java併發程式設計實踐Java程式設計
- Java 併發程式設計:ThreadLocal 的使用及其原始碼實現Java程式設計thread原始碼
- 快速瞭解Python併發程式設計的工程實現(上)Python程式設計
- 快速瞭解Python併發程式設計的工程實現(下)Python程式設計
- Java併發程式設計實踐Java程式設計
- Golang 併發程式設計實踐Golang程式設計
- 併發程式設計中死鎖、遞迴鎖、程式/執行緒池、協程TCP伺服器併發等知識點程式設計遞迴執行緒TCP伺服器
- 通過 Socket 實現 TCP 程式設計入門TCP程式設計
- 【Java併發程式設計】Synchronized關鍵字實現原理Java程式設計synchronized
- Python 中一種輕鬆實現併發程式設計的方法Python程式設計
- 併發程式設計程式設計
- 【面試實戰】# 併發程式設計面試程式設計
- 用多執行緒,實現併發,TCP執行緒TCP
- 41、併發程式設計之多程式實操篇程式設計
- Java併發程式設計:ThreadLocal的使用以及實現原理解析Java程式設計thread
- 【TCP/IP】TCP伺服器併發處理&原始碼TCP伺服器原始碼
- java併發程式設計系列:java併發程式設計背景知識Java程式設計
- Java併發程式設計的藝術,解讀併發程式設計的優缺點Java程式設計
- Java併發程式設計---java規範與模式下的併發程式設計1.1Java程式設計模式
- Java併發程式設計實踐-this溢位Java程式設計
- JAVA併發程式設計實踐 下載Java程式設計
- 《JAVA併發程式設計實戰》物件的組合Java程式設計物件
- 【併發程式設計】(二)Java併發機制底層實現原理——synchronized關鍵字程式設計Javasynchronized
- java 併發程式設計Java程式設計
- 併發程式設計—— LinkedTransferQueue程式設計
- 併發程式設計(ReentrantLock)程式設計ReentrantLock
- Go 併發程式設計Go程式設計
- golang併發程式設計Golang程式設計
- Golang 併發程式設計Golang程式設計
- Python併發程式設計Python程式設計
- 併發程式設計 synchronized程式設計synchronized
- 併發程式設計(四)程式設計
- 併發程式設計(二)程式設計