TCP 服務(wù)端和客戶端程序設(shè)計(C)

流程圖

1 預(yù)備知識

1.1 socket函數(shù)

為了執(zhí)行網(wǎng)絡(luò)輸入輸出船万,一個進(jìn)程必須做的第一件事就是調(diào)用socket函數(shù)獲得一個文件描述符

 #include <sys/socket.h>
 int socket(int family,int type,int protocol);    
      返回:非負(fù)描述字---成功   -1---失敗
  • 第一個參數(shù)指明了協(xié)議簇翔冀,目前支持5種協(xié)議簇芥炭,最常用的有AF_INET(IPv4協(xié)議)和AF_INET6(IPv6協(xié)議)蛹稍;
  • 第二個參數(shù)指明套接口類型铺纽,有三種類型可選:SOCK_STREAM(字節(jié)流套接口)、SOCK_DGRAM(數(shù)據(jù)報套接口)和SOCK_RAW(原始套接口)枚钓;
  • 如果套接口類型不是原始套接口铅搓,那么第三個參數(shù)就為0

1.2 connect函數(shù)

當(dāng)用socket建立了套接口后,可以調(diào)用connect為這個套接字指明遠(yuǎn)程端的地址搀捷;如果是字節(jié)流套接口星掰,connect就使用三次握手建立一個連接多望;如果是數(shù)據(jù)報套接口,connect僅指明遠(yuǎn)程端地址氢烘,而不向它發(fā)送任何數(shù)據(jù)

#include <sys/socket.h>      
  int connect(int sockfd, const struct sockaddr * addr, socklen_t addrlen);  
           返回:0---成功   -1---失敗

第一個參數(shù)是socket函數(shù)返回的套接口描述字怀偷;第二和第三個參數(shù)分別是一個指向套接口地址結(jié)構(gòu)的指針和該結(jié)構(gòu)的大小。
這些地址結(jié)構(gòu)的名字均已“sockaddr_”開頭播玖,并以對應(yīng)每個協(xié)議族的唯一后綴結(jié)束椎工。以IPv4套接口地址結(jié)構(gòu)為例,它以“sockaddr_in”命名蜀踏,定義在頭文件<netinet/in.h>维蒙;以下是結(jié)構(gòu)體的內(nèi)容:

struct in_addr {
 in_addr_t s_addr;     /* IPv4地址 */
}; 
struct sockaddr_in {
 uint8_t sin_len; /* 無符號的8位整數(shù) */
 sa_family_t sin_family;
 /* 套接口地址結(jié)構(gòu)的地址簇,這里為AF_INET */
 in_port_t sin_port; /* TCP或UDP端口 */
 struct in_addr sin_addr;
 char sin_zero[8];  
};

1.3 bind函數(shù)

為套接口分配一個本地IP和協(xié)議端口果覆,對于網(wǎng)際協(xié)議木西,協(xié)議地址是32位IPv4地址或128位IPv6地址與16位的TCP或UDP端口號的組合;如指定端口為0随静,調(diào)用bind時內(nèi)核將選擇一個臨時端口,如果指定一個通配IP地址吗讶,則要等到建立連接后內(nèi)核才選擇一個本地IP地址燎猛。

#include <sys/socket.h>  
 int bind(int sockfd, const struct sockaddr * server, socklen_t addrlen);
 返回:0---成功   -1---失敗 

第一個參數(shù)是socket函數(shù)返回的套接口描述字;
第二和第三個參數(shù)分別是一個指向特定于協(xié)議的地址結(jié)構(gòu)的指針和該地址結(jié)構(gòu)的長度照皆。

1.4 listen函數(shù)

listen函數(shù)僅被TCP服務(wù)器調(diào)用重绷,它的作用是將用sock創(chuàng)建的主動套接口轉(zhuǎn)換成被動套接口,并等待來自客戶端的連接請求膜毁。

#include <sys/socket.h>
 int listen(int sockfd,int backlog);   
 返回:0---成功   -1---失敗

第一個參數(shù)是socket函數(shù)返回的套接口描述字昭卓;
第二個參數(shù)規(guī)定了內(nèi)核為此套接口排隊的最大連接個數(shù)。
由于listen函數(shù)第二個參數(shù)的原因瘟滨,內(nèi)核要維護(hù)兩個隊列:以完成連接隊列和未完成連接隊列候醒。未完成隊列中存放的是TCP連接的三路握手未完成的連接,accept函數(shù)是從已連接隊列中取連接返回給進(jìn)程杂瘸;當(dāng)以連接隊列為空時倒淫,進(jìn)程將進(jìn)入睡眠狀態(tài)。

1.5 accept函數(shù)

accept函數(shù)由TCP服務(wù)器調(diào)用败玉,從已完成連接隊列頭返回一個已完成連接敌土,如果完成連接隊列為空,則進(jìn)程進(jìn)入睡眠狀態(tài)运翼。

#include <sys/socket.h>         
 int accept(int listenfd, struct sockaddr *client, socklen_t * addrlen);  
  回:非負(fù)描述字---成功   -1---失敗

第一個參數(shù)是socket函數(shù)返回的套接口描述字返干;
第二個和第三個參數(shù)分別是一個指向連接方的套接口地址結(jié)構(gòu)和該地址結(jié)構(gòu)的長度;該函數(shù)返回的是一個全新的套接口描述字血淌;如果對客戶段的信息不感興趣矩欠,可以將第二和第三個參數(shù)置為空。

1.6 write和read函數(shù)

當(dāng)服務(wù)器和客戶端的連接建立起來后,就可以進(jìn)行數(shù)據(jù)傳輸了晚顷,服務(wù)器和客戶端用各自的套接字描述符進(jìn)行讀/寫操作峰伙。因為套接字描述符也是一種文件描述符,所以可以用文件讀/寫函數(shù)write()和read()進(jìn)行接收和發(fā)送操作该默。
(1)write()函數(shù)用于數(shù)據(jù)的發(fā)送瞳氓。

#include <unistd.h>         
 int write(int sockfd, char *buf, int len); 
  回:非負(fù)---成功   -1---失敗

參數(shù)sockfd是套接字描述符,對于服務(wù)器是accept()函數(shù)返回的已連接套接字描述符栓袖,對于客戶端是調(diào)用socket()函數(shù)返回的套接字描述符匣摘;
參數(shù)buf是指向一個用于發(fā)送信息的數(shù)據(jù)緩沖區(qū);
len指明傳送數(shù)據(jù)緩沖區(qū)的大小裹刮。
(2)read()函數(shù)用于數(shù)據(jù)的接收音榜。

#include <unistd.h>         
 int read(int sockfd, char *buf, intlen);  
  回:非負(fù)---成功   -1---失敗

參數(shù)sockfd是套接字描述符,對于服務(wù)器是accept()函數(shù)返回的已連接套接字描述符捧弃,對于客戶端是調(diào)用socket()函數(shù)返回的套接字描述符赠叼;
參數(shù)buf是指向一個用于接收信息的數(shù)據(jù)緩沖區(qū);
len指明接收數(shù)據(jù)緩沖區(qū)的大小违霞。

1.7 send和recv函數(shù)

TCP套接字提供了send()和recv()函數(shù)嘴办,用來發(fā)送和接收操作。這兩個函數(shù)與write()和read()函數(shù)很相似买鸽,只是多了一個附加的參數(shù)涧郊。
(1)send()函數(shù)用于數(shù)據(jù)的發(fā)送。

#include <sys/types.h>
#include < sys/socket.h >         
ssize_t send(int sockfd, const void *buf, size_t len, int flags);  
  回:返回寫出的字節(jié)數(shù)---成功   -1---失敗

前3個參數(shù)與write()相同眼五,參數(shù)flags是傳輸控制標(biāo)志妆艘。
(2)recv()函數(shù)用于數(shù)據(jù)的發(fā)送。

#include <sys/types.h>
#include < sys/socket.h >         
ssize_t recv(int sockfd, void *buf, size_t len, int flags);  
  回:返回讀入的字節(jié)數(shù)---成功   -1---失敗

前3個參數(shù)與read()相同看幼,參數(shù)flags是傳輸控制標(biāo)志批旺。

2 服務(wù)器端

2.1 源碼

#include <stdio.h>  
       #include <stdlib.h>  
       #include <string.h>  
       #include <unistd.h>  
       #include <sys/types.h>  
       #include <sys/socket.h>  
       #include <netinet/in.h>  
       #include <arpa/inet.h>  
       
       #define  PORT 1234  
       #define  BACKLOG 1  
   
       int main()  
       {  
       int  listenfd, connectfd;  
       struct  sockaddr_in server;  
       struct  sockaddr_in client;  
       socklen_t  addrlen;  
       if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)  
       {  
       perror("Creating  socket failed.");  
       exit(1);  
       }  
       int opt =SO_REUSEADDR;  
       setsockopt(listenfd,SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));  
       bzero(&server,sizeof(server));  
       server.sin_family=AF_INET;  
       server.sin_port=htons(PORT);  
       server.sin_addr.s_addr= htonl (INADDR_ANY);  
       if(bind(listenfd, (struct sockaddr *)&server, sizeof(server)) == -1) {  
       perror("Binderror.");  
       exit(1);  
       }     
       if(listen(listenfd,BACKLOG)== -1){  /* calls listen() */  
       perror("listen()error\n");  
       exit(1);  
       }  
       addrlen =sizeof(client);  
       if((connectfd = accept(listenfd,(struct sockaddr*)&client,&addrlen))==-1) {  
       perror("accept()error\n");  
       exit(1);  
       }  
       printf("Yougot a connection from cient's ip is %s, prot is %d\n",inet_ntoa(client.sin_addr),htons(client.sin_port));  
       send(connectfd,"Welcometo my server.\n",22,0);  
       close(connectfd);  
       close(listenfd);  
return 0;  
       }  

2.2 編譯運(yùn)行

mkdir tcp_learn
cd tcp_learn/
vim tcpserver.c

粘貼入源碼,然后:

gcc -o tcpserver tcpserver.c        //編譯
./tcpserver             //運(yùn)行

暫時還沒現(xiàn)象桌吃,客戶端還沒連接進(jìn)來朱沃。

3 客戶端

3.1 源碼

#include<stdio.h>  
       #include <stdlib.h>  
       #include<unistd.h>  
       #include<string.h>  
       #include<sys/types.h>  
       #include<sys/socket.h>  
       #include<netinet/in.h>  
       #include<netdb.h>  
   
       #define  PORT 1234  
       #define  MAXDATASIZE 100  
   
       int main(int argc, char *argv[])  
       {  
       int  sockfd, num;  
       char  buf[MAXDATASIZE];  
       struct hostent *he;  
       struct sockaddr_in server;  
       if (argc!=2) {  
       printf("Usage:%s <IP Address>\n",argv[0]);  
       exit(1);  
       }  
       if((he=gethostbyname(argv[1]))==NULL){  
       printf("gethostbyname()error\n");  
       exit(1);  
       }  
       if((sockfd=socket(AF_INET, SOCK_STREAM, 0))==-1){  
       printf("socket()error\n");  
       exit(1);  
       }  
       bzero(&server,sizeof(server));  
       server.sin_family= AF_INET;  
       server.sin_port = htons(PORT);  
       server.sin_addr =*((struct in_addr *)he->h_addr);  
       if(connect(sockfd,(struct sockaddr *)&server,sizeof(server))==-1){  
       printf("connect()error\n");  
       exit(1);  
       }  
       if((num=recv(sockfd,buf,MAXDATASIZE,0)) == -1){  
       printf("recv() error\n");  
       exit(1);  
       }  
       buf[num-1]='\0';  
       printf("Server Message: %s\n",buf);  
       close(sockfd);  
return 0;  
}  

3.2 編譯運(yùn)行

新打開一個命令窗口:

cd tcp_learn/
vim tcpclient.c

粘貼源碼,然后運(yùn)行:

gcc -o tcpclient tcpclient.c
./tcpclient 127.0.0.1

4 結(jié)果

TCP
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末茅诱,一起剝皮案震驚了整個濱河市逗物,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌瑟俭,老刑警劉巖翎卓,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異摆寄,居然都是意外死亡失暴,警方通過查閱死者的電腦和手機(jī)坯门,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來逗扒,“玉大人古戴,你說我怎么就攤上這事【丶纾” “怎么了现恼?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長黍檩。 經(jīng)常有香客問我叉袍,道長,這世上最難降的妖魔是什么刽酱? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任喳逛,我火速辦了婚禮,結(jié)果婚禮上棵里,老公的妹妹穿的比我還像新娘润文。我一直安慰自己,他們只是感情好殿怜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布转唉。 她就那樣靜靜地躺著,像睡著了一般稳捆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上麦轰,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天乔夯,我揣著相機(jī)與錄音,去河邊找鬼款侵。 笑死末荐,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的新锈。 我是一名探鬼主播甲脏,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼妹笆!你這毒婦竟也來了块请?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤拳缠,失蹤者是張志新(化名)和其女友劉穎墩新,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體窟坐,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡海渊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年绵疲,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片臣疑。...
    茶點(diǎn)故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡盔憨,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出讯沈,到底是詐尸還是另有隱情郁岩,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布芙盘,位于F島的核電站驯用,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏儒老。R本人自食惡果不足惜蝴乔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望薇正。 院中可真熱鬧,春花似錦囚衔、人聲如沸挖腰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽猴仑。三九已至,卻和暖如春肥哎,著一層夾襖步出監(jiān)牢的瞬間辽俗,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工篡诽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留崖飘,地道東北人。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓杈女,卻偏偏與公主長得像朱浴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子达椰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容