linux下的c socket編程(1)--簡介與client端的處理

一、介紹:服務器

socket是進程間的方式之一,是進程間的通訊。這裏說的進程並不必定是在同一臺機器上也有多是經過網絡鏈接的不一樣機器上。只要他們之間創建了socket鏈接,那麼數據即可以在機器之間進行雙向的交流,直到鏈接斷開爲止。網絡

二、socket的創建:dom

在咱們接觸實際的代碼API以前,咱們應該對基礎的鏈接方式有所瞭解。socket

1
2
3
4
5
6
7
NOTE left of server:創建一個正在被監聽的socket並等待客戶端的鏈接
NOTE right of client:創建一個客戶端socket並嘗試鏈接server
NOTE left of server:接受來自client的鏈接請求
server->client:發送與接受數據
client->server:接受與發送數據
NOTE left of server:關閉當前鏈接
NOTE right of client:關閉當前鏈接

上面就是基礎的鏈接方式。函數

一、首先server須要建立正在被監聽的socket,等待client鏈接請求。spa

二、client建立一個socket,嘗試鏈接server。unix

三、server接受client的請求,創建起來二者之間的鏈接。指針

四、數據交換,雙向通訊code

五、任何一方均可以斷開鏈接,斷開鏈接以後會自動銷燬。server

對於客戶端來講:

    一、經過系統函數socket()建立一個socket

    二、經過系統函數connect()向server端口socket發起請求

    三、交換數據,實現這種數據交換的方式有不少種,其中最簡單的就是使用系統函數read(),write()

對於服務端來講:

    一、經過系統調用函數sockcet()建立一個socket。

    二、經過系統函數bind()綁定到這個socket到server的一個端口上。

    三、經過系統函數listen()監聽這個socket

    四、當監聽到有一個請求來臨時,經過系統函數accept()接受一個請求。這個函數會阻塞io直到二者的鏈接徹底斷開。

    五、交換數據

socket的類型:

    當一個socket被創建起來時,進程間須要去說明所使用的協議和socket_type。只有通訊雙方都擁有相同的type和協議。

    目前普遍實用的協議有大類,分別是unix文件系統協議,internet網絡協議。對應的他們有各自的特色。使用unix_domain的雙方使用公共的文件系統進行通訊,使用internet_domain的進程分別位於不一樣的主機上,他們經過網絡進行通訊。

    使用unix_domain的socket地址本質上就是文件系統的一個記錄,自己是一條字符串。

    使用internrt_domain的socket包含兩個部分,一部分是主機的ip地址,一部分是socket綁定到的端口號。通常端口號比較低的端口都會被看成特殊的用途,好比端口號是80的端口是提供http服務的。

    目前普遍實用的socket類型有兩種,一種是流socket,一種是數據報socket。stream_socket處理通訊就像是處理流水同樣的接二連三的字節流,而datagram_sockets須要讀取完整的字符,一般一個字符有幾個字節組成。

    接下來的內容是創建在使用TCP協議的基礎上,這是一種可靠的面向流字節的協議,另一種協議是UDP協議,這是一種不可靠的面向字符的協議。

client端的簡單實例:

建立socket:

不論是server仍是client,第一步都是建立socket:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# include <stdio.h>
# include <stdlib.h>
# include <sys/socket.h>
# include <sys/types.h>
 
int  main(  int  argc, char * argv[])
{
     int  socket_desc;
     socket_desc = socket(AF_INET,SOCK_STREAM, 0 );
 
     if (- 1 ==socket_desc)
{
     perror( "cannot create socket!\n" );
     exit( 1 );
}
}

socket()函數建立一個socket而且返回一個對應的描述符。

其對應的參數分別爲:

AF_INET->ipv4

SOCK_STREAM->流socket

PROCOTOL 協議,使用ip協議

發起鏈接:

咱們經過ip地址和端口號去鏈接遠程主機,爲此咱們須要建立正確的結構體去保存遠程主機的基本信息,從而表示遠程主機。

1
struct sockaddr_in server;

sockaddr_in 是一個包含網絡地址的結構體,下面的是定義:

1
2
3
4
5
6
7
8
9
10
11
struct sockaddr_in
{
short sin_family'
unsigned short sin_[ort;
struct in_addr sin_addr;
char sin_zero[ 8 ];
};
struct in_addr
{
unsigned long s_addr;
};

能夠看獲得,這個結構體中還有一種類型爲in_addr,其內部1的結構知識一個long類型的數據。ip地址便保存在這個long類型中。

函數inet_addr()能夠很方便的將ip地址轉換爲long類型的格式。

1
server.sin_addr.s_addr = inet_addr( "127.0.0.1" );

既然知道了這個遠程主機的server地址,那麼接下來的就是發起鏈接了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# include <stdio.h>
# include <stdlib.h>
# include <sys/socket.h>
#inlcude<sys/types.h>
# include <netinet/ in .h>
# include <arpa/inet.h>
int  main()
{
int  socket_desc;
struct sockaddr_in server;
//建立socket
socket_desc= socket(AF_INET,SOCK_STREAM, 0 );
if (- 1 ==socket_desc)
{
perror( "cannort cerate socket" );
exit( 1 );
}
//設置遠程服務器的信息
 
server.sin_family=AF_INER;
server.sin_port=htons( 80 );
server.sin_addr.s_addr = inet_addr( "127.0.0.1" );
 
//鏈接:
 
if (connect(socket_desc,(struct sockaddr*)&server,sizeof(server))< 0 )
{
perror( "counld not connect!\n" );
return  0 ;
}
//當服務器接受連接是便會創建鏈接
 
printf( "connct success\n" );
return  0 ;
}

connect()函數回想服務器發起請求創建一個鏈接。

其參數爲:

一、int sockfd =>socket 描述符

二、const struct sockaddr* addr =>sockaddr 的結構體,通用的socket地址。

三、socklen_taddrlen =>socket描述符的長度。

struct sockaddr 是通用的套接字地址,而struct sockaddr_in 則是internet環境下套接字地執形式,兩者長度同樣都是16個字節。兩者是並列結構,指向sockaddr_in 結構的指針也能夠指向sockaddr。通常狀況下,須要把sockaddr_in 結構強制轉換成sockaddr結構在傳入系統調用函數中。更多見connect()。

另外,代碼中htons()函數的做用是將主機的數據轉化爲網絡字節序,至於爲真麼要轉換數據的字節順序,這裏就先不說了,能夠本身去了解。

    到了這裏,咱們不只建立了socket並且已經成功的鏈接了服務器。下面就是向服務器通訊的過程了。

在socket上發送數據:

函數send()實現發送數據的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# include <sys/socket.h>
# include <arpa/inet.h>
# include <netinet/ in .h>
int  main()
{
int  sock_desc;
struct sockaddr_in server;
char *message;
sock_desc=socket(AF_INET,SOCK_STREAM, 0 );
if (- 1 ==sock_desc)
{
perror( "cannot create socket\n" );
exit( 1 );
}
server.sinn_family=AF_INET;
server.sin_port=htons( 80 );
server.sin_addr.s_addr=inet_addr( "127.0.0.1" );
if (connect(sock_desc,(struct sockaddr*)&server,sizeof(server))< 0 )
{
perror( "connect error\n" );
return  1 ;
}
printf( "connect successed\n" );
message= "hello world\n" ;
if (send(sock_desc,message,strlen(message), 0 )< 0 )
{
printf( "send error\n" );
return  2 ;
}
printf( "message send success\n" );
return  0 ;
}

send()函數的實現是向服務器發送數據,他其實就是向socket寫數據,相似的就像是向文件中寫入數據。

其參數爲:

一、int sockfd=>指定發送數據的socket描述符

二、const void * buff=>發送數據

三、size_t nbytes=>發送數據的長度

四、int flags=>標誌
到如今爲止,咱們已經完成了client的大部分操做,已經可以向對方發送數據,那麼接下來就是接受服務端的返回的數據了。

經過socket接收數據:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# include <sys/socket.h>
# include <arpa/inet.h>
# include <netinet/ in .h>
int  main()
{
int  sock_desc;
struct sockaddr_in server;
char *message;
sock_desc=socket(AF_INET,SOCK_STREAM, 0 );
if (- 1 ==sock_desc)
{
perror( "cannot create socket\n" );
exit( 1 );
}
server.sinn_family=AF_INET;
server.sin_port=htons( 80 );
server.sin_addr.s_addr=inet_addr( "127.0.0.1" );
if (connect(sock_desc,(struct sockaddr*)&server,sizeof(server))< 0 )
{
perror( "connect error\n" );
return  1 ;
}
printf( "connect successed\n" );
message= "hello world\n" ;
if (send(sock_desc,message,strlen(message), 0 )< 0 )
{
printf( "send error\n" );
return  2 ;
}
printf( "message send success\n" );
//接收數據
if (recv(sock_desc,server_reply, 2000 , 0 )< 0 )
{
perror( "recv failed\n" );
return  3 ;
}
printf( "recv seccessed\n" );
puts(server_reply);
return  0 ;
}

recv()函數就是爲了接受socket的數據,其參數爲:

一、int sockfd=>接收端的socket描述符

二、void * buff => 存放數據的緩衝區,數據存放在* buff中。

三、size_t nbytes=> 知名buff的長度。

四、int flags=> 通常設置爲0

至此,咱們完成了,socket通訊的主要流程,成功的拿到了服務端返回的數據,在上方通訊完成以後,即可以將此socket鏈接關閉。

關閉socket:

close(sock_desc);

client端口的總結:

 因此經過上面的討論,最終的所有代碼是:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#inlcude<stdio.h>
# include <string.h>
# include <stdlib.h>
# include <sys/types.h>
# include <sys/socket.h>
# include <arpa/inet.h>
# include <netinet/ in .h>
#Include<unistd.h>
int  main()
{
int  socket_desc;
struct sockaddr_in server;
char * message,server_reply[ 2000 ];
 
//建立socket
socket_desc = socket(AF_INET,SOCK_STREAM, 0 );
if (- 1 ==socket_desc)
{
perror( "socket failed\n" );
exit( 1 );
}
server.sin_family=AF_INET;
server.sin_port=htons( 80 );
server.sin_addr.s_addr=inet_addr( "127.0.0.1" );
 
//進行鏈接:
 
if (connect(socket_desc,(struct sockaddr*)&server,sizeof(server))< 0 )
{
perror( "connect connect\n" );
return  1 ;
}
//發送數據:
 
messgge= "hello world\n" ;
if (send(socket_desc,message,strlen(message), 0 )< 0 )
{
perror( "send dagta error\n" );
return  2 ;
}
printf( "send message successed\n" );
 
//接收數據
if (recv(socket_addr,server_reply, 2000 , 0 )< 0 )
{
perror( "recv success\n" );
return  3 ;
}
printf( "recv success\n" );
puts(server_reply);
//關閉socket
close(socket_desc);
return  0 ;
}
相關文章
相關標籤/搜索