1. 程式人生 > >linux下C語言程式設計日誌(1):基於TCP協議的伺服器/客戶端程式

linux下C語言程式設計日誌(1):基於TCP協議的伺服器/客戶端程式

  基於TCP協議的伺服器/客戶端程式

  首先我們看一下使用TCP協議進行網路通訊的程式基本模型:伺服器首先進行初始化操作:呼叫函式socket建立一個套接字,函式bind將這個套接字與伺服器的公認地址繫結在一起,函式listen將這個套接字換成傾聽套接字,然後呼叫函式accept來等待客戶機的請求。過了一段時間後,客戶機啟動,呼叫socket建立一個套接字,然後呼叫函式connect來與伺服器建立連線。連線建立之後,客戶機和伺服器通過讀、寫套接字來進行通訊。

熟練使用模型中的所有函式是編寫網路程式的重要前提,下面我們就挑選幾個重要的函式進行講解,其它的可以參考linux的幫助手冊。

Socket()函式原型為:

#include <sys/types.h>

#include < sys/socket.h >

int socket(int domain,int type,int protocol);

引數domain指定要建立的套接字的協議簇,具體見表7-1;引數type指定套接字型別,具體見表7-2;引數protocol指定使用哪種協議。函式socket成功執行時,返回一個正整數,稱為套接字描述符,否則返回-1,並設定errno為相應的錯誤型別。

7-1 domain的取值及其含義

Flags

含義

AF_UNIX

UNIX協議簇,本機的程序通訊時使用

AF_INET

Internet協議簇(TCP/IP

AF_ISO

ISO協議簇

7-2 type的取值及其含義

Flags

含義

SOCK_STREAM

流式套接字

AF_DGRAM

資料報套接字

AF_RAW

原始套接字

bind()函式原型為:

#include <sys/types.h>

#include < sys/socket.h >

int bind(int sockfd,struct sockaddr *my_addr,int addrlen);

sockfd是呼叫socket返回的檔案描述符;my_addr儲存地址資訊(埠和IP地址);addrlen設定為套接字地址的長度,即sizeof(struct sockaddr)。函式bind成功執行時返回0;否則返回-1,並設定errno的錯誤型別。

connect()函式用來與遠端伺服器建立一個TCP連線,其函式原型為:

#include <sys/types.h>

#include < sys/socket.h >

int connect(int sockfd,struct sockaddr *serv_addr,int addrlen);

引數sockfd是呼叫socket返回的檔案描述符;引數serv_addr指定遠端伺服器的套接字地址,包括埠和IP地址;引數addrlen設定為套接字地址的長度。函式connect成功執行時返回0;否則返回-1,並設定errno的錯誤型別。

從套接字讀寫資料的read()和write()函式原型為:

#include <sys/types.h>

#include < sys/socket.h >

int read(int fd,char *buf,int len);

int write(int fd,char *buf,int len);

引數fd指定讀寫操作的套接字描述符;函式read的引數buf指定接收資料緩衝區,函式write的引數buf指定傳送資料緩衝區;引數len指定接收或傳送的資料量大小。

  下面我們就利用前面的理論知識具體編寫一個簡單的基於TCP協議的伺服器/客戶端程式。

  務器端程式server.c

/*************本程式用於建立服務端程式*****************/

#include <stdlib.h>

#include <stdio.h>

#include <errno.h>

#include <string.h>

#include <netdb.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <sys/socket.h>

int main(int argc, char *argv[])

{

int sockfd,new_fd;

struct sockaddr_in server_addr;

struct sockaddr_in client_addr;

int sin_size,portnumber;

char hello[]={"Hello! Are You Fine?/n"};

if(argc!=2)

{

printf("Usage:%s portnumber/a/n",argv[0]);

exit(1);

}

if((portnumber=atoi(argv[1]))<0)

{

printf("Usage:%s portnumber/a/n",argv[0]);

exit(1);

}

/* 伺服器端開始建立socket描述符 */

if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)

{

printf("Socket error:%s/n/a",strerror(errno));

exit(1);

}

/* 伺服器端填充 sockaddr_in結構 */

bzero(&server_addr,sizeof(struct sockaddr_in));

server_addr.sin_family=AF_INET;

server_addr.sin_addr.s_addr=htonl(INADDR_ANY); /*IP地址轉換為網路位元組序*/

server_addr.sin_port=htons(portnumber); /*埠號轉換為網路位元組序*/

/* 捆綁sockfd描述符 */

if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==

-1)

{

printf("Bind error:%s/n/a",strerror(errno));

exit(1);

}

/* 監聽sockfd描述符 */

if(listen(sockfd,5)==-1) /*5為請求佇列的最大請求數*/

{

printf("Listen error:%s/n/a",strerror(errno));

exit(1);

}

while(1)

{

/* 伺服器阻塞,直到客戶程式建立連線 */

sin_size=sizeof(struct sockaddr_in);

if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size

))==-1)

{

printf("Accept error:%s/n/a",strerror(errno));

exit(1);

}

/*inet_ntoa的作用是將一個32Ipv4地址轉換為相應的點分十進位制數串*/

printf("Server get connection from %s/n",inet_ntoa(client_addr.sin_addr));

/*相客戶端傳送hello字元陣列的內容*/

if(write(new_fd,hello,strlen(hello))==-1)

{

printf("Write Error:%s/n",strerror(errno));

exit(1);

}

/* 這個通訊已經結束 */

close(new_fd);

}/* while結尾處*/

close(sockfd);

exit(0);

}

客戶端程式client.c

#include <stdlib.h>

#include <stdio.h>

#include <errno.h>

#include <string.h>

#include <netdb.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <sys/socket.h>

int main(int argc, char *argv[])

{

int sockfd;

char buffer[1024];

struct sockaddr_in server_addr;

struct hostent *host;

int portnumber,nbytes;

if(argc!=3)

{

printf("Usage:%s hostname portnumber/a/n",argv[0]);

exit(1);

}

/*gethostbyname可以通過主機名稱得到主機的IP地址*/

if((host=gethostbyname(argv[1]))==NULL)

{

printf("Gethostname error/n");

exit(1);

}

/*portnumber為埠號*/

if((portnumber=atoi(argv[2]))<0)

{

printf("Usage:%s hostname portnumber/a/n",argv[0]);

exit(1);

}

/* 客戶程式開始建立 sockfd描述符 */

if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)

{

printf("Socket Error:%s/a/n",strerror(errno));

exit(1);

}

/* 客戶程式填充服務端的資料 */

bzero(&server_addr,sizeof(server_addr));

server_addr.sin_family=AF_INET;

server_addr.sin_port=htons(portnumber);/*主機位元組序轉換為網路位元組序*/

server_addr.sin_addr=*((struct in_addr *)host->h_addr);

/* 客戶程式發起連線請求 */

if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)

{

printf("Connect Error:%s/a/n",strerror(errno));

exit(1);

}

/* 連線成功了 */

if((nbytes=read(sockfd,buffer,1024))==-1)

{

printf("Read Error:%s/n",strerror(errno));

exit(1);

}

buffer[nbytes]='/0';

printf("I have received:%s/n",buffer);

/* 結束通訊 */

close(sockfd);

exit(0);

}

Makefile檔案的編寫

all:server client

server:server.c

gcc $^ -o [email protected]

client:client.c

gcc $^ -o [email protected]

clear:

rm –f server

rm –f client

編譯執行:

執行make後會產生兩個程式server(伺服器端)和client(客戶端)。先執行:

./server portnumber&

其中可以讓portnumber隨便取一個大於1204且不在/etc/services中出現的號碼,這裡就用8888好了,然後執行:

./client localhost 8888

其執行結果是

Server get connection from 127.0 0.1

I have receivedHello! Are You Fine?

/*************本程式用於建立服務端程式*****************/

#include <stdlib.h>

#include <stdio.h>

#include <errno.h>

#include <string.h>

#include <netdb.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <sys/socket.h>

int main(int argc, char *argv[])

{

int sockfd,new_fd;

struct sockaddr_in server_addr;

struct sockaddr_in client_addr;

int sin_size,portnumber;

char hello[]={"Hello! Are You Fine?/n"};

if(argc!=2)

{

printf("Usage:%s portnumber/a/n",argv[0]);

exit(1);

}

if((portnumber=atoi(argv[1]))<0)

{

printf("Usage:%s portnumber/a/n",argv[0]);

exit(1);

}

/* 伺服器端開始建立socket描述符 */

if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)

{

printf("Socket error:%s/n/a",strerror(errno));

exit(1);

}

/* 伺服器端填充 sockaddr_in結構 */

bzero(&server_addr,sizeof(struct sockaddr_in));

server_addr.sin_family=AF_INET;

server_addr.sin_addr.s_addr=htonl(INADDR_ANY); /*IP地址轉換為網路位元組序*/

server_addr.sin_port=htons(portnumber); /*埠號轉換為網路位元組序*/

/* 捆綁sockfd描述符 */

if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==

-1)

{

printf("Bind error:%s/n/a",strerror(errno));

exit(1);

}

/* 監聽sockfd描述符 */

if(listen(sockfd,5)==-1) /*5為請求佇列的最大請求數*/

{

printf("Listen error:%s/n/a",strerror(errno));

exit(1);

}

while(1)

{

/* 伺服器阻塞,直到客戶程式建立連線 */

sin_size=sizeof(struct sockaddr_in);

if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size

))==-1)

{

printf("Accept error:%s/n/a",strerror(errno));

exit(1);

}

/*inet_ntoa的作用是將一個32Ipv4地址轉換為相應的點分十進位制數串*/

printf("Server get connection from %s/n",inet_ntoa(client_addr.sin_addr));

/*相客戶端傳送hello字元陣列的內容*/

if(write(new_fd,hello,strlen(hello))==-1)

{

printf("Write Error:%s/n",strerror(errno));

exit(1);

}

/* 這個通訊已經結束 */

close(new_fd);

}/* while結尾處*/

close(sockfd);

exit(0);

}

客戶端程式client.c

#include <stdlib.h>

#include <stdio.h>

#include <errno.h>

#include <string.h>

#include <netdb.h>

#include <sys/types.h>

#include <netinet/in.h>

#include <sys/socket.h>

int main(int argc, char *argv[])

{

int sockfd;

char buffer[1024];

struct sockaddr_in server_addr;

struct hostent *host;

int portnumber,nbytes;

if(argc!=3)

{

printf("Usage:%s hostname portnumber/a/n",argv[0]);

exit(1);

}

/*gethostbyname可以通過主機名稱得到主機的IP地址*/

if((host=gethostbyname(argv[1]))==NULL)

{

printf("Gethostname error/n");

exit(1);

}

/*portnumber為埠號*/

if((portnumber=atoi(argv[2]))<0)

{

printf("Usage:%s hostname portnumber/a/n",argv[0]);

exit(1);

}

/* 客戶程式開始建立 sockfd描述符 */

if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)

{

printf("Socket Error:%s/a/n",strerror(errno));

exit(1);

}

/* 客戶程式填充服務端的資料 */

bzero(&server_addr,sizeof(server_addr));

server_addr.sin_family=AF_INET;

server_addr.sin_port=htons(portnumber);/*主機位元組序轉換為網路位元組序*/

server_addr.sin_addr=*((struct in_addr *)host->h_addr);

/* 客戶程式發起連線請求 */

if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)

{

printf("Connect Error:%s/a/n",strerror(errno));

exit(1);

}

/* 連線成功了 */

if((nbytes=read(sockfd,buffer,1024))==-1)

{

printf("Read Error:%s/n",strerror(errno));

exit(1);

}

buffer[nbytes]='/0';

printf("I have received:%s/n",buffer);

/*

相關推薦

linuxC語言程式設計日誌1基於TCP協議伺服器/客戶程式

  基於TCP協議的伺服器/客戶端程式  首先我們看一下使用TCP協議進行網路通訊的程式基本模型:伺服器首先進行初始化操作:呼叫函式socket建立一個套接字,函式bind將這個套接字與伺服器的公認地址繫結在一起,函式listen將這個套接字換成傾聽套接字,然後呼叫函式acc

MFC介面程式設計基礎10基於對話方塊的MFC應用程式

上一篇:MFC介面程式設計基礎(09):選單(二) 下一篇:MFC介面程式設計基礎(11):靜態文字框、命令按鈕和編輯框 MFC程式設計 MFC 是 Visual C++ 的核心。雖然在 Windows 應用程

linux C語言程式設計2——程序的建立,掛起,解掛,程序間通訊

在 linux 下利用C語言實現程序的建立,掛起和解掛操作 #include <stdio.h> #include <sys/stat.h> #include <sy

linuxC語言程式設計解決warning : incompatible implicit declaration of built-in function問題

         在C語言程式設計過程中,偶遇如下warning,雖然並不影響最終的編譯結果,但是看著warning也很無語,畢竟強迫症。        我們可以發現被警告沒有宣告的都是常用

linuxC語言程式設計操作MySQL資料庫

原文地址:http://www.2cto.com/database/201506/407827.html 在實際應用中,我們不可能在命令列登入進資料庫進行資料的查詢、插入等操作,使用者一般是使用一個介面良好的應用程式軟體來對資料進行管理。為了方便應用程式的開發,MySQ

C語言小筆記1

列舉型別的大小是4,和一個int整形大小一樣   就是最後一個逗號後面的表示式的值,比如: int a=1,b; b=(a+1,a+2,a+3); 那麼b的值就是a+3,也就是4   函式名   :printf  函式原型:in

C語言複習筆記1——結構體

結構體 結構體宣告 結構體是一種由一序列的成員組成的型別,成員的儲存以順序分配於記憶體中(與聯合體相反,聯合體是由一個序列的成員組成的型別,成員儲存在記憶體中重疊)。 結構體的型別指定符與聯合體( union )型別指定符相同,只是所用的關鍵詞有別。 語法 str

C語言程式設計練習

問題描述:.給出一個英語句子,希望你把句子裡的單詞順序都翻轉過來     輸入樣例:I love you    輸出樣例:you love I  1 /**********************************************************

Linux的socket程式設計實踐TCP服務優化和常見函式

併發下的殭屍程序處理 只有一個程序連線的時候,我們可以使用以下兩種方法處理殭屍程序: 1)通過忽略SIGCHLD訊號,避免殭屍程序     在server端程式碼中新增     signal(

Linux的socket程式設計實踐 Select的限制和poll併發的初步知識

select的限制 用select實現的併發伺服器,能達到的併發數一般受兩方面限制: 1)一個程序能開啟的最大檔案描述符限制。這可以通過調整核心引數來改變。可以通過ulimit -n(number)來調整或者使用setrlimit函式設定(需要root許可權),但一個系

C++抽象程式設計——字串1——字串運算

如今,我們雖然仍然把電腦稱為計算機,但是如今它處理的文字資訊卻遠遠多於我們的數字運算, 現代計算機處理文字資料的能力直接引領了簡訊,電子郵件,文書處理系統,線上參考庫(如百度百科),以及各種其他有用的應用程式的發展。這部分內容介紹了C++中的<string>庫,

小白必看c語言細節整理1

C語言細節整理 這篇文章是用於自己本身對c語言進行的一些整理,如果對大家有幫助的話,記得轉發出去讓更多人瞭解哦。 這是我對c primer plus這本書的整理,所以可能和一些書籍順序會不同,本文章和這本書一起使用最好,我也會對一些書中重要的方面批註頁碼的。 第

C語言學習總結1-遞迴函式的理解

啥是遞迴? 即是該函式呼叫它本身自己,這種呼叫過程稱為遞迴。 遞迴可以相當於迴圈,所以想結束遞迴,就必須有終止遞迴的條件測試部分,否則就會出現無限遞迴(即無限迴圈)。同時,這也是使用遞迴的難點

Linux的socket程式設計實踐Unix域協議和socketpair傳遞檔案描述符

UNIX域協議並不是一個實際的協議族,而是在單個主機上執行客戶/伺服器通訊的一種方法,所用API與在不同主機上執行客戶/伺服器通訊所使用的API相同。UNIX域協議可以視為IPC方法之一,Unix域協

C282例 程式設計日誌1

2014年10月24日14:37:37 》C語言入門 開始從最基礎的開始,學習C Primer Plus,按照書所說系統的學習,劃出重點,寫下想法,打好基礎。 》C語言應用實踐 同時,使用程式設計練習,如c282例程式設計,通過這些掌握的更好。最後一些專案實戰,瞭解一個

C語言詳解1資料型別

資料型別 關於C語言詳解系列部落格的目錄:https://blog.csdn.net/snake_lp/article/details/78630717點選開啟連結一,概述資料型別就是固定記憶體大小空間

【杭電100題】C語言程式設計練習2014 青年歌手大獎賽_評委會打分

http://acm.hdu.edu.cn/showproblem.php?pid=2014Problem Description青年歌手大獎賽中,評委會給參賽選手打分。選手得分規則為去掉一個最高分和一個最低分,然後計算平均得分,請程式設計輸出某選手的得分。Input輸入資料

華為C語言程式設計規範整理

總體原則 1、清晰第一 2、簡潔為美 3、選擇合適的風格,與程式碼原有風格保持一致 1 標頭檔案 對於C語言來說,標頭檔案的設計體現了大部分的系統設計。 原則1.1 標頭檔案中適合放置介

Linux的socket程式設計實踐TCP的粘包問題和常用解決方案

TCP粘包問題的產生 由於TCP協議是基於位元組流並且無邊界的傳輸協議, 因此很有可能產生粘包問題。此外,傳送方引起的粘包是由TCP協議本身造成的,TCP為提高傳輸效率,傳送方往往要收集到足夠多的資料

c語言輸出笑臉'\1'

c語言輸出笑臉(’\1’) 就如上圖,在程式編譯執行之後,輸出了笑臉符號,可是在別人的電腦中又沒有顯示出這個錯誤。也就是說同樣的程式碼,同樣的編譯器,卻顯示不同的內容。 - 笑臉:’\1’ (有的編譯器是顯示空格或者一個正方形框) - 縮寫/字元: