久久久久久久999_99精品久久精品一区二区爱城_成人欧美一区二区三区在线播放_国产精品日本一区二区不卡视频_国产午夜视频_欧美精品在线观看免费

標題: Linux下的TCP socket編程 [打印本頁]

作者: 51黑tt    時間: 2016-3-5 18:39
標題: Linux下的TCP socket編程
網絡編程,一定離不開套接口;那什么是套接口呢?在Linux下,所有的I/O操作都是通過讀寫文件描述符而產生的,文件描述符是一個和打開的文件相關聯的整數,這個文件并不只包括真正存儲在磁盤上的文件,還包括一個網絡連接、一個命名管道、一個終端等,而套接口就是系統進程和文件描述符通信的一種方法。目前最常用的套接口是字:字節流套接口(基于TCP)和數據報套接口(基于UDP),當然還有原始套接口(原始套接口提供TCP套接口和UDP套接口所不提供的功能,如構造自己的TCP或UDP分組)等,我們這里主要介紹字節流套接口和數據報套接口。
  要學習網絡編程,一定離不開網絡庫的函數,下面就將常用的網絡函數和用法列出來供大家參考:
  1、socket函數:為了執行網絡輸入輸出,一個進程必須做的第一件事就是調用socket函數獲得一個文件描述符。


-----------------------------------------------------------------
#include<sys/socket.h>
int socket(int family,int type,int protocol);    
    返回:非負描述字---成功   -1---失敗
-----------------------------------------------------------------

  第一個參數指明了協議簇,目前支持5種協議簇,最常用的有AF_INET(IPv4協議)和AF_INET6(IPv6協議);第二個參數指明套接口類型,有三種類型可選:SOCK_STREAM(字節流套接口)、SOCK_DGRAM(數據報套接口)和SOCK_RAW(原始套接口);如果套接口類型不是原始套接口,那么第三個參數就為0.

PS:
-----------------------------------------------------------------
個人一般用到的就是
if((socket_fd=socket(AF_INET,SOCK_STREAM,0)) == -1 )
    perror("....");
if((socket_fd=socket(AF_INET,SOCK_DGRAM,0)) == -1 )
    perror("....");
-----------------------------------------------------------------

  2、connect函數:當用socket建立了套接口后,可以調用connect為這個套接字指明遠程端的地址;如果是字節流套接口,connect就使用三次握手建立一個連接;如果是數據報套接口,connect僅指明遠程端地址,而不向它發送任何數據。


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

  第一個參數是socket函數返回的套接口描述字;第二和第三個參數分別是一個指向套接口地址結構的指針和該結構的大小。
  這些地址結構的名字均已“sockaddr_”開頭,并以對應每個協議族的唯一后綴結束。以IPv4套接口地址結構為例,它以“sockaddr_in”命名,定義在頭文件;以下是結構體的內容:


------------------------------------------------------------------
struct in_addr {
in_addr_t s_addr;     
};
struct sockaddr_in {
uint8_t sin_len;
sa_family_t sin_family;

in_port_t sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};   
-------------------------------------------------------------------


PS:
------------------------------------------------------------------
struct sockaddr_in dest;



bzero(&dest, sizeof(dest));

dest.sin_family = AF_INET;

dest.sin_port = htons(port);//port就是指定的服務器ip,以及通信的端口

if (inet_aton(ip, (struct in_addr *) &dest.sin_addr.s_addr) == 0) {

perror(ip);

exit(errno);

}



if (connect(sockfd, (struct sockaddr *) &dest, sizeof(dest)) != 0) {

perror("Connect error\n");

exit(errno);

}


-------------------------------------------------------------------


  3、bind函數:為套接口分配一個本地IP 和協議端口,對于網際協議,協議地址是32位IPv4地址或128位IPv6地址與16位的TCP或UDP端口號的組合;如指定端口為0,調用bind時內核將選擇一個臨時端口,如果指定一個通配IP地址,則要等到建立連接后內核才選擇一個本地IP地址。


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

  第一個參數是socket函數返回的套接口描述字;第二和第第三個參數分別是一個指向特定于協議的地址結構的指針和該地址結構的長度。

PS:
-------------------------------------------------------------------

struct sockaddr_in server_address;

server_sockfd = socket(AF_INET, SOCK_STREAM, 0);

server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = htons(9734);
server_len = sizeof(server_address);
bind(server_sockfd, (struct sockaddr *)&server_address, server_len);

-------------------------------------------------------------------

  4、listen函數:listen函數僅被TCP服務器調用,它的作用是將用sock創建的主動套接口轉換成被動套接口,并等待來自客戶端的連接請求。




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

  第一個參數是socket函數返回的套接口描述字;第二個參數規定了內核為此套接口排隊的最大連接個數。由于listen函數第二個參數的原因,內核要維護兩個隊列:以完成連接隊列和未完成連接隊列。未完成隊列中存放的是TCP連接的三路握手為完成的連接,accept函數是從以連接隊列中取連接返回給進程;當以連接隊列為空時,進程將進入睡眠狀態。

PS:
-------------------------------------------------------------------
listen(sockfd,5);
  
-------------------------------------------------------------------

  5、accept函數:accept函數由TCP服務器調用,從已完成連接隊列頭返回一個已完成連接,如果完成連接隊列為空,則進程進入睡眠狀態。


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

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




PS:
-------------------------------------------------------------------
struct sockaddr_in client_address;

client_len = sizeof(client_address);
client_sockfd = accept(server_sockfd,
(struct sockaddr *)&client_address, &client_len);
-------------------------------------------------------------------

  6、inet_pton函數:將點分十進制串轉換成網絡字節序二進制值,此函數對IPv4地址和IPv6地址都能處理。


-------------------------------------------------------------------
#include<sys/inet.h>
int inet_pton(int family,const char * strptr,void * addrptr);
返回:1---成功 0---輸入不是有效的表達格式 -1---失敗

  int inet_aton(const char *strptr, struct in_addr *inp);
-------------------------------------------------------------------

  第一個參數可以是AF_INET或AF_INET6:第二個參數是一個指向點分十進制串的指針:第三個參數是一個指向轉換后的網絡字節序的二進制值的指針。

  7、inet_ntop函數:和inet_pton函數正好相反,inet_ntop函數是將網絡字節序二進制值轉換成點分十進制串。


-------------------------------------------------------------------
#include  
const char * inet_ntop(int family,const void *
addrptr,char * strptr,size_t len);  
   返回:指向結果的指針---成功   NULL---失敗
-------------------------------------------------------------------

  第一個參數可以是AF_INET或AF_INET6:第二個參數是一個指向網絡字節序的二進制值的指針;第三個參數是一個指向轉換后的點分十進制串的指針;第四個參數是目標的大小,以免函數溢出其調用者的緩沖區。
  8、fork函數:在網絡服務器中,一個服務端口可以允許一定數量的客戶端同時連接,這時單進程是不可能實現的,而fork就分配一個子進程和客戶端會話,當然,這只是fork的一個典型應用。




-------------------------------------------------------------------
#include<unistd.h>  
pid_t fork(void);
返回:在子進程中為0,在父進程中為子進程ID   -1---失敗
-------------------------------------------------------------------

  fork函數調用后返回兩次,父進程返回子進程ID,子進程返回0。
  有了上面的基礎知識,我們就可以進一步了解TCP套接口和UDP套接口
  1、TCP套接口
  TCP套接口使用TCP建立連接,建立一個TCP連接需要三次握手,基本過程是服務器先建立一個套接口并等待客戶端的連接請求;當客戶端調用 connect進行主動連接請求時,客戶端TCP發送一個SYN,告訴服務器客戶端將在連接中發送的數據的初始序列號;當服務器收到這個SYN后也給客戶端發一個SYN,里面包含了服務器將在同一連接中發送的數據的初始序列號;最后客戶在確認服務器發的SYN。到此為止,一個TCP連接被建立。
  下面就用一個例子來說明服務器和客戶是怎么連接的


-------------------------------------------------------------------

#include
#include
#include
#include
#include
#include
#include
#include
int main(int argc,char *argv[]) {
int sockfd,numbytes;
char buf[100];
struct hostent *he;
struct sockaddr_in their_addr;
int i = 0;
//將基本名字和地址轉換
he = gethostbyname(argv[1]);
//建立一個TCP套接口
if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1) {
perror("socket");
exit(1);
}
//初始化結構體,連接到服務器的2323端口
their_addr.sin_family = AF_INET;
their_addr.sin_port = htons(2323);
their_addr.sin_addr = *((struct in_addr *)he->h_addr);
bzero(&(their_addr.sin_zero),8);
//和服務器建立連接
if(connect(sockfd,(struct sockaddr *)&their_addr,
sizeof(struct sockaddr))
==-1){
perror("connect");
exit(1);
}
//向服務器發送字符串"hello!"
if(send(sockfd,"hello!",6,0)==-1) {
perror("send");
exit(1);
}
//接受從服務器返回的信息
if((numbytes = recv(sockfd,buf,100,0))==-1) {
perror("recv");
exit(1);
}
buf[numbytes] = '';
printf("result:%s",buf);
close(sockfd);
return 0;
}
--------------------------------------------------------------------

#include
#include
#include
#include
#include
#include
#include
#include
main() {
int sockfd,new_fd;
struct sockaddr_in my_addr;
struct sockaddr_in their_addr;
int sin_size;
//建立TCP套接口
if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1) {
perror("socket");
exit(1);
}
//初始化結構體,并綁定2323端口
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(2323);
my_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(my_addr.sin_zero),8);
//綁定套接口
if(bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct
sockaddr))==-1)
{
perror("bind");
exit(1);
}
//創建監聽套接口
if(listen(sockfd,10)==-1) {
perror("listen");
exit(1);
}
//等待連接
while(1) {
sin_size = sizeof(struct sockaddr_in);
perror("server is run");
//如果建立連接,將產生一個全新的套接字
if((new_fd = accept(sockfd,(struct sockaddr *)
&their_addr,&sin_size))==-1)
{
perror("accept");
exit(1);
}
//生成一個子進程來完成和客戶端的會話,父進程繼續監聽
if(!fork()) {
//讀取客戶端發來的信息
if((numbytes = recv(new_fd,buff,strlen(buff),0))==-1)
{
perror("recv");
exit(1);
}
printf("%s",buff);
//將從客戶端接收到的信息再發回客戶端
if(send(new_fd,buff,strlen(buff),0)==-1)
perror("send");
close(new_fd);
exit(0);
}
close(new_fd);
}
close(sockfd);
}
------------------------------------------------------------------

   現在讓我們來編譯這兩個程序:


root@linuxaid#gcc -o server server.c
root@linuxaid#gcc -o client client.c

  然后在一臺計算機上先運行服務器程序,再在另一個終端上運行客戶端就會看到結果;如果不運行服務器程序而先運行客戶程序將立即提示"Connect: Connection refused",這就是TCP套接口的好處,如果是UDP套接口將會有一個延時才會得到錯誤信息(UDP套接口后面有介紹)。
  建立一個TCP連接需要三次握手,而斷開一個TCP則需要四個分節。當某個應用進程調用close(主動端)后(可以是服務器端,也可以是客戶端),這一端的TCP發送一個FIN,表示數據發送完畢;另一端(被動端)發送一個確認,當被動端待處理的應用進程都處理完畢后,發送一個FIN到主動端,并關閉套接口,主動端接收到這個FIN后再發送一個確認,到此為止這個TCP連接被斷開。






歡迎光臨 (http://www.zg4o1577.cn/bbs/) Powered by Discuz! X3.1
主站蜘蛛池模板: 国产精品区一区二区三区 | 国产欧美精品在线 | 日韩www视频 | 看羞羞视频免费 | 亚洲国产高清在线 | 亚洲综合日韩精品欧美综合区 | 欧美国产日韩精品 | 6080亚洲精品一区二区 | 视频一区在线观看 | 蜜桃毛片| 成人做爰www免费看视频网站 | 欧美日韩视频在线第一区 | 欧美一区二区三区在线观看 | 欧美精品一区二区在线观看 | 精品欧美二区 | 国产精品一区在线观看 | 国产精品久久久久久久久久软件 | 久久精品国产99国产精品 | 日本精品久久 | 在线观看欧美一区 | 99精品久久久久久中文字幕 | 91夜夜夜| 99久久久久 | 欧美日韩综合视频 | 免费看a | 欧美日韩精品一区二区 | 亚洲国产精品一区二区www | 日韩精品在线播放 | 啪啪免费网站 | 在线黄 | 日韩三级视频 | 99九九视频| h视频在线免费观看 | 日韩精品一区二区三区 | 国产精品视频久久 | 91在线精品一区二区 | 日韩a在线 | 亚洲免费在线观看av | 成人免费视频观看视频 | 亚洲精品在线视频 | 在线视频 亚洲 |