取得本地主機名:
int gethostname(char *hostname, size_t size);
獲得主機名存到hostname中。
取得本地的信息:
int getsockname(int sockfd, struct sockaddr *addr, int *addrlen);
addr存有返回的主機信息。
示例:
struct sockaddr_in sa;
int len = sizeof(sa);
getpeername(sockfd, (struct sockaddr *)&sa, &len);
printf("本地IP:%s", inet_ntoa(sa.sin_addr));
取得對方主機的信息:
int getpeername(int sockfd, struct sockaddr *addr, int *addrlen);
addr存有返回的主機信息。
示例:
struct sockaddr_in sa;
int len = sizeof(sa);
getpeername(sockfd, (struct sockaddr *)&sa, &len);
printf("對方IP:%s", inet_ntoa(sa.sin_addr));
獲得DNS信息:
struct hostent *gethostbyname(const char *name);
struct hostent *gethostbyaddr(const char *addr, int len, int type);
返回了一個指向struct hostent的指針,struct hostent定義如下:
struct hostent {
char *h_name; /* official name of host */
char **h_aliases; /* alias list */
int h_addrtype; /* host address type */
int h_length; /* length of address */
char **h_addr_list; /* list of addresses */
};
#define h_addr h_addr_list[0] /* for backward compatibility */
對於如何獲得DNS信息,將在後面的程序片斷中詳細講解。
DNS操作時的錯誤處理與普通程序不同,gethostbyname通過設置h_errno代表出錯號,對應的錯誤函數有hstrerror()和herror(),分別對應於strerror()和perror()這兩個普通的錯誤函數。
獲得或改變socket屬性
int getsockopt(int sockfd, int level, int name, char *value, int *optlen);
int setsockopt(int sockfd, int level, int name, char *value, int *optlen);
對於socket編程,level一般爲常數SOL_SOCKET
name屬性類型,value屬性參數,optlen屬性內存塊的長度
常用的有:
SO_RCVTIMEO,SO_SNDTIMEO:獲得或設置socket發送/接收的timeout。
SO_SNDBUF,SO_RCVBUF:獲得或設置socket發送/接收的buffer大小。
SO_BROADCAST:獲得或設置socket狀況,使之可以廣播發送數據報。(只能用於UDP方式)。
SO_REUSEADDR:設置該socket綁定的端口可以被重用。
注意:在Linux系統中,如果一個socket綁定了某個端口,該socket正常關閉或程序退出後,在一段時間內該端口依然保持被綁定的狀態,其他程序(或者重新啓動的原程序)無法綁定該端口。可以通過調用以下語句避免該問題:
opt = 1;
len = sizeof(opt);
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,&len);
獲得或改變socket的I/O屬性:
int ioctl(int sockfd,long cmd,unsigned long* argp);
cmd屬性類型,argp屬性的參數。
常用的有:
FIONREAD,返回socket緩衝區中未讀數據的字節數
FIONBIO,argp爲零時爲阻塞模式,非零時爲非阻塞模式
SIOCATMARK ,判斷是否有未讀的帶外數據(僅用於TCP協議),返回true或false
int fcntl(int fd, int cmd, long arg);
F_SETFL,arp爲O_NONBLOCK時進入非阻塞模式,爲0時進入阻塞模式。
F_GETFL,獲得屬性。
初始化sock連接符:
int socket(int domain, int type, int protocol);
函數返回socket描述符,返回-1表示出錯
domain參數只能取AF_INET, protocol參數一般取0
應用示例:
TCP方式:sockfd = socket(AF_INET,SOCK_STREAM,0);
UDP方式:sockfd =socket(AF_INET, SOCK_DGRAM,0);
綁定端口:
int bind(int sockfd, struct sockaddr *sa, int addrlen);
函數返回-1表示出錯,最常見的錯誤是該端口已經被其他程序綁定。
需要注意的一點:在Linux系統中,1024以下的端口只有擁有root權限的程序才能綁定。
連接網絡(用於TCP方式):
int connect(int sockfd, struct sockaddr *servaddr, int addrlen);
函數返回-1表示出錯,可能是連接超時或無法訪問。返回0表示連接成功,可以通過sockfd傳輸數據了。
監聽端口(用於TCP方式):
int listen(int sockfd, int queue_length);
需要在此前調用bind()函數將sockfd綁定到一個端口上,否則由系統指定一個隨機的端口。
接收隊列:一個新的Client的連接請求先被放在接收隊列中,直到Server程序調用accept函數接受連接請求。
第二個參數queue_length,指的就是接收隊列的長度 也就是在Server程序調用accept函數之前最大允許的連接請求數,多餘的連接請求將被拒絕。
響應連接請求(用於TCP方式):
int accept(int sockfd,struct sockaddr *addr,int *addrlen);
accept()函數將響應連接請求,建立連接併產生一個新的socket描述符來描述該連接,該連接用來與特定的Client交換信息。
函數返回新的連接的socket描述符,錯誤返回-1
addr將在函數調用後被填入連接對方的地址信息,如對方的IP、端口等。
addrlen作爲參數表示addr內存區的大小,在函數返回後將被填入返回的addr結構的大小。
accept缺省是阻塞函數,阻塞直到有連接請求
應用示例:
struct sockaddr_in their_addr; /* 用於存儲連接對方的地址信息*/
int sin_size = sizeof(struct sockaddr_in);
… …(依次調用socket(), bind(), listen()等函數)
new_fd = accept(sockfd, &their_addr, &sin_size);
printf(”對方地址: %s/n", inet_ntoa(their_addr.sin_addr));
… …
關閉socket連接:
int close(int sockfd);
關閉連接將中斷對該socket的讀寫操作。
關閉用於listen()的socket描述符將禁止其他Client的連接請求。
部分關閉socket連接:
int shutdown(int sockfd, int how);
Shutdown()函數可以單方面的中斷連接,即禁止某個方向的信息傳遞。
參數how:
0 - 禁止接收信息
1 - 禁止發送信息
2 - 接收和發送都被禁止,與close()函數效果相同
socket輪詢選擇:
int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
應用於多路同步I/O模式(將在同步工作模式中詳細講解)
FD_ZERO(*set) 清空socket集合
FD_SET(s, *set) 將s加入socket集合
FD_CLR(s, *set) 從socket集合去掉s
FD_ISSET(s, *set) 判斷s是否在socket集合中
常數FD_SETSIZE:集合元素的最多個數
等待選擇機制:
int poll(struct pollfd *ufds, unsigned int nfds, int timeout);
是select機制的一個變種,應用於多路同步I/O模式(將在同步工作模式中詳細講解)
ufds是pollfd結構的數組,數組元素個數爲nfds。
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
接收/發送消息:
TCP方式:
int send(int s, const void *buf, int len, int flags);
int recv(int s, void *buf, int len, int flags);
函數返回實際發送/接收的字節數,返回-1表示出錯,需要關閉此連接。
函數缺省是阻塞函數,直到發送/接收完畢或出錯
注意:如果send函數返回值與參數len不相等,則剩餘的未發送信息需要再次發送
UDP方式:
int sendto(int s, const void *buf, int len, int flags, const struct sockaddr *to, int tolen);
int recvfrom(int s,void *buf, int len, int flags, struct sockaddr *from, int *fromlen);
與TCP方式的區別:
需要指定發送/接收數據的對方(第五個參數to/from)
函數返回實際發送/接收的字節數,返回-1表示出錯。
函數缺省是阻塞函數,直到發送/接收完畢或出錯
注意:如果send函數返回值與參數len不相等,則剩餘的未發送信息需要再次發送
基於消息的方式:
int sendmsg(int s, const struct msghdr *msg, int flags);
int recvmsg(int s, struct msghdr *msg, int flags);
發送/接收一個消息,消息使用如下數據結構:
struct msghdr {
void * msg_name; /* optional address */
socklen_t msg_namelen; /* size of address */
struct iovec * msg_iov; /* scatter/gather array */
size_t msg_iovlen; /* # elements in msg_iov */
void * msg_control; /* ancillary data, see below */
socklen_t msg_controllen; /* ancillary data buffer len */
int msg_flags; /* flags on received message */
};
這種方式可以使用面向連接和無連接兩種方式,靈活性較大,但不太常用,將在後面的程序示例(網絡仿真設備)中解釋工作流程。
標誌位:
上面這六個發送/接收函數均有一個參數flags,用來指明數據發送/接收的標誌,常用的標誌主要有:
MSG_PEEK 對數據接收函數有效,表示讀出網絡數據後不清除已讀的數據
MSG_WAITALL 對數據接收函數有效,表示一直執行直到buf讀滿、socket出錯或者程序收到信號。
MSG_DONTWAIT 對數據發送函數有效,表示不阻塞等待數據發送完後返回,而是直接返回。(只對非阻塞socket有效)
MSG_NOSIGNAL 對發送接收函數有效,表示在對方關閉連接後出錯但不發送SIGPIPE信號給程序。
MSG_OOB 對發送接收都有效,表示讀/寫帶外數據(out-of-band data)
http://263.aka.org.cn/Lectures/002/Lecture-2.1.8/Lecture-2.1.8/new_page_31.htm
帶外數據實例圖
IP地址轉換函數:
unsigned long inet_addr (const char *cp);
inet_addr將一個點分十進制IP地址字符串轉換成32位數字表示的IP地址(網絡字節順序)。
char* inet_ntoa (struct in_addr in);
inet_ntoa將一個32位數字表示的IP地址轉換成點分十進制IP地址字符串。
這兩個函數互爲反函數
字節順序轉換
htons()--"Host to Network Short"
htonl()--"Host to Network Long"
ntohs()--"Network to Host Short"
ntohl()--"Network to Host Long"