歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux管理 >> Linux網絡

Linux網絡編程和套接字

1、套接字概述

套接字的本意是插座,在網絡中用來描述計算機中不同程序與其他計算機程序的通信方式。

常用的套接字類型有3種:

1)流套接字(SOCK——STREAM):使用了面向連接的可靠的數據通信方式,即TCP套接字;

2)數據報套接字(Raw Sockets):使用了不面向連接的數據傳輸方式,即UDP套接字;

3)原始套接字(SOCK——RAW):沒有經過處理的IP數據包,可以根據自己程序的要求進行封裝。

2、常用函數

1、創建套接字函數:成功時返回文件描述符,失敗時返回-1

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

//參數domain用於指定創建套接字所使用的協議族(可取AF_UNIX,AF_INET,AF_INTE6)

//參數type指定套接字的類型(可取SOCK_STREAM,SOCK_DGRAM,SOCK_RAW)

//參數protocol通常設置為0

2、在指定套接字上創建鏈接函數:成功時返回0,失敗時返回-1

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

//參數sockfd是一個由函數socket創建的套接字

//參數serv_addr是一個地址結構,指定服務器的IP地址和端口號

//參數addrlen為參數serv_addr的長度

3、將一個套接字和某個端口綁定在一起的函數:成功時返回0,失敗時返回-1

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

//一般只有服務器端的程序調用,參數my_addr指定了sockfd將綁定到的本地

//地址,可以將參數my_addr的sin_addr設置為INADDR_ANY而不是某個確定

//IP地址就可以綁定到任何網絡接口。

4、把套接字轉化為被動監聽函數:成功時返回0,失敗時返回-1

int listen(int s,int backlog);

//參數s為套接字,參數backlog指定鏈接請求隊列的最大長度;

5、接收連接請求函數:成功時返回文件描述符,失敗時返回-1

int accept(int s,struct sockaddr *addr,socklen_t *addrlen);

//參數s是由函數socket創建,經函數bind綁定到本地某一端口上,然後通過

//函數listen轉化而來的監聽套接字

//參數addr用來保存發起連接請求的主機的地址和端口

//參數addrlen是addr所指向的結構體的大小

6、在TCP套接字上發送數據函數:有連接

包含3要素:套接字s,待發數據msg,數據長度len

ssize_t send(int s,const void *msg,size_t len,int flags);

//函數只能對處於連接狀態的套接字使用,參數s為已建立好連接的套接字描述

//符,即accept函數的返回值

//參數msg指向存放待發送數據的緩沖區

//參數len為待發送數據的長度,參數flags為控制選項,一般設置為0

7、在TCP套接字上接收數據函數:有連接

包含3要素:套接字s,接收緩沖區buf,長度len

ssize_t recv(int s,void *buf,size_t len,int flags);

//函數recv從參數s所指定的套接字描述符(必須是面向連接的套接字)上接收

//數據並保存到參數buf所指定的緩沖區

//參數len則為緩沖區長度,參數flags為控制選項,一般設置為0

8、在UCP套接字上發送數據函數:無連接

ssize_t sendto(int s,const void *msg,size_t len,int flags,const struct sockaddr *to,socklen_t tolen);

//函數功能與函數send類似,但函數sendto不需要套接字處於連接狀態,所以

//該函數通常用來發送UDP數據,同時因為是無連接的套接字,在使用sendto時

//需要指定數據的目的地址,參數msg指向待發送數據的緩沖區。

//參數len指定了待發送數據的長度

//參數flags是控制選項,含義與send函數中的一致

//參數to用於指定目的地址,目的地址的長度由tolen指定

9、在UDP套接字上接收數據函數:無連接

ssize_t recvfrom(int s ,void *buf,size_t len,int flags,struct sockaddr *from,socklen_t *fromlen);

//與函數recv功能類似,只是函數recv只能用於面向連接的套接字,而函數

//recvfrom沒有此限制,可以用於從無連接的套接字上接收數據

//參數buf指向接收緩沖區

//參數len指定了緩沖區的大小

//參數flags是控制選項,含義與recv中的一致

//如果參數from非空,且該套接字不是面向連接的,則函數recvfrom返回時,

//參數from中將保存數據的源地址

//參數fromlen在調用recvfrom前為參數from的長度,調用recvfrom後將

//保存from的實際大小

10、關閉套接字函數:

int close(int fd);

//參數fd為一個套接字描述符;

11、多路復用函數:

int select(int n,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);

//參數n是需要監視的文件描述符數

//參數readfds指定需要監視的可讀文件描述符集合

//參數writefds指定需要監視的可寫文件描述符集合

//參數exceptfds指定需要監視的異常文件描述符的集合

//參數timeout指定了阻塞的時間

3、服務器端套接字(接收連接請求的套接字)創建過程

第一步:調用socket函數創建套接字。

第二步:調用bind函數分配IP地址和端口號。

第三部:調用listen函數轉為可接收請求狀態。

第四步:調用accept函數受理連接請求。

4、客戶端套接字(發送連接請求的套接字)創建過程

只有兩步:

1、調用socket函數創建套接字。

2、調用connect函數向服務器端發送連接請求。

5、Linux的文件操作

文件描述符:是系統自動分配給文件或套接字的整數。

每當生成文件或套接字,操作系統就會自動返回給我們一個整數。這個整數就是文件描述符,即創建的文件或套接字的別名,方便稱呼而已。文件描述符在Windows中又稱為句柄。

1、打開文件:

 

這裡寫圖片描述

 

2、關閉文件或套接字:

 

這裡寫圖片描述

 

3、將數據寫入文件:

 

這裡寫圖片描述

 

4、讀取文件中的數據:

 

這裡寫圖片描述

 

注:ssize_t = signed int, size_t = unsigned int,都是通過typedef聲明,為基本數據類型取的別名。既然已經有了基本數據類型,那麼為什麼還需要為它取別名呢?是因為目前普遍認為int是32位的,而過去16位操作系統時代,int是16位的。根據系統的不同,時代的變化,基本數據類型的表現形式也隨著變化的。如果為基本數據類型取了別名,以後要修改,也就只需要修改typedef聲明即可,這將大大減少代碼變動。

6、服務器端實例

服務器端受理連接請求的程序。編譯並運行該程序,創建等待連接請求的服務器端。

#include

#include

#include

#include

#include

#include

void error_handling(char *message);

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

{

int serv_sock;

int clnt_sock;

struct sockaddr_in serv_addr;

struct sockaddr_in clnt_addr;

socklen_t clnt_addr_size;

char message[] = "Hello World!";

if(argc != 2)

{

printf("Usage:%s \n", argv[0]);

exit(1);

}

//(1)調用socket函數創建套接字

serv_sock = socket(PF_INET, SOCK_STREAM, 0);

if(serv_sock == -1)

error_handling("socket() error");

memset(&serv_addr, 0, sizeof(serv_addr));

serv_addr.sin_family = AF_INET;

serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

serv_addr.sin_port = htons(atoi(argv[1]));

//(2)調用bind函數分配IP地址和端口號

if(bind(serv_sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)

error_handling("bind() error");

//(3)調用listen函數轉為可接收請求狀態

if(listen(serv_sock, 5) == -1)

error_handling("listen() error");

clnt_addr_size = sizeof(clnt_addr);

//(4)調用accept函數受理連接請求.沒有連接請求時調用不會返回,直到有請求

clnt_sock = accept(serv_sock, (struct sockaddr*) &clnt_addr, &clnt_addr_size);

if(clnt_sock == -1)

error_handling("accept() error");

//(5)write函數用於傳輸數據。執行到此行說明已有了連接請求

//向文件描述符為clnt_sock的客戶端文件中傳輸message中的數據

write(clnt_sock, message, sizeof(message));

close(clnt_sock);//(6)

close(serv_sock);//(7)

return 0;

}

void error_handling(char *message)

{

fputs(message, stderr);

fputc('\n', stderr);

exit(1);

}

7、客戶端實例

#include

#include

#include

#include

#include

#include

void error_handling(char *message);

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

{

int sock;

struct sockaddr_in serv_addr;

char message[30];

int str_len;

if(argc != 3)

{

printf("Usage: %s \n", argv[0]);

exit(1);

}

//(1)創建套接字.但此時並不馬上分為客戶端或服務器端

sock = socket(PF_INET, SOCK_STREAM, 0);

if(sock == -1)

error_handling("socket() error");

memset(&serv_addr, 0, sizeof(serv_addr));

serv_addr.sin_family = AF_INET;

serv_addr.sin_addr.s_addr = inet_addr(argv[1]);

serv_addr.sin_port = htons(atoi(argv[2]));

//(2)調用connect函數向服務器端發送連接請求.此時確定為客戶端

if(connect(sock, (struct sockaddr*) &serv_addr, sizeof(serv_addr)) == -1)

error_handling("connect() error");

//(3)調用read函數向自身聲明的message數組中保存數據

str_len = read(sock, message, sizeof(message) - 1);

if(str_len == -1)

error_handling("read() error");

printf("Message from server: %s \n", message);

close(sock);//(4)

return 0;

}

void error_handling(char *message)

{

fputs(message, stderr);

fputc('\n', stderr);

exit(1);

}

Copyright © Linux教程網 All Rights Reserved