㈠ 在linux系統下編寫一個socket程序
我給你一個更高端的
#include <stdlib.h>
#include <sys/types.h>
#include <stdio.h>
#include <sys/socket.h>
#include <linux/in.h>
#include <string.h>
#define M 8888
struct qun
{
int cy[5];
};
struct haoyou
{
int py[5];
};
struct ri
{
int geren[5];
};
int main()
{
struct qun group[5]={0};
struct haoyou pyd[5]={0};
struct ri ziji[5]={0};
printf("server onil\n");
printf("this port is %d\n",M);
fd_set rest;
int servsock=socket(AF_INET,SOCK_STREAM,0);
if(-1==servsock)
{
perror("socket perror");
return 1;
}
struct sockaddr_in servaddr;
struct sockaddr_in clienaddr;
servaddr.sin_family=AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(M);
int a=bind(servsock,(struct sockaddr *)&servaddr,sizeof(servaddr));
if(-1==a)
{
perror("bind perror\n");
return 1;
}
int b=listen(servsock,5);
if(-1==b)
{
perror("listen perror");
return 1;
}
FD_ZERO(&rest);
char sendbuff[1024];
char recvbuff[1024];
int n;
int qw;
int r[10];
int min[1024]={0};
int i=0;
int iWho[10]={0};
int mabi[1024]={0};
char mingzi[10][50]={0};
char caonima[1024]={0};
int wocao[10]={0};
n=sizeof(clienaddr);
struct timeval time;
while(1)
{
time.tv_sec = 0;
time.tv_usec = 470;
FD_SET(servsock,&rest);
FD_SET(fileno(stdin),&rest);
select(servsock+1,&rest,NULL,NULL,&time);
if(FD_ISSET(servsock,&rest))
{
r[i]=accept(servsock,(struct sockaddr *)&clienaddr,&n);
if(r[i]==-1)
{
i--;
}
i++;
}
int p;
for(p=0;p<i;p++)
{
FD_SET(r[p],&rest);
}
int ret=select(r[i-1]+1,&rest,NULL,NULL,&time);
if(ret<0)
{
printf("socket error\n");
continue;
}
else
{
int u;
for(u=0;u<i;u++
㈡ linux socket是什麼意思
基於Linux的SOCKET編程。
㈢ Linux下的socket是怎麼回事,如何利用其實現區域網內的數據處理
//服務端server.c
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#define SERVPORT 6000 /*伺服器監聽埠號 */
#define BACKLOG 10 /* 最大同時連接請求數 */
#define MAXDATASIZE 100
main()
{
char buf[MAXDATASIZE];
int sockfd,client_fd; /*sock_fd:監聽socket;client_fd:數據傳輸socket */
struct sockaddr_in my_addr; /* 本機地址信息 */
struct sockaddr_in remote_addr; /* 客戶端地址信息 */
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket創建出錯!");
exit(1);
}
my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(SERVPORT);
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, BACKLOG) == -1)
{
perror("listen出錯!");
exit(1);
}
while(1)
{
sin_size = sizeof(struct sockaddr_in);
if ((client_fd = accept(sockfd, (struct sockaddr *)&remote_addr, &sin_size)) == -1)
{
perror("accept出錯");
continue;
}
printf("received a connection from %s\n", inet_ntoa(remote_addr.sin_addr));
if (!fork())
{ /* 子進程代碼段 */
if ((recvbytes=recv(client_fd, buf, MAXDATASIZE, 0)) ==-1)
{
perror("recv出錯!");
close(client_fd);
exit(0);
}
buf[recvbytes] = '\0';
printf("from client Received: %s",buf);
if (send(client_fd, "thanks!\n", 8, 0) == -1)
perror("send出錯!");
close(client_fd);
exit(0);
}
close(client_fd);
}
}
//客戶端client.c
#include<stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define SERVPORT 6000
#define MAXDATASIZE 100
main(int argc, char *argv[])
{
int sockfd, recvbytes;
char buf[MAXDATASIZE];
struct hostent *host;
struct sockaddr_in serv_addr;
if (argc < 2)
{
fprintf(stderr,"Please enter the server's hostname!\n");
exit(1);
}
if((host=gethostbyname(argv[1]))==NULL)
{
herror("gethostbyname出錯!");
exit(1);
}
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
perror("socket創建出錯!");
exit(1);
}
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(SERVPORT);
serv_addr.sin_addr = *((struct in_addr *)host->h_addr);
bzero(&(serv_addr.sin_zero),8);
if (connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) == -1)
{
perror("connect出錯!");
exit(1);
}
if (send(sockfd, "hello!\n", 7, 0) == -1)
{
perror("send出錯!");
exit(1);
}
if ((recvbytes=recv(sockfd, buf, MAXDATASIZE, 0)) ==-1)
{
perror("recv出錯!");
exit(1);
}
buf[recvbytes] = '\0';
printf("Received: %s",buf);
close(sockfd);
}
㈣ linux下 socket函數的返回值代表什麼
int socket;domain指明所使用的協議族,通常為PF_INET,表示互聯網協議族;type參數指定socket的類型:SOCK_STREAM 或SOCK_DGRAM,Socket介面還定義了原始Socket,允許程序使用低層協議;protocol通常賦值"0"。
Socket()調用返回一個整型socket描述符,你可以在後面的調用使用它。 Socket描述符是一個指向內部數據結構的指針,它指向描述符表入口。
調用Socket函數時,socket執行體將建立一個Socket,實際上"建立一個Socket"意味著為一個Socket數據結構分配存儲空間。 Socket執行體為你管理描述符表。
(4)linux本地socket擴展閱讀:
支持下述類型描述:
SOCK_STREAM 提供有序的、可靠的、雙向的和基於連接的位元組流,使用帶外數據傳送機制,為Internet地址族使用TCP。
SOCK_DGRAM 支持無連接的、不可靠的和使用固定大小(通常很小)緩沖區的數據報服務,為Internet地址族使用UDP。
SOCK_STREAM類型的套介面為全雙向的位元組流。對於流類套介面,在接收或發送數據前必需處於已連接狀態。用connect()調用建立與另一套介面的連接,連接成功後,即可用send()和recv()傳送數據。當會話結束後,調用close()。帶外數據根據規定用send()和recv()來接收。
㈤ linux socket 設置從哪個網路設備發送數據 SO
原因: 1、 因為伺服器是時時在監聽有沒有客戶端的連接,如果伺服器不綁定IP和埠的話,客戶端上線的時候怎麼連到伺服器呢,所以伺服器要綁定IP和埠,而客戶端就不需要了,客戶端上線是主動向伺服器發出請求的,因為伺服器已經綁定了IP和埠,所以客戶端上線的就向這個IP和埠發出請求,這時因為客戶開始發數據了(發上線請求),系統就給客戶端分配一個隨機埠,這個埠和客戶端的IP會隨著上線請求一起發給伺服器,服務收到上線請求後就可以從中獲起發此請求的客戶的IP和埠,接下來伺服器就可以利用獲起的IP和埠給客戶端回應消息了。 2、採用UDP通信 1)若有客戶端和伺服器之分的程序,創建sock後即可在該socket上用recvfrom/sendto方法發送接受數據了,因為客戶端只需要用sendto發送數據到指定的地址,當然若是bind了,程序也沒什麼問題,區別就是系統用默認自動bind()指定你自己的socket參數地址(特別是在指定特定埠的UDP對等通信)只是這種情況沒有這樣用的。 那UDP伺服器是怎麼知道客戶端的IP地址和UDP埠? 一般來說有兩種方式: 一種是客戶端發消息顯式地告訴伺服器IP地址和埠,消息內容就包括IP地址和UDP埠。 另外一種就是隱式的,伺服器從收到的包的頭部中得到包的源IP地址和埠。 2)若是沒有客戶端和伺服器之分的程序,即自己指定特定埠的UDP對等通信,則客戶端和伺服器都需要bind()IP地址和埠了。 通常udp服務端根本不需要知道客戶端的socket,它直接建立一個socket用於發送即可,udp通信的關鍵只在於IP和埠。 多個客戶端如果需要點到點分發,必須給服務端socket循環設置每個客戶端的IP並發出,但更常用的是廣播分發,服務端socket設定一個X.X.X.255的廣播地址並始終向它發送,每個客戶端建立的socket只需要綁定這個廣播地址便可以收到。 客戶端用不用bind 的區別 無連接的socket的客戶端和服務端以及面向連接socket的服務端通過調用bind函數來配置本地信息。使用bind函數時,通過將my_addr.sin_port置為0,函數會自動為你選擇一個未佔用的埠來使用。 Bind()函數在成功被調用時返回0;出現錯誤時返回"-1"並將errno置為相應的錯誤號。需要注意的是,在調用bind函數時一般不要將埠號置為小於1024的值,因為1到1024是保留埠號,你可以選擇大於1024中的任何一個沒有被佔用的埠號。 有連接的socket客戶端通過調用Connect函數在socket數據結構中保存本地和遠端信息,無須調用bind(),因為這種情況下只需知道目的機器的IP地址,而客戶通過哪個埠與伺服器建立連接並不需要關心,socket執行體為你的程序自動選擇一個未被佔用的埠,並通知你的程序數據什麼時候打開埠。(當然也有特殊情況,linux系統中rlogin命令應當調用bind函數綁定一個未用的保留埠號,還有當客戶端需要用指定的網路設備介面和埠號進行通信等等) 總之: 1.需要在建連前就知道埠的話,需要 bind 2.需要通過指定的埠來通訊的話,需要 bind 具體到上面那兩個程序,本來用的是TCP,客戶端就不用綁定埠了,綁定之後只能運行一個client 的程序,是屬於自己程序中人為設定的障礙,而從伺服器那邊得到的客戶機連接埠號(是系統自動分配的)與這邊客戶機綁定的埠號根本是不相關的,所以客戶 綁定也就失去了意義。 注意: 一個埠可以用於多個連接(比如多個客戶端連接伺服器的同一埠)。但是在同一個操作系統上,即伺服器和客戶端都是本機上,多個客戶端去連接伺服器,只有第一個客戶端的連接會被接收,第二個客戶端的連接請求不會被接收。 首先,伺服器和客戶端都可以bind,bind並不是伺服器的專利。 客戶端進程bind埠: 由進程選擇一個埠去連伺服器,(如果默認情況下,調用bind函數時,內核指定的埠是同一個,那麼運行多個調用了bind 的client 程序,會出現埠被佔用的錯誤)注意這里的埠是客戶端的埠。如果不分配就表示交給內核去選擇一個可用埠。 客戶端進程bind IP地址:相當於為發送出去的IP數據報分配了源IP地址,但交給進程分配IP地址的時候(就是這樣寫明了bind IP地址的時候)這個IP地址必須是主機的一個介面,不能分配一個不存在的IP。如果不分配就表示由內核根據所用的輸出介面來選擇源IP地址。 一般情況下客戶端是不用調用bind函數的,一切都交給內核搞定! 服務端進程bind埠:基本是必須要做的事情,比如一個伺服器啟動時(比如freebsd),它會一個一個的捆綁眾所周知的埠來提供服務,同樣,如果bind了一個埠就表示我這個伺服器會在這個埠提供一些「特殊服務」。 服務端進程bind IP地址:目的是限制了服務端進程創建的socket只接受那些目的地為此IP地址的客戶鏈接,一般一個伺服器程序里都有 servaddr.sin_addr.s_addr = htonl(INADDR_ANY); // 只是針對IP4,IP6代碼不太一樣 這樣一句話,意思就是:我不指定客戶端的IP,隨便連,來者不拒! 總之只要你bind時候沒有指定哪一項(置為0),內核會幫你選擇。
㈥ Linux怎麼使用ss命令查看系統的socket狀態
ss是Socket Statistics的縮寫。顧名思義,ss命令可以用來獲取socket統計信息,它可以顯示和netstat類似的內容。但ss的優勢在於它能夠顯示更多更詳細的有關TCP和連接狀態的信息,而且比netstat更快速更高效。當伺服器的socket連接數量變得非常大時,無論是使用netstat命令還是直接cat /proc/net/tcp,執行速度都會很慢。可能你不會有切身的感受,但請相信我,當伺服器維持的連接達到上萬個的時候,使用netstat等於浪費 生命,而用ss才是節省時間。天下武功唯快不破。ss快的秘訣在於,它利用到了TCP協議棧中tcp_diag。tcp_diag是一個用於分析統計的模塊,可以獲得Linux 內核中第一手的信息,這就確保了ss的快捷高效。當然,如果你的系統中沒有tcp_diag,ss也可以正常運行,只是效率會變得稍慢。(但仍然比 netstat要快。)
命令格式:
ss [參數]
ss [參數] [過濾]
2.命令功能:
ss(Socket Statistics的縮寫)命令可以用來獲取 socket統計信息,此命令輸出的結果類似於 netstat輸出的內容,但它能顯示更多更詳細的 TCP連接狀態的信息,且比 netstat 更快速高效。它使用了 TCP協議棧中 tcp_diag(是一個用於分析統計的模塊),能直接從獲得第一手內核信息,這就使得 ss命令快捷高效。在沒有 tcp_diag,ss也可以正常運行。
3.命令參數:
-h, --help 幫助信息
-V, --version 程序版本信息
-n, --numeric 不解析服務名稱
-r, --resolve 解析主機名
-a, --all 顯示所有套接字(sockets)
-l, --listening 顯示監聽狀態的套接字(sockets)
-o, --options 顯示計時器信息
-e, --extended 顯示詳細的套接字(sockets)信息
-m, --memory 顯示套接字(socket)的內存使用情況
-p, --processes 顯示使用套接字(socket)的進程
-i, --info 顯示 TCP內部信息
-s, --summary 顯示套接字(socket)使用概況
-4, --ipv4 僅顯示IPv4的套接字(sockets)
-6, --ipv6 僅顯示IPv6的套接字(sockets)
-0, --packet 顯示 PACKET 套接字(socket)
-t, --tcp 僅顯示 TCP套接字(sockets)
-u, --udp 僅顯示 UCP套接字(sockets)
-d, --dccp 僅顯示 DCCP套接字(sockets)
-w, --raw 僅顯示 RAW套接字(sockets)
-x, --unix 僅顯示 Unix套接字(sockets)
-f, --family=FAMILY 顯示 FAMILY類型的套接字(sockets),FAMILY可選,支持 unix, inet, inet6, link, netlink
-A, --query=QUERY, --socket=QUERY
QUERY := {all|inet|tcp|udp|raw|unix|packet|netlink}[,QUERY]
-D, --diag=FILE 將原始TCP套接字(sockets)信息轉儲到文件
-F, --filter=FILE 從文件中都去過濾器信息
FILTER := [ state TCP-STATE ] [ EXPRESSION ]
4.使用實例:
實例1:顯示TCP連接
命令:ss -t -a
輸出:
代碼如下:
[root@localhost ~]# ss -t -a
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 0 127.0.0.1:smux *:*
LISTEN 0 0 *:3690 *:*
LISTEN 0 0 *:ssh *:*
ESTAB 0 0 192.168.120.204:ssh 10.2.0.68:49368
[root@localhost ~]#
實例2:顯示 Sockets 摘要
命令:ss -s
輸出:
代碼如下:
[root@localhost ~]# ss -s
Total: 34 (kernel 48)
TCP: 4 (estab 1, closed 0, orphaned 0, synrecv 0, timewait 0/0), ports 3《/p》 《p》Transport Total IP IPv6
* 48 - -
RAW 0 0 0
UDP 5 5 0
TCP 4 4 0
INET 9 9 0
FRAG 0 0 0
[root@localhost ~]#
說明:列出當前的established, closed, orphaned and waiting TCP sockets
實例3:列出所有打開的網路連接埠
命令:ss -l
輸出:
代碼如下:
[root@localhost ~]# ss -l
Recv-Q Send-Q Local Address:Port Peer Address:Port
0 0 127.0.0.1:smux *:*
0 0 *:3690 *:*
0 0 *:ssh *:*
[root@localhost ~]#
實例4:查看進程使用的socket
命令:ss -pl
輸出:
代碼如下:
[root@localhost ~]# ss -pl
Recv-Q Send-Q Local Address:Port Peer Address:Port
0 0 127.0.0.1:smux *:* users:((「snmpd」,2716,8))
0 0 *:3690 *:* users:((「svnserve」,3590,3))
0 0 *:ssh *:* users:((「sshd」,2735,3))
[root@localhost ~]#
實例5:找出打開套接字/埠應用程序
命令:ss -lp | grep 3306
輸出:
代碼如下:
[root@localhost ~]# ss -lp|grep 1935
0 0 *:1935 *:* users:((「fmsedge」,2913,18))
0 0 127.0.0.1:19350 *:* users:((「fmsedge」,2913,17))
[root@localhost ~]# ss -lp|grep 3306
0 0 *:3306 *:* users:((「mysqld」,2871,10))
[root@localhost ~]#
實例6:顯示所有UDP Sockets
命令:ss -u -a
輸出:
代碼如下:
[root@localhost ~]# ss -u -a
State Recv-Q Send-Q Local Address:Port Peer Address:Port
UNCONN 0 0 127.0.0.1:syslog *:*
UNCONN 0 0 *:snmp *:*
ESTAB 0 0 192.168.120.203:39641 10.58.119.119:domain
[root@localhost ~]#
實例7:顯示所有狀態為established的SMTP連接
命令:ss -o state established 『( dport = :smtp or sport = :smtp )』
輸出:
代碼如下:
[root@localhost ~]# ss -o state established 『( dport = :smtp or sport = :smtp )』
Recv-Q Send-Q Local Address:Port Peer Address:Port
[root@localhost ~]#
實例8:顯示所有狀態為Established的HTTP連接
命令:ss -o state established 『( dport = :http or sport = :http )』
輸出:
代碼如下:
[root@localhost ~]# ss -o state established 『( dport = :http or sport = :http )』
Recv-Q Send-Q Local Address:Port Peer Address:Port
0 0 75.126.153.214:2164 192.168.10.42:http
[root@localhost ~]#
實例9:列舉出處於 FIN-WAIT-1狀態的源埠為 80或者 443,目標網路為 193.233.7/24所有 tcp套接字
命令:ss -o state fin-wait-1 『( sport = :http or sport = :https )』 dst 193.233.7/24
實例10:用TCP 狀態過濾Sockets:
命令:
代碼如下:
ss -4 state FILTER-NAME-HERE
ss -6 state FILTER-NAME-HERE
輸出:
代碼如下:
[root@localhost ~]#ss -4 state closing
Recv-Q Send-Q Local Address:Port Peer Address:Port
1 11094 75.126.153.214:http 192.168.10.42:4669
說明:
FILTER-NAME-HERE 可以代表以下任何一個:
代碼如下:
established
syn-sent
syn-recv
fin-wait-1
fin-wait-2
time-wait
closed
close-wait
last-ack
listen
closing
all : 所有以上狀態
connected : 除了listen and closed的所有狀態
synchronized :所有已連接的狀態除了syn-sent
bucket : 顯示狀態為maintained as minisockets,如:time-wait和syn-recv.
big : 和bucket相反。
實例11:匹配遠程地址和埠號
命令:
代碼如下:
ss dst ADDRESS_PATTERN
ss dst 192.168.1.5
ss dst 192.168.119.113:http
ss dst 192.168.119.113:smtp
ss dst 192.168.119.113:443
輸出:
代碼如下:
[root@localhost ~]# ss dst 192.168.119.113
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 0 192.168.119.103:16014 192.168.119.113:20229
ESTAB 0 0 192.168.119.103:16014 192.168.119.113:61056
ESTAB 0 0 192.168.119.103:16014 192.168.119.113:61623
ESTAB 0 0 192.168.119.103:16014 192.168.119.113:60924
ESTAB 0 0 192.168.119.103:16050 192.168.119.113:43701
ESTAB 0 0 192.168.119.103:16073 192.168.119.113:32930
ESTAB 0 0 192.168.119.103:16073 192.168.119.113:49318
ESTAB 0 0 192.168.119.103:16014 192.168.119.113:3844
[root@localhost ~]# ss dst 192.168.119.113:http
State Recv-Q Send-Q Local Address:Port Peer Address:Port
[root@localhost ~]# ss dst 192.168.119.113:3844
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 0 192.168.119.103:16014 192.168.119.113:3844
[root@localhost ~]#
實例12:匹配本地地址和埠號
命令:
代碼如下:
ss src ADDRESS_PATTERN
ss src 192.168.119.103
ss src 192.168.119.103:http
ss src 192.168.119.103:80
ss src 192.168.119.103:smtp
ss src 192.168.119.103:25
輸出:
代碼如下:
[root@localhost ~]# ss src 192.168.119.103:16021
State Recv-Q Send-Q Local Address:Port Peer Address:Port
ESTAB 0 0 192.168.119.103:16021 192.168.119.201:63054
ESTAB 0 0 192.168.119.103:16021 192.168.119.201:62894
ESTAB 0 0 192.168.119.103:16021 192.168.119.201:63055
ESTAB 0 0 192.168.119.103:16021 192.168.119.201:2274
ESTAB 0 0 192.168.119.103:16021 192.168.119.201:44784
ESTAB 0 0 192.168.119.103:16021 192.168.119.201:7233
ESTAB 0 0 192.168.119.103:16021 192.168.119.103:58660
ESTAB 0 0 192.168.119.103:16021 192.168.119.201:44822
ESTAB 0 0 192.168.119.103:16021 10.2.1.206:56737
ESTAB 0 0 192.168.119.103:16021 10.2.1.206:57487
ESTAB 0 0 192.168.119.103:16021 10.2.1.206:56736
ESTAB 0 0 192.168.119.103:16021 10.2.1.206:64652
ESTAB 0 0 192.168.119.103:16021 10.2.1.206:56586
ESTAB 0 0 192.168.119.103:16021 10.2.1.206:64653
ESTAB 0 0 192.168.119.103:16021 10.2.1.206:56587
[root@localhost ~]#
實例13:將本地或者遠程埠和一個數比較
命令:
代碼如下:
ss dport OP PORT
ss sport OP PORT
輸出:
代碼如下:
[root@localhost ~]# ss sport = :http
[root@localhost ~]# ss dport = :http
[root@localhost ~]# ss dport \》 :1024
[root@localhost ~]# ss sport \》 :1024
[root@localhost ~]# ss sport \《 :32000
[root@localhost ~]# ss sport eq :22
[root@localhost ~]# ss dport != :22
[root@localhost ~]# ss state connected sport = :http
[root@localhost ~]# ss \( sport = :http or sport = :https \)
[root@localhost ~]# ss -o state fin-wait-1 \( sport = :http or sport = :https \) dst 192.168.1/24
說明:
ss dport OP PORT 遠程埠和一個數比較;ss sport OP PORT 本地埠和一個數比較。
OP 可以代表以下任意一個:
《= or le : 小於或等於埠號
》= or ge : 大於或等於埠號
== or eq : 等於埠號
!= or ne : 不等於埠號
《 or gt : 小於埠號
》 or lt : 大於埠號
實例14:ss 和 netstat 效率對比
命令:
代碼如下:
time netstat -at
time ss
輸出:
代碼如下:
[root@localhost ~]# time ss
real 0m0.739s
user 0m0.019s
sys 0m0.013s
[root@localhost ~]#
[root@localhost ~]# time netstat -at
real 2m45.907s
user 0m0.063s
sys 0m0.067s
[root@localhost ~]#
說明:
用time 命令分別獲取通過netstat和ss命令獲取程序和概要佔用資源所使用的時間。在伺服器連接數比較多的時候,netstat的效率完全沒法和ss比。
㈦ Linux下Socket編程 怎樣實現客戶端之間互相通信
網路的Socket數據傳輸是一種特殊的I/O,Socket也是一種文件描述符。Socket也具有一個類似於打開文件的函數調用Socket(),該函數返回一個整型的Socket描述符,隨後的連接建立、數據傳輸等操作都是通過該Socket實現的。
下面用Socket實現一個windows下的c語言socket通信例子,這里我們客戶端傳遞一個字元串,伺服器端進行接收。
【伺服器端】
#include"stdafx.h"
#include<stdio.h>
#include<winsock2.h>
#include<winsock2.h>
#defineSERVER_PORT5208//偵聽埠
voidmain()
{
WORDwVersionRequested;
WSADATAwsaData;
intret,nLeft,length;
SOCKETsListen,sServer;//偵聽套接字,連接套接字
structsockaddr_insaServer,saClient;//地址信息
char*ptr;//用於遍歷信息的指針
//WinSock初始化
wVersionRequested=MAKEWORD(2,2);//希望使用的WinSockDLL的版本
ret=WSAStartup(wVersionRequested,&wsaData);
if(ret!=0)
{
printf("WSAStartup()failed! ");
return;
}
//創建Socket,使用TCP協議
sListen=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sListen==INVALID_SOCKET)
{
WSACleanup();
printf("socket()faild! ");
return;
}
//構建本地地址信息
saServer.sin_family=AF_INET;//地址家族
saServer.sin_port=htons(SERVER_PORT);//注意轉化為網路位元組序
saServer.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//使用INADDR_ANY指示任意地址
//綁定
ret=bind(sListen,(structsockaddr*)&saServer,sizeof(saServer));
if(ret==SOCKET_ERROR)
{
printf("bind()faild!code:%d ",WSAGetLastError());
closesocket(sListen);//關閉套接字
WSACleanup();
return;
}
//偵聽連接請求
ret=listen(sListen,5);
if(ret==SOCKET_ERROR)
{
printf("listen()faild!code:%d ",WSAGetLastError());
closesocket(sListen);//關閉套接字
return;
}
printf("Waitingforclientconnecting! ");
printf("Tips:Ctrl+ctoquit! ");
//阻塞等待接受客戶端連接
while(1)//循環監聽客戶端,永遠不停止,所以,在本項目中,我們沒有心跳包。
{
length=sizeof(saClient);
sServer=accept(sListen,(structsockaddr*)&saClient,&length);
if(sServer==INVALID_SOCKET)
{
printf("accept()faild!code:%d ",WSAGetLastError());
closesocket(sListen);//關閉套接字
WSACleanup();
return;
}
charreceiveMessage[5000];
nLeft=sizeof(receiveMessage);
ptr=(char*)&receiveMessage;
while(nLeft>0)
{
//接收數據
ret=recv(sServer,ptr,5000,0);
if(ret==SOCKET_ERROR)
{
printf("recv()failed! ");
return;
}
if(ret==0)//客戶端已經關閉連接
{
printf("Clienthasclosedtheconnection ");
break;
}
nLeft-=ret;
ptr+=ret;
}
printf("receivemessage:%s ",receiveMessage);//列印我們接收到的消息。
}
//closesocket(sListen);
//closesocket(sServer);
//WSACleanup();
}
【客戶端】
#include"stdafx.h"
#include<stdio.h>
#include<stdlib.h>
#include<winsock2.h>
#defineSERVER_PORT5208//偵聽埠
voidmain()
{
WORDwVersionRequested;
WSADATAwsaData;
intret;
SOCKETsClient;//連接套接字
structsockaddr_insaServer;//地址信息
char*ptr;
BOOLfSuccess=TRUE;
//WinSock初始化
wVersionRequested=MAKEWORD(2,2);//希望使用的WinSockDLL的版本
ret=WSAStartup(wVersionRequested,&wsaData);
if(ret!=0)
{
printf("WSAStartup()failed! ");
return;
}
//確認WinSockDLL支持版本2.2
if(LOBYTE(wsaData.wVersion)!=2||HIBYTE(wsaData.wVersion)!=2)
{
WSACleanup();
printf("InvalidWinSockversion! ");
return;
}
//創建Socket,使用TCP協議
sClient=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sClient==INVALID_SOCKET)
{
WSACleanup();
printf("socket()failed! ");
return;
}
//構建伺服器地址信息
saServer.sin_family=AF_INET;//地址家族
saServer.sin_port=htons(SERVER_PORT);//注意轉化為網路節序
saServer.sin_addr.S_un.S_addr=inet_addr("192.168.1.127");
//連接伺服器
ret=connect(sClient,(structsockaddr*)&saServer,sizeof(saServer));
if(ret==SOCKET_ERROR)
{
printf("connect()failed! ");
closesocket(sClient);//關閉套接字
WSACleanup();
return;
}
charsendMessage[]="hellothisisclientmessage!";
ret=send(sClient,(char*)&sendMessage,sizeof(sendMessage),0);
if(ret==SOCKET_ERROR)
{
printf("send()failed! ");
}
else
printf("clientinfohasbeensent!");
closesocket(sClient);//關閉套接字
WSACleanup();
}
㈧ linux socket 怎麼處理大量的數據
1、 引言
Linux的興起可以說是Internet創造的一個奇跡。Linux作為一個完全開放其原代碼的免費的自由軟體,兼容了各種UNIX標准(如POSIX、UNIX System V 和 BSD UNIX 等)的多用戶、多任務的具有復雜內核的操作系統。在中國,隨著Internet的普及,一批主要以高等院校的學生和ISP的技術人員組成的Linux愛好者隊伍已經蓬勃成長起來。越來越多的編程愛好者也逐漸酷愛上這個優秀的自由軟體。本文介紹了Linux下Socket的基本概念和函數調用。
2、 什麼是Socket
Socket(套接字)是通過標準的UNIX文件描述符和其它程序通訊的一個方法。每一個套接字都用一個半相關描述:{協議,本地地址、本地埠}來表示;一個完整的套接字則用一個相關描述:{協議,本地地址、本地埠、遠程地址、遠程埠},每一個套接字都有一個本地的由操作系統分配的唯一的套接字型大小。
3、 Socket的三種類型
(1) 流式Socket(SOCK_STREAM)
流式套接字提供可靠的、面向連接的通信流;它使用TCP協議,從而保證了數據傳輸的正確性和順序的。
(2) 數據報Socket(SOCK_DGRAM)
數據報套接字定義了一種無連接的服務,數據通過相互獨立的報文進行傳輸,是無序的,並且不保證可靠、無差錯。它使用數據報協議UDP
(3) 原始Socket
原始套接字允許對底層協議如IP或ICMP直接訪問,它功能強大但使用較為不便,主要用於一些協議的開發。
4、 利用套接字發送數據
1、 對於流式套接字用系統調用send()來發送數據。
2、 對於數據報套接字,則需要自己先加一個信息頭,然後調用sendto()函數把數據發送出去。
5、 Linux中Socket的數據結構
(1) struct sockaddr { //用於存儲套接字地址
unsigned short sa_family;//地址類型
char sa_data[14]; //14位元組的協議地址
};
(2) struct sockaddr_in{ //in 代表internet
short int sin_family; //internet協議族
unsigned short int sin_port;//埠號,必須是網路位元組順序
struct in_addr sin_addr;//internet地址,必須是網路位元組順序
unsigned char sin_zero;//添0(和struct sockaddr一樣大小
};
(3) struct in_addr{
unsigned long s_addr;
};
6、 網路位元組順序及其轉換函數
(1) 網路位元組順序
每一台機器內部對變數的位元組存儲順序不同,而網路傳輸的數據是一定要統一順序的。所以對內部位元組表示順序與網路位元組順序不同的機器,一定要對數據進行轉換,從程序的可移植性要求來講,就算本機的內部位元組表示順序與網路位元組順序相同也應該在傳輸數據以前先調用數據轉換函數,以便程序移植到其它機器上後能正確執行。真正轉換還是不轉換是由系統函數自己來決定的。
(2) 有關的轉換函數
* unsigned short int htons(unsigned short int hostshort):
主機位元組順序轉換成網路位元組順序,對無符號短型進行操作4bytes
* unsigned long int htonl(unsigned long int hostlong):
主機位元組順序轉換成網路位元組順序,對無符號長型進行操作8bytes
* unsigned short int ntohs(unsigned short int netshort):
網路位元組順序轉換成主機位元組順序,對無符號短型進行操作4bytes
* unsigned long int ntohl(unsigned long int netlong):
網路位元組順序轉換成主機位元組順序,對無符號長型進行操作8bytes
註:以上函數原型定義在netinet/in.h里
7、 IP地址轉換
有三個函數將數字點形式表示的字元串IP地址與32位網路位元組順序的二進制形式的IP地址進行轉換
(1) unsigned long int inet_addr(const char * cp):該函數把一個用數字和點表示的IP地址的字元串轉換成一個無符號長整型,如:struct sockaddr_in ina
ina.sin_addr.s_addr=inet_addr("202.206.17.101")
該函數成功時:返回轉換結果;失敗時返回常量INADDR_NONE,該常量=-1,二進制的無符號整數-1相當於255.255.255.255,這是一個廣播地址,所以在程序中調用iner_addr()時,一定要人為地對調用失敗進行處理。由於該函數不能處理廣播地址,所以在程序中應該使用函數inet_aton()。
(2)int inet_aton(const char * cp,struct in_addr * inp):此函數將字元串形式的IP地址轉換成二進制形式的IP地址;成功時返回1,否則返回0,轉換後的IP地址存儲在參數inp中。
(3) char * inet_ntoa(struct in-addr in):將32位二進制形式的IP地址轉換為數字點形式的IP地址,結果在函數返回值中返回,返回的是一個指向字元串的指針。
8、 位元組處理函數
Socket地址是多位元組數據,不是以空字元結尾的,這和C語言中的字元串是不同的。Linux提供了兩組函數來處理多位元組數據,一組以b(byte)開頭,是和BSD系統兼容的函數,另一組以mem(內存)開頭,是ANSI C提供的函數。
以b開頭的函數有:
(1) void bzero(void * s,int n):將參數s指定的內存的前n個位元組設置為0,通常它用來將套接字地址清0。
(2) void b(const void * src,void * dest,int n):從參數src指定的內存區域拷貝指定數目的位元組內容到參數dest指定的內存區域。
(3) int bcmp(const void * s1,const void * s2,int n):比較參數s1指定的內存區域和參數s2指定的內存區域的前n個位元組內容,如果相同則返回0,否則返回非0。
註:以上函數的原型定義在strings.h中。
以mem開頭的函數有:
(1) void * memset(void * s,int c,size_t n):將參數s指定的內存區域的前n個位元組設置為參數c的內容。
(2) void * memcpy(void * dest,const void * src,size_t n):功能同b(),區別:函數b()能處理參數src和參數dest所指定的區域有重疊的情況,memcpy()則不能。
(4) int memcmp(const void * s1,const void * s2,size_t n):比較參數s1和參數s2指定區域的前n個位元組內容,如果相同則返回0,否則返回非0。
註:以上函數的原型定義在string.h中。
9、 基本套接字函數
(1) socket()
#include< sys/types.h>
#include< sys/socket.h>
int socket(int domain,int type,int protocol)
參數domain指定要創建的套接字的協議族,可以是如下值:
AF_UNIX //UNIX域協議族,本機的進程間通訊時使用
AF_INET //Internet協議族(TCP/IP)
AF_ISO //ISO協議族
參數type指定套接字類型,可以是如下值:
SOCK_STREAM //流套接字,面向連接的和可靠的通信類型
SOCK_DGRAM //數據報套接字,非面向連接的和不可靠的通信類型
SOCK_RAW //原始套接字,只對Internet協議有效,可以用來直接訪問IP協議
參數protocol通常設置成0,表示使用默認協議,如Internet協議族的流套接字使用TCP協議,而數據報套接字使用UDP協議。當套接字是原始套接字類型時,需要指定參數protocol,因為原始套接字對多種協議有效,如ICMP和IGMP等。
Linux系統中創建一個套接字的操作主要是:在內核中創建一個套接字數據結構,然後返回一個套接字描述符標識這個套接字數據結構。這個套接字數據結構包含連接的各種信息,如對方地址、TCP狀態以及發送和接收緩沖區等等,TCP協議根據這個套接字數據結構的內容來控制這條連接。
(2) 函數connect()
#include< sys/types.h>
#include< sys/socket.h>
int connect(int sockfd,struct sockaddr * servaddr,int addrlen)
參數sockfd是函數socket返回的套接字描述符;參數servaddr指定遠程伺服器的套接字地址,包括伺服器的IP地址和埠號;參數addrlen指定這個套接字地址的長度。成功時返回0,否則返回-1,並設置全局變數為以下任何一種錯誤類型:ETIMEOUT、ECONNREFUSED、EHOSTUNREACH或ENETUNREACH。
在調用函數connect之前,客戶機需要指定伺服器進程的套接字地址。客戶機一般不需要指定自己的套接字地址(IP地址和埠號),系統會自動從1024至5000的埠號范圍內為它選擇一個未用的埠號,然後以這個埠號和本機的IP地址填充這個套接字地址。
客戶機調用函數connect來主動建立連接。這個函數將啟動TCP協議的3次握手過程。在建立連接之後或發生錯誤時函數返回。連接過程可能出現的錯誤情況有:
(1) 如果客戶機TCP協議沒有接收到對它的SYN數據段的確認,函數以錯誤返回,錯誤類型為ETIMEOUT。通常TCP協議在發送SYN數據段失敗之後,會多次發送SYN數據段,在所有的發送都高中失敗之後,函數以錯誤返回。
註:SYN(synchronize)位:請求連接。TCP用這種數據段向對方TCP協議請求建立連接。在這個數據段中,TCP協議將它選擇的初始序列號通知對方,並且與對方協議協商最大數據段大小。SYN數據段的序列號為初始序列號,這個SYN數據段能夠被確認。當協議接收到對這個數據段的確認之後,建立TCP連接。
(2) 如果遠程TCP協議返回一個RST數據段,函數立即以錯誤返回,錯誤類型為ECONNREFUSED。當遠程機器在SYN數據段指定的目的埠號處沒有服務進程在等待連接時,遠程機器的TCP協議將發送一個RST數據段,向客戶機報告這個錯誤。客戶機的TCP協議在接收到RST數據段後不再繼續發送SYN數據段,函數立即以錯誤返回。
註:RST(reset)位:表示請求重置連接。當TCP協議接收到一個不能處理的數據段時,向對方TCP協議發送這種數據段,表示這個數據段所標識的連接出現了某種錯誤,請求TCP協議將這個連接清除。有3種情況可能導致TCP協議發送RST數據段:(1)SYN數據段指定的目的埠處沒有接收進程在等待;(2)TCP協議想放棄一個已經存在的連接;(3)TCP接收到一個數據段,但是這個數據段所標識的連接不存在。接收到RST數據段的TCP協議立即將這條連接非正常地斷開,並向應用程序報告錯誤。
(3) 如果客戶機的SYN數據段導致某個路由器產生「目的地不可到達」類型的ICMP消息,函數以錯誤返回,錯誤類型為EHOSTUNREACH或ENETUNREACH。通常TCP協議在接收到這個ICMP消息之後,記錄這個消息,然後繼續幾次發送SYN數據段,在所有的發送都告失敗之後,TCP協議檢查這個ICMP消息,函數以錯誤返回。
註:ICMP:Internet 消息控制協議。Internet的運行主要是由Internet的路由器來控制,路由器完成IP數據包的發送和接收,如果發送數據包時發生錯誤,路由器使用ICMP協議來報告這些錯誤。ICMP數據包是封裝在IP數據包的數據部分中進行傳輸的,其格式如下:
類型
碼
校驗和
數據
0 8 16 24 31
類型:指出ICMP數據包的類型。
代碼:提供ICMP數據包的進一步信息。
校驗和:提供了對整個ICMP數據包內容的校驗和。
ICMP數據包主要有以下類型:
(1) 目的地不可到達:A、目的主機未運行;B、目的地址不存在;C、路由表中沒有目的地址對應的條目,因而路由器無法找到去往目的主機的路由。
(2) 超時:路由器將接收到的IP數據包的生存時間(TTL)域減1,如果這個域的值變為0,路由器丟棄這個IP數據包,並且發送這種ICMP消息。
(3) 參數出錯:當IP數據包中有無效域時發送。
(4) 重定向:將一條新的路徑通知主機。
(5) ECHO請求、ECHO回答:這兩條消息用語測試目的主機是否可以到達。請求者向目的主機發送ECHO請求ICMP數據包,目的主機在接收到這個ICMP數據包之後,返回ECHO回答ICMP數據包。
(6) 時戳請求、時戳回答:ICMP協議使用這兩種消息從其他機器處獲得其時鍾的當前時間。
調用函數connect的過程中,當客戶機TCP協議發送了SYN數據段的確認之後,TCP狀態由CLOSED狀態轉為SYN_SENT狀態,在接收到對SYN數據段的確認之後,TCP狀態轉換成ESTABLISHED狀態,函數成功返回。如果調用函數connect失敗,應該用close關閉這個套接字描述符,不能再次使用這個套接字描述符來調用函數connect。
註:TCP協議狀態轉換圖:
被動OPEN CLOSE 主動OPEN
(建立TCB) (刪除TCB) (建立TCB,
發送SYN)
接收SYN SEND
(發送SYN,ACK) (發送SYN)
接收SYN的ACK(無動作)
接收SYN的ACK 接收SYN,ACK
(無動作) (發送ACK)
CLOSE
(發送FIN) CLOSE 接收FIN
(發送FIN) (發送FIN)
接收FIN
接收FIN的ACK(無動作) (發送ACK) CLOSE(發送FIN)
接收FIN 接收FIN的ACK 接收FIN的ACK
(發送ACK) (無動作) (無動作)
2MSL超時(刪除TCB)
(3) 函數bind()
函數bind將本地地址與套接字綁定在一起,其定義如下:
#include< sys/types.h>
#include< sys/socket.h>
int bind(int sockfd,struct sockaddr * myaddr,int addrlen);
參數sockfd是函數sockt返回的套接字描述符;參數myaddr是本地地址;參數addrlen是套接字地址結構的長度。執行成功時返回0,否則,返回-1,並設置全局變數errno為錯誤類型EADDRINUSER。
伺服器和客戶機都可以調用函數bind來綁定套接字地址,但一般是伺服器調用函數bind來綁定自己的公認埠號。綁定操作一般有如下幾種組合方式:
表1
程序類型
IP地址
埠號
說明
伺服器
INADDR_ANY
非零值
指定伺服器的公認埠號
伺服器
本地IP地址
非零值
指定伺服器的IP地址和公認埠號
客戶機
INADDR_ANY
非零值
指定客戶機的連接埠號
客戶機
本地IP地址
非零值
指定客戶機的IP地址連接埠號
客戶機
本地IP地址
零
指定客戶機的IP地址
分別說明如下:
(1) 伺服器指定套接字地址的公認埠號,不指定IP地址:即伺服器調用bind時,設置套接字的IP地址為特殊的INADDE-ANY,表示它願意接收來自任何網路設備介面的客戶機連接。這是伺服器最常用的綁定方式。
(2) 伺服器指定套接字地址的公認埠號和IP地址:伺服器調用bind時,如果設置套接字的IP地址為某個本地IP地址,這表示這台機器只接收來自對應於這個IP地址的特定網路設備介面的客戶機連接。當伺服器有多塊網卡時,可以用這種方式來限制伺服器的接收范圍。
(3) 客戶機指定套接字地址的連接埠號:一般情況下,客戶機調用connect函數時不用指定自己的套接字地址的埠號。系統會自動為它選擇一個未用的埠號,並且用本地的IP地址來填充套接字地址中的相應項。但有時客戶機需要使用一個特定的埠號(比如保留埠號),而系統不會未客戶機自動分配一個保留埠號,所以需要調用函數bind來和一個未用的保留埠號綁定。
(4) 指定客戶機的IP地址和連接埠號:表示客戶機使用指定的網路設備介面和埠號進行通信。
(5) 指定客戶機的IP地址:表示客戶機使用指定的網路設備介面和埠號進行通信,系統自動為客戶機選一個未用的埠號。一般只有在主機有多個網路設備介面時使用。
我們一般不在客戶機上使用固定的客戶機埠號,除非是必須使用的情況。在客戶機上使用固定的埠號有以下不利:
(1) 伺服器執行主動關閉操作:伺服器最後進入TIME_WAIT狀態。當客戶機再次與這個伺服器進行連接時,仍使用相同的客戶機埠號,於是這個連接與前次連接的套接字對完全一樣,但是一呢、為前次連接處於TIME_WAIT狀態,並未消失,所以這次連接請求被拒絕,函connect以錯誤返回,錯誤類型為ECONNREFUSED
(2) 客戶機執行主動關閉操作:客戶機最後進入TIME_WAIT狀態。當馬上再次執行這個客戶機程序時,客戶機將繼續與這個固定客戶機埠號綁定,但因為前次連接處於TIME_WAIT狀態,並未消失,系統會發現這個埠號仍被佔用,所以這次綁定操作失敗,函數bind以錯誤返回,錯誤類型為EADDRINUSE。
(4) 函數listen()
函數listen將一個套接字轉換為征聽套接字,定義如下;
#include< sys/socket,h>
int listen(int sockfd,int backlog)
參數sockfd指定要轉換的套接字描述符;參數backlog設置請求隊列的最大長度;執行成功時返回0, 否則返回-1。函數listen功能有兩個:
(1) 將一個尚未連接的主動套接字(函數socket創建的可以用來進行主動連接但不能接受連接請求的套接字)轉換成一個被動連接套接字。執行listen之後,伺服器的TCP狀態由CLOSED轉為LISTEN狀態。
(2) TCP協議將到達的連接請求隊列,函數listen的第二個參數指定這個隊列的最大長度。
註:參數backlog的作用:
TCP協議為每一個征聽套接字維護兩個隊列:
(1) 未完成連接隊列:每個尚未完成3次握手操作的TCP連接在這個隊列中佔有一項。TCP希望儀在接收到一個客戶機SYN數據段之後,在這個隊列中創建一個新條目,然後發送對客戶機SYN數據段的確認和自己的SYN數據段(ACK+SYN數據段),等待客戶機對自己的SYN數據段的確認。此時,套接字處於SYN_RCVD狀態。這個條目將保存在這個隊列中,直到客戶機返回對SYN數據段的確認或者連接超時。
(2) 完成連接隊列:每個已經完成3次握手操作,但尚未被應用程序接收(調用函數accept)的TCP連接在這個隊列中佔有一項。當一個在未完成連接隊列中的連接接收到對SYN數據段的確認之後,完成3次握手操作,TCP協議將它從未完成連接隊列移到完成連接隊列中。此時,套接字處於ESTABLISHED狀態。這個條目將保存在這個隊列中,直到應用程序調用函數accept來接收它。
參數backlog指定某個征聽套接字的完成連接隊列的最大長度,表示這個套接字能夠接收的最大數目的未接收連接。如果當一個客戶機的SYN數據段到達時,征聽套接字的完成隊列已經滿了,那麼TCP協議將忽略這個SYN數據段。對於不能接收的SYN數據段,TCP協議不發送RST數據段,
(5) 函數accept()
函數accept從征聽套接字的完成隊列中接收一個已經建立起來的TCP連接。如果完成連接隊列為空,那麼這個進程睡眠。
#include< sys/socket.h>
int accept(int sockfd,struct sockaddr * addr,int * addrlen)
參數sockfd指定征聽套接字描述符;參數addr為指向一個Internet套接字地址結構的指針;參數addrlen為指向一個整型變數的指針。執行成功時,返回3個結果:函數返回值為一個新的套接字描述符,標識這個接收的連接;參數addr指向的結構變數中存儲客戶機地址;參數addrlen指向的整型變數中存儲客戶機地址的長度。失敗時返回-1。
征聽套接字專為接收客戶機連接請求,完成3次握手操作而用的,所以TCP協議不能使用征聽套接字描述符來標識這個連接,於是TCP協議創建一個新的套接字來標識這個要接收的連接,並將它的描述符發揮給應用程序。現在有兩個套接字,一個是調用函數accept時使用的征聽套接字,另一個是函數accept返回的連接套接字(connected socket)。一個伺服器通常只需創建一個征聽套接字,在伺服器進程的整個活動期間,用它來接收所有客戶機的連接請求,在伺服器進程終止前關閉這個征聽套接字;對於沒一個接收的(accepted)連接,TCP協議都創建一個新的連接套接字來標識這個連接,伺服器使用這個連接套接字與客戶機進行通信操作,當伺服器處理完這個客戶機請求時,關閉這個連接套接字。
當函數accept阻塞等待已經建立的連接時,如果進程捕獲到信號,函數將以錯誤返回,錯誤類型為EINTR。對於這種錯誤,一般重新調用函數accept來接收連接。
(6) 函數close()
函數close關閉一個套接字描述符。定義如下:
#include< unistd.h>
int close(int sockfd);
執行成功時返回0,否則返回-1。與操作文件描述符的close一樣,函數close將套接字描述符的引用計數器減1,如果描述符的引用計數大於0,則表示還有進程引用這個描述符,函數close正常返回;如果為0,則啟動清除套接字描述符的操作,函數close立即正常返回。
調用close之後,進程將不再能夠訪問這個套接字,但TCP協議將繼續使用這個套接字,將尚未發送的數據傳遞到對方,然後發送FIN數據段,執行關閉操作,一直等到這個TCP連接完全關閉之後,TCP協議才刪除該套接字。
(7) 函數read()和write()
用於從套接字讀寫數據。定義如下:
int read(int fd,char * buf,int len)
int write(int fd,char * buf,int len)
函數執行成功時,返回讀或寫的數據量的大小,失敗時返回-1。
每個TCP套接字都有兩個緩沖區:套接字發送緩沖區、套接字接收緩沖區,分別處理發送和接收任務。從網路讀、寫數據的操作是由TCP協議在內核中完成的:TCP協議將從網路上接收到的數據保存在相應套接字的接收緩沖區中,等待用戶調用函數將它們從接收緩沖區拷貝到用戶緩沖區;用戶將要發送的數據拷貝到相應套接字的發送緩沖區中,然後由TCP協議按照一定的演算法處理這些數據。
讀寫連接套接字的操作與讀寫文件的操作類似,也可以使用函數read和write。函數read完成將數據從套接字接收緩沖區拷貝到用戶緩沖區:當套接字接收緩沖區有數據可讀時,1:可讀數據量大於函數read指定值,返回函數參數len指定的數據量;2:了度數據量小於函數read指定值,函數read不等待請求的所有數據都到達,而是立即返回實際讀到的數據量;當無數據可讀時,函數read將阻塞不返回,等待數據到達。
當TCP協議接收到FIN數據段,相當於給讀操作一個文件結束符,此時read函數返回0,並且以後所有在這個套接字上的讀操作均返回0,這和普通文件中遇到文件結束符是一樣的。
當TCP協議接收到RST數據段,表示連接出現了某種錯誤,函數read將以錯誤返回,錯誤類型為ECONNERESET。並且以後所有在這個套接字上的讀操作均返回錯誤。錯誤返回時返回值小於0。
函數write完成將數據從用戶緩沖區拷貝到套接字發送緩沖區的任務:到套接字發送緩沖區有足夠拷貝所有用戶數據的空間時,函數write將數據拷貝到這個緩沖區中,並返回老輩的數量大小,如果可用空間小於write參數len指定的大小時,函數write將阻塞不返回,等待緩沖區有足夠的空間。
當TCP協議接收到RST數據段(當對方已經關閉了這條連接之後,繼續向這個套接字發送數據將導致對方TCP協議返回RST數據段),TCP協議接收到RST數據段時,函數write將以錯誤返回,錯誤類型為EINTR。以後可以繼續在這個套接字上寫數據。
(8) 函數getsockname()和getpeername()
函數getsockname返回套接字的本地地址;函數getpeername返回套接字對應的遠程地址。
10、 結束語
網路程序設計全靠套接字接收和發送信息。上文主要講述了Linux 下Socket的基本概念、Sockets API以及Socket所涉及到的TCP常識
㈨ 在linux中C語言socket怎麼將接收到的波形數據(十六進制的數據)存入到本地磁碟中,並按文件分級存放。
估計你是用的tcp socket,導致「videlord」網友說的情況:對於tcp socket,send與recv不是對等的,recv時只要緩沖有數據就會收上來。簡單說就是你send 4次,比如分別為10 10 10 10位元組,對端可以一次recv到這40位元組數據,也可以recv 40次、每次1位元組。
解決辦法有兩種:
改用udp socket,send/recv自然對等了
繼續用tcp socket,自己進行數據分段:比如自行約定在數據前約定4個位元組用於描述數據長度,這樣發送時,send 4+33位元組,send 4+35位元組;接收時,先獲取描述長度的4位元組獲得長度,再按照長度接收數據(可能需要多次recv湊齊指定長度)。