操作系統:Linux
主要簡述:Socket編程常用函數
編程語言:c語言
本文涉及到的函數有
socket()、bind()、connect()、listen()、accept()、send()、recv()
涉及到的結構體有
sockaddr_in、in_addr、sockaddr
socket()函數
創建套接字
頭文件
#include <sys/socket.h>
函數原型
int socket(int af, int type, int protocol);
af:IP地址的類型
- AF_INET : IPv4
- AF_INET6: IPV6
type:數據傳輸方式
- SOCK_STREAM:面向連接的數據傳輸方式
- SOCK_DGRAM:無連接的數據傳輸方式
protocol:傳輸協議
- IPPROTO_TCP:TCP傳輸協議
- IPPTOTO_UDP:UDP傳輸協議
返回值
- 成功:0
- 失敗:-1
舉例
使用IPv4協議、面向連接的數據傳輸方式、TCP傳輸協議,來創建套接字
int tcp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
一般情況下有了 af 和 type 兩個參數就可以創建套接字了,操作系統會自動推演出協議類型
所以上述例子也可以寫出以下形式(將傳輸協議設爲0)
int tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
bind()函數
地址綁定,將套接字與地址關聯
頭文件
#include <sys/types.h>
#include <sys/socket.h>
函數原型
int bind(int sockfd, struct sockaddr *addr, socklen_t addrlen);
sockfd:socket文件描述符
addr:sockaddr 結構體變量的指針
addrlen:addr 變量的大小
返回值
- 成功:0
- 失敗:-1
舉例
//創建套接字
int sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//創建sockaddr_in結構體變量
struct sockaddr_in serv_addr;
memset(&serv_addr, 0, sizeof(serv_addr)); //每個字節都用0填充
serv_addr.sin_family = AF_INET; //使用IPv4地址
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具體的IP地址
serv_addr.sin_port = htons(1234); //端口
//將套接字和IP、端口綁定
bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
sockaddr_in 結構體
struct sockaddr_in{
sa_family_t sin_family; //地址族,也就是地址類型
uint16_t sin_port; //16位的端口號
struct in_addr sin_addr; //32位IP地址
char sin_zero[8]; //不使用,一般用0填充
};
端口號需要用 htons() 函數轉換
in_addr 結構體
頭文件
#include <netinet/in.h>
結構體
struct in_addr{
in_addr_t s_addr; //32位的IP地址
};
sockaddr 結構體
struct sockaddr{
sa_family_t sin_family; //地址族,也就是地址類型
char sa_data[14]; //IP地址和端口號
};
sockaddr 是一種通用的結構體,可以用來保存多種類型的IP地址和端口號,而 sockaddr_in 是專門用來保存 IPv4 地址的結構體
connect() 函數
建立連接,創建與指定外部端口的連接
頭文件
#include <sys/types.h>
#include <sys/socket.h>
函數原型
int connect(int sockfd, struct sockaddr *serv_addr, socklen_t addrlen);
sockfd:socket文件描述符
addr:sockaddr 結構體變量的指針
addrlen:addr 變量的大小
返回值
- 成功:0
- 失敗:-1
舉例
connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr))
listen() 函數
讓套接字進入被動監聽狀態
使得一個進程可以接受其它進程的請求,從而成爲一個服務器進程
- 被動監聽:指當沒有客戶端請求時,套接字處於“睡眠”狀態,只有當接收到客戶端請求時,套接字纔會被“喚醒”來響應請求
頭文件
#include <sys/types.h>
#include <sys/socket.h>
函數原型
int listen(int sockfd, int backlog);
sockfd:被監聽的套接字的標識符
backlog:請求隊列的最大長度(能存放多少個客戶端請求)
- 請求隊列:當套接字正在處理客戶端請求時,如果有新的請求進來,套接字將把新的請求放入緩衝區,再從緩衝區取出請求 ,此緩衝區稱爲請求隊列
返回值
- 成功:0
- 失敗:-1
舉例
#define BACKLOG 10
...
if (listen(sockfd, BACKLOG) == -1) {
perror("listen出錯!");
exit(1);
}
accept() 函數
在一個套接口接受一個連接,當套接字處於監聽狀態時,可以通過 accept() 函數來接收客戶端請求
accept() 會阻塞程序執行(後面代碼不能被執行),直到有新的請求到來
頭文件
#include<sys/socket.h>
函數原型
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockfd:服務器端套接字的標識符
addr:sockaddr 結構體變量的指針
addrlen:addr 變量的大小
返回值
- 成功:返回接收到的套接字的描述符
- 失敗:-1
舉例
struct sockaddr_in cli_addr;
sin_size = sizeof(cli_addr);
accept(sockfd, (struct sockaddr *)&cli_addr, &sin_size));
send()函數
發送數據
將數據由指定的socket 傳給對方主機
頭文件
#include <sys/types.h>
#include <sys/socket.h>
函數原型
int send(int s, const void * msg, int len, unsigned int falgs);
s:以建立好連接的socket標識符
msg:發送的消息內容
len:發送內容的長度
falgs:一般設爲0
返回值
- 成功:返回實際傳送出去的字符數
- 失敗:-1
舉例
client_fd = accept(sockfd, (struct sockaddr *)&remote_addr, &sin_size));
...
send(client_fd, "Hello, this is a message\n", 26, 0);
recv()函數
接收數據
接收遠端主機經指定的 socket 傳來的數據, 並把數據存到 buf 指向的內存空間
頭文件
#include <sys/types.h>
#include <sys/socket.h>
函數原型
int recv(int sock, void *buf, int len, unsigned int flags);
sock:接收端套接字描述符
buf:指定緩衝區,存放接收到的數據
len:緩衝區的長度
flags:一般設爲0
返回值
- 成功:返回接收到的字符數
- 失敗:-1
舉例
#define MAXDATASIZE 100 /*每次最大數據傳輸量 */
recv(sockfd, buf, MAXDATASIZE, 0))