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 ;
}
相关文章
相关标签/搜索