【摘要】 介紹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
+關注
關注
87文章
11225瀏覽量
208911 -
編程
+關注
關注
88文章
3591瀏覽量
93592 -
線程
+關注
關注
0文章
504瀏覽量
19649
發布評論請先 登錄
相關推薦
評論