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的作用是將一個32位Ipv4地址轉換為相應的點分十進位制數串*/ 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 received:Hello! 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的作用是將一個32位Ipv4地址轉換為相應的點分十進位制數串*/ 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); /*
基於TCP協議的伺服器/客戶端程式 首先我們看一下使用TCP協議進行網路通訊的程式基本模型:伺服器首先進行初始化操作:呼叫函式socket建立一個套接字,函式bind將這個套接字與伺服器的公認地址繫結在一起,函式listen將這個套接字換成傾聽套接字,然後呼叫函式acc
上一篇:MFC介面程式設計基礎(09):選單(二)
下一篇:MFC介面程式設計基礎(11):靜態文字框、命令按鈕和編輯框
MFC程式設計
MFC 是 Visual C++ 的核心。雖然在 Windows 應用程
在 linux 下利用C語言實現程序的建立,掛起和解掛操作
#include <stdio.h>
#include <sys/stat.h>
#include <sy
在C語言程式設計過程中,偶遇如下warning,雖然並不影響最終的編譯結果,但是看著warning也很無語,畢竟強迫症。
我們可以發現被警告沒有宣告的都是常用
原文地址:http://www.2cto.com/database/201506/407827.html
在實際應用中,我們不可能在命令列登入進資料庫進行資料的查詢、插入等操作,使用者一般是使用一個介面良好的應用程式軟體來對資料進行管理。為了方便應用程式的開發,MySQ
列舉型別的大小是4,和一個int整形大小一樣
就是最後一個逗號後面的表示式的值,比如:
int a=1,b; b=(a+1,a+2,a+3); 那麼b的值就是a+3,也就是4
函式名 :printf
函式原型:in
結構體
結構體宣告
結構體是一種由一序列的成員組成的型別,成員的儲存以順序分配於記憶體中(與聯合體相反,聯合體是由一個序列的成員組成的型別,成員儲存在記憶體中重疊)。
結構體的型別指定符與聯合體( union )型別指定符相同,只是所用的關鍵詞有別。
語法 str 問題描述:.給出一個英語句子,希望你把句子裡的單詞順序都翻轉過來
輸入樣例:I love you 輸出樣例:you love I
1 /**********************************************************
併發下的殭屍程序處理
只有一個程序連線的時候,我們可以使用以下兩種方法處理殭屍程序:
1)通過忽略SIGCHLD訊號,避免殭屍程序
在server端程式碼中新增
signal(
select的限制
用select實現的併發伺服器,能達到的併發數一般受兩方面限制:
1)一個程序能開啟的最大檔案描述符限制。這可以通過調整核心引數來改變。可以通過ulimit -n(number)來調整或者使用setrlimit函式設定(需要root許可權),但一個系
如今,我們雖然仍然把電腦稱為計算機,但是如今它處理的文字資訊卻遠遠多於我們的數字運算, 現代計算機處理文字資料的能力直接引領了簡訊,電子郵件,文書處理系統,線上參考庫(如百度百科),以及各種其他有用的應用程式的發展。這部分內容介紹了C++中的<string>庫,
C語言細節整理
這篇文章是用於自己本身對c語言進行的一些整理,如果對大家有幫助的話,記得轉發出去讓更多人瞭解哦。
這是我對c primer plus這本書的整理,所以可能和一些書籍順序會不同,本文章和這本書一起使用最好,我也會對一些書中重要的方面批註頁碼的。
第
啥是遞迴?
即是該函式呼叫它本身自己,這種呼叫過程稱為遞迴。
遞迴可以相當於迴圈,所以想結束遞迴,就必須有終止遞迴的條件測試部分,否則就會出現無限遞迴(即無限迴圈)。同時,這也是使用遞迴的難點
UNIX域協議並不是一個實際的協議族,而是在單個主機上執行客戶/伺服器通訊的一種方法,所用API與在不同主機上執行客戶/伺服器通訊所使用的API相同。UNIX域協議可以視為IPC方法之一,Unix域協
2014年10月24日14:37:37
》C語言入門
開始從最基礎的開始,學習C Primer Plus,按照書所說系統的學習,劃出重點,寫下想法,打好基礎。
》C語言應用實踐
同時,使用程式設計練習,如c282例程式設計,通過這些掌握的更好。最後一些專案實戰,瞭解一個
資料型別 關於C語言詳解系列部落格的目錄:https://blog.csdn.net/snake_lp/article/details/78630717點選開啟連結一,概述資料型別就是固定記憶體大小空間
http://acm.hdu.edu.cn/showproblem.php?pid=2014Problem Description青年歌手大獎賽中,評委會給參賽選手打分。選手得分規則為去掉一個最高分和一個最低分,然後計算平均得分,請程式設計輸出某選手的得分。Input輸入資料
總體原則
1、清晰第一
2、簡潔為美
3、選擇合適的風格,與程式碼原有風格保持一致
1 標頭檔案
對於C語言來說,標頭檔案的設計體現了大部分的系統設計。
原則1.1 標頭檔案中適合放置介
TCP粘包問題的產生
由於TCP協議是基於位元組流並且無邊界的傳輸協議, 因此很有可能產生粘包問題。此外,傳送方引起的粘包是由TCP協議本身造成的,TCP為提高傳輸效率,傳送方往往要收集到足夠多的資料
c語言輸出笑臉(’\1’)
就如上圖,在程式編譯執行之後,輸出了笑臉符號,可是在別人的電腦中又沒有顯示出這個錯誤。也就是說同樣的程式碼,同樣的編譯器,卻顯示不同的內容。
- 笑臉:’\1’ (有的編譯器是顯示空格或者一個正方形框)
- 縮寫/字元: |