Linux下TCP通信簡單實例
基于TCP(面向連接)的socket編程,分為服務(wù)器端和客戶端
服務(wù)器端的流程如下:
(1)創(chuàng)建套接字(socket)
(2)將套接字綁定到一個本地地址和端口上(bind)
(3)將套接字設(shè)為監(jiān)聽模式,準(zhǔn)備接收客戶端請求(listen)
(4)等待客戶請求到來;當(dāng)請求到來后,接受連接請求,返回一個新的對應(yīng)于此次連接的套接字(accept)
(5)用返回的套接字和客戶端進(jìn)行通信(send/recv)
(6)返回,等待另一個客戶請求。
(7)關(guān)閉套接字。
客戶端的流程如下:
(1)創(chuàng)建套接字(socket)
(2)向服務(wù)器發(fā)出連接請求(connect)
(3)和服務(wù)器端進(jìn)行通信(send/recv)
(4)關(guān)閉套接字
下面通過一個具體例子講解一下具體的過程和相關(guān)的函數(shù),環(huán)境是suse linux。
#include <stdio.h> #include <stdlib.h> #include <strings.h> #include <sys/types.h> #include <sys/socket.h> #include <memory.h> #include <unistd.h> //#include <linux/in.h> #include <netinet/in.h> //#include <linux/inet_diag.h> #include <arpa/inet.h> #include <signal.h> /** 關(guān)于 sockaddr sockaddr_in socketaddr_un說明 http://maomaozaoyue.blog.sohu.com/197538359.html */ #define PORT 11910 //定義通信端口 #define BACKLOG 5 //定義偵聽隊列長度 #define buflen 1024 void process_conn_server(int s); void sig_pipe(int signo); int ss,sc; //ss為服務(wù)器socket描述符,sc為某一客戶端通信socket描述符 int main(int argc,char *argv[]) { struct sockaddr_in server_addr; //存儲服務(wù)器端socket地址結(jié)構(gòu) struct sockaddr_in client_addr; //存儲客戶端 socket地址結(jié)構(gòu) int err; //返回值 pid_t pid; //分叉進(jìn)行的ID /*****************socket()***************/ ss = socket(AF_INET,SOCK_STREAM,0); //建立一個序列化的,可靠的,雙向連接的的字節(jié)流 if(ss<0) { printf("server : server socket create error\n"); return -1; } //注冊信號 sighandler_t ret; ret = signal(SIGTSTP,sig_pipe); if(SIG_ERR == ret) { printf("信號掛接失敗\n"); return -1; } else printf("信號掛接成功\n"); /******************bind()****************/ //初始化地址結(jié)構(gòu) memset(&server_addr,0,sizeof(server_addr)); server_addr.sin_family = AF_INET; //協(xié)議族 server_addr.sin_addr.s_addr = htonl(INADDR_ANY); //本地地址 server_addr.sin_port = htons(PORT); err = bind(ss,(struct sockaddr *)&server_addr,sizeof(sockaddr)); if(err<0) { printf("server : bind error\n"); return -1; } /*****************listen()***************/ err = listen(ss,BACKLOG); //設(shè)置監(jiān)聽的隊列大小 if(err < 0) { printf("server : listen error\n"); return -1; } /****************accept()***************/ /** 為類方便處理,我們使用兩個進(jìn)程分別管理兩個處理: 1,服務(wù)器監(jiān)聽新的連接請求;2,以建立連接的C/S實現(xiàn)通信 這兩個任務(wù)分別放在兩個進(jìn)程中處理,為了防止失誤操作 在一個進(jìn)程中關(guān)閉 偵聽套接字描述符 另一進(jìn)程中關(guān)閉 客戶端連接套接字描述符。注只有當(dāng)所有套接字全都關(guān)閉時 當(dāng)前連接才能關(guān)閉,fork調(diào)用的時候父進(jìn)程與子進(jìn)程有相同的 套接字,總共兩套,兩套都關(guān)閉掉才能關(guān)閉這個套接字 */ for(;;) { socklen_t addrlen = sizeof(client_addr); //accept返回客戶端套接字描述符 sc = accept(ss,(struct sockaddr *)&client_addr,&addrlen); //注,此處為了獲取返回值使用 指針做參數(shù) if(sc < 0) //出錯 { continue; //結(jié)束此次循環(huán) } else { printf("server : connected\n"); } //創(chuàng)建一個子線程,用于與客戶端通信 pid = fork(); //fork 調(diào)用說明:子進(jìn)程返回 0 ;父進(jìn)程返回子進(jìn)程 ID if(pid == 0) //子進(jìn)程,與客戶端通信 { close(ss); process_conn_server(sc); } else { close(sc); } } } /** 服務(wù)器對客戶端連接處理過程;先讀取從客戶端發(fā)送來的數(shù)據(jù), 然后將接收到的數(shù)據(jù)的字節(jié)的個數(shù)發(fā)送到客戶端 */ //通過套接字 s 與客戶端進(jìn)行通信 void process_conn_server(int s) { ssize_t size = 0; char buffer[buflen]; //定義數(shù)據(jù)緩沖區(qū) for(;;) { //等待讀 for(size = 0;size == 0 ;size = read(s,buffer,buflen)); //輸出從客戶端接收到的數(shù)據(jù) printf("%s",buffer); //結(jié)束處理 if(strcmp(buffer,"quit") == 0) { close(s); //成功返回0,失敗返回-1 return ; } sprintf(buffer,"%d bytes altogether\n",size); write(s,buffer,strlen(buffer)+1); } } void sig_pipe(int signo) { printf("catch a signal\n"); if(signo == SIGTSTP) { printf("接收到 SIGTSTP 信號\n"); int ret1 = close(ss); int ret2 = close(sc); int ret = ret1>ret2?ret1:ret2; if(ret == 0) printf("成功 : 關(guān)閉套接字\n"); else if(ret ==-1 ) printf("失敗 : 未關(guān)閉套接字\n"); exit(1); } } |
客戶端代碼:
#include <stdio.h> #include <strings.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> //#include <linux/in.h> #include <stdlib.h> #include <memory.h> #include <arpa/inet.h> #include <netinet/in.h> #include <signal.h> //添加信號處理 防止向已斷開的連接通信 /** 信號處理順序說明:在Linux操作系統(tǒng)中某些狀況發(fā)生時,系統(tǒng)會向相關(guān)進(jìn)程發(fā)送信號, 信號處理方式是:1,系統(tǒng)首先調(diào)用用戶在進(jìn)程中注冊的函數(shù),2,然后調(diào)用系統(tǒng)的默認(rèn) 響應(yīng)方式,此處我們可以注冊自己的信號處理函數(shù),在連接斷開時執(zhí)行 */ #define PORT 11910 #define Buflen 1024 void process_conn_client(int s); void sig_pipe(int signo); //用戶注冊的信號函數(shù),接收的是信號值 int s; //全局變量 , 存儲套接字描述符 int main(int argc,char *argv[]) { sockaddr_in server_addr; int err; sighandler_t ret; char server_ip[50] = ""; int port = 0; strcpy(server_ip, argv[1]); port = atoi(argv[2]); /********************socket()*********************/ s= socket(AF_INET,SOCK_STREAM,0); if(s<0) { printf("client : create socket error\n"); return 1; } printf("client : socket fd = %d\n", s); //信號處理函數(shù) SIGINT 是當(dāng)用戶按一個 Ctrl-C 建時發(fā)送的信號 ret = signal(SIGTSTP,sig_pipe); if(SIG_ERR == ret) { printf("信號掛接失敗\n"); return -1; } else printf("信號掛接成功\n") ; /*******************connect()*********************/ //設(shè)置服務(wù)器地址結(jié)構(gòu),準(zhǔn)備連接到服務(wù)器 memset(&server_addr,0,sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(PORT); server_addr.sin_addr.s_addr = htonl(INADDR_ANY); /*將用戶數(shù)入對額字符串類型的IP格式轉(zhuǎn)化為整型數(shù)據(jù)*/ //inet_pton(AF_INET,argv[1],&server_addr.sin_addr.s_addr); printf("please input server ip address : \n"); read(0,server_ip,50); //err = inet_pton(AF_INET,server_ip,&server_addr.sin_addr.s_addr); server_addr.sin_addr.s_addr = inet_addr(server_ip); err = connect(s,(struct sockaddr *)&server_addr,sizeof(sockaddr)); if(err == 0) { printf("client : connect to server\n"); } else { printf("client : connect error\n"); return -1; } //與服務(wù)器端進(jìn)行通信 process_conn_client(s); close(s); } void process_conn_client(int s) { ssize_t size = 0; char buffer[Buflen]; for(;;) { memset(buffer,'\0',Buflen); /*從標(biāo)準(zhǔn)輸入中讀取數(shù)據(jù)放到緩沖區(qū)buffer中*/ size = read(0,buffer,Buflen); // 0,被默認(rèn)的分配到標(biāo)準(zhǔn)輸入 1,標(biāo)準(zhǔn)輸出 2,error if(size > 0) { //當(dāng)向服務(wù)器發(fā)送 “quit” 命令時,服務(wù)器首先斷開連接 write(s,buffer,strlen(buffer)+1); //向服務(wù)器端寫 //等待讀取到數(shù)據(jù) for(size = 0 ; size == 0 ; size = read(s,buffer,Buflen) ); write(1,buffer,strlen(buffer)+1); //向標(biāo)準(zhǔn)輸出寫 } } } void sig_pipe(int signo) //傳入套接字描述符 { printf("Catch a signal\n"); if(signo == SIGTSTP) { printf("接收到 SIGTSTP 信號\n"); int ret = close(s); if(ret == 0) printf("成功 : 關(guān)閉套接字\n"); else if(ret ==-1 ) printf("失敗 : 未關(guān)閉套接字\n"); exit(1); } } |
posted on 2014-02-20 10:57 順其自然EVO 閱讀(458) 評論(0) 編輯 收藏 所屬分類: linux