Linux C-網絡編程--上篇

1.通用套接字

#include<linux/socket.h>
struct sockaddr{	
unsigned short sa_family;//協議族類型2字節,AF_INET(TCP/IP協議)	
char sa_data[14]; //具體協議地址14字節
}

一般並不對該通用結構體進行操作,而是使用另一個等價數據結構

2.具體賦值使用的套接字

struct sockaddr_in{	
    unsigned short sin_family;    //協議族類型2字節
    unsigned short int sin_port;  //端口號2字節	
    struct in_addr sin_addr;      //IP地址4字節
    unsigned char sin_zero[8];	  //填充字節8字節,一般賦值爲0
}
struct in_addr{
    unsigned long s_addr;
}

通常在編寫基於TCP/IP協議的網絡程序時,使用結構體sockaddr_in來設置地址,然後通過強制類型轉換成sockaddr類型

struct sockaddr_in sock;
sock.sin_family=AF_INET;
sock.sin_port=htons(PORT)//設置端口號
sock.sin_addr.s_addr=inet_addr(ADDRESS);//設置地址
memset(sock.sin_zero,0x00,sizeof(sock.sin_zero))

3.創建套接字

shell下可通過man socket查看函數原型

#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
/*
返回值:
	成功返回新的套接字
	失敗返回-1
參數:
domain:
 	AF_UNIX 只在本機通信的套接字
 	AF_INET 使用IPv4 TCP/IP協議
 	AF_INET6 使用IPv6 TCP/IP協議
type:
 	SOCK_STREAM TCP流套接字
 	SOCK_DGRAM UDP數據報套接字
 	SOCK_RAW 原始套接字
protocol:
 	通常設置爲0表示通過前兩個參數確定協議
 	使用原始套接字時,系統無法唯一確定協議,此參數指定所用協議

誤碼存入error
*/
//TCP套接字
int sock_fd;
sock_fd = socket(AF_INET,SOCK_STREAM,0);
//UDP套接字
sock_fd = socket(AF_INET,SOCK_DGRAM,0);

4.建立連接

1.面向連接套接字(TCP)只能調一次connect
2.UDP可以調用多次,改變與目的地址的綁定


#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *serv_addr,socklen_t addrlen);
/*
成功返回0失敗返回-1;錯誤碼存入errno
若將serv_add的sa_family設置爲AF_UNSPEC可以取消綁定。
addrlen爲參數serv_addr的長度。
*/
struct sockaddr_in serv_addr;
memset(&serv_addr,0,sizeof(struct sockaddr_in));
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(PORT)
inet_aton(ADDRESS,&serv_addr.sin_addr);
memset(sock.sin_zero,0,sizeof(sock.sin_zero))
connect(sock_fd,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr_in));

1.若套接字類型爲SOCK_STREAM,則connect用於向服務器發送請求,服務器IP地址和PORT由參數2指定。
2.若套接字類型爲SOCK_DGRAM,則connect並不是建立真正連接,只是告訴內核與該套接字進行通信的目的地址(參數2),只有該目的地址發來的數據纔會被該socket接收。
3.對於SOCK_DGRAM類型的套接字,調用connect的好處是不必每次發送和接收數據都指定目的地址。

5.綁定套接字

#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *my_addr,socklen_t addrlen);
/*
成功返回0失敗返回-1,錯誤碼存入error
*/
struct sockaddr_in serv_addr;
memset(&serv_addr,0,sizeof(struct sockaddr_in));
serv_addr.sin_family=AF_INET;serv_addr.sin_port=htons(PORT)
//INADDR_ANY表示可綁定到任意網絡接口,對於多網卡,則表示本服務器程序將處理來自所有網絡接口上相應端口的連接請求
sock.sin_addr.s_addr=htonl(INADDR_ANY);
ifbind(sock_fd,(struct sockaddr*)&serv_addr,sizeof(struct sockaddr_in))<0{
     perror("bind error");   
     exit(1);
} 

6.監聽套接字

#include<sys/socket.h>
int listen(int sock_fd,int backlog);
參數backlog表示連接請求隊列最大長度。若連接滿,則之後連接被服務器拒絕。
成功返回0失敗返回-1

注意函數listen只是將套接字設置爲監聽模式,並不能接收連接請求。

7.接受連接

#include<sys/types.h>
#include<sys/socket.h>
int accept(int sock_fd,struct sockaddr* addr,socklen_t* addrlen);
//參數addr保存發起連接請求的IP和端口
//成功返回新的客戶端套接字,失敗返回-1

Linux 套接字默認是阻塞方式,也可設置爲非阻塞模式,則隊列空accept立即返回-1 errno爲EAGAIN

int client_fd;
int client_len;
struct sockaddr_in client_addr;
client_len=sizeof(struct sockaddr_in);
client_fd=accept(sock_fd,(struct sockaddr*)&client_addr,&lcient_len);

8.發送和接收數據

8.1TCP數據傳輸

//發送數據
#include<sys/type.h>
#include<sys/socket.h>
ssize_t send(int sd,const void* msg,size_t len,int flags);
//該函數只能對處於連接狀態的套接字使用。
/*
sd:套接字
msg:待發送數據
len:待發送數據長度
flags:控制選項,一般設置爲0,或者以下值
	MSG_OOB:指定的套接字上發送帶外數據(該條件字必須支持纔可以)
	MSG_DONTROUTE:通過最直接的路徑發送數據,忽略下層協議的路由設置
返回值:
	成功返回實際發送字節數,失敗返回-1
	發送數據過長則出現錯誤error設置爲EMSGSIZE
	若發送數據大於該套接字的緩衝區域剩餘空間,則send一般會被阻塞
	若該套接字被設置爲非阻塞方式,則立即返回-1,error爲EAGAIN
*/
//接收數據
#include<sys/type.h>
#include<sys/socket.h>
ssize_t recv(int sd,void* buf,size_t len,int flags);
//該函數處理面向連接的套接字即TCP連接
/*
flag:控制選項一般爲0或以下值
	MSG_OOB:請求接收帶外數據
	MSG_PEEK:只查看數據而不讀出
	MSG_WAITALL:只在接收緩衝區滿時才返回
返回值:成功返回接收的數據字節數,錯誤返回-1
	若數據包太長緩衝區放不下,則剩餘數據可能被丟棄(根據接收數據的套接字類型而定)
	若指定套接字無數據到達時,則recv阻塞
	可設置該套接字爲非阻塞模式,立即返回-1,error爲EAGAIN
	函數recv接收到數據就返回,並不會等待接收到參數len指定長度的數據才返回。
*/

8.2 UDP數據傳輸

#include<sys/type.h>
#include<sys/socket.h>
ssize_t sendto(int sd,const void* msg,size_t len,int flags,const struct sockaddr *to,socklen_t tolen);
//該函數不需要套接字處於連接狀態,可用來發送UDP數據,因爲是面向無連接的套接字,因此使用sendto需要指定數據的目的地址。
//參數與send相同的一致。
//成功返回實際發送字節數,出錯返回-1

#include<sys/type.h>
#include<sys/socket.h>
ssize_t recvfrom(int sd,void* buf,size_t len,int flags,struct sockaddr *from,socklen_t *fromlen);
//參數from中將保存數據的源地址
//參數fromlen調用前保存參數from的長度,調用後保存from的實際長度
//執行成功返回實際接收數據的字節數,失敗返回-1

9.關閉套接字描述符

#include<unistd.h>
int close(int fd);
成功返回0,失敗返回-1
#include<sys/socket.h>
int shutdown(int s,int how);
//shutdown功能比close強大,可進行更細緻的控制。
參數:how指定關閉方式
	SHUT_RD:關閉連接的讀通道,不再接收數據,接收緩衝區中未被讀取的數據將被丟棄,仍可發送數據
	SHUT_WR:關閉連接的寫通道,進程不再發送數據,發送緩衝區中還未發送的數據將被丟棄,仍可接收數據
	SHUT_RDWR:讀寫通道均被關閉
成功返回0,失敗返回-1
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章