Network Programming(四)

Struct

  1. struct sockaddr{
    sa_family_t sa_family;
    char sa_data[14];}

对于IPv4

  1. struct sockaddr_in{
    sa_family_t sin_family;
    in_port_t sin_port;
    struct in_addr sin_addr;};
    这个就是我们常用来填充的结构体
  2. struct in_addr{
    uint32_t s_addr;
    };
    s_addr通常用到htonl(INADDR_ANY);
    htonl就是把本机字节顺序转化为网络字节顺序
    htonl 是针对32位,4个字节而言
    htons 是针对16位,2个字节而言
    htonl()–”Host to Network Long”
    ntohl()–”Network to Host Long”
    htons()–”Host to Network Short”
    ntohs()–”Network to Host Short

实例三

写一个服务器用来接受多个客户端的消息,并判断这个消息是否是回文,返回结果。写一个客户端,用来发送消息,用户输入“quit#”退出连接。

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
客户端
# include <sys/socket.h>
# include <arpa/inet.h>
# include <netdb.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>


# define SERVERPORT 8888
# define LOCALHOST "127.0.0.1"
# define MYMSGLEN 2048

int question ( int sock, char * string )
{
int code, length, ret ;

// Send the length of the string.
length = strlen ( string ) + 1 ;

// Send the string of characters.
code = send ( sock, string, length, 0 ) ;
if ( code == -1 ) {
perror ( "send" ) ;
exit ( 1 ) ;
}
printf ( "Message sent to server, waiting for the answer ..... \n " ) ;
// Wait for the answer.
code = recv ( sock, & ret, sizeof ( int ), 0 ) ;
if ( code == -1 ) {
perror ( "recv" ) ;
exit ( 1 ) ;
}

//ret返回服务器判断结果
return ret ;
}

int connecting ( )
{
// Socket creation.第一步,创建socket
int sock ;
sock = socket ( AF_INET, SOCK_STREAM, 0 ) ;//TCP直连
if ( sock == -1 ) {
perror ( "socket" ) ;
exit ( 1 ) ;
}

// Initialisation of the sockaddr_in data structure

struct sockaddr_in addr ;
socklen_t len ;

struct sockaddr_in newAddr ;
struct sockaddr_in peerAddr ;

//在初始化地址时,最好先把地址各位归0
memset ( & addr, 0, sizeof ( struct sockaddr_in ) ) ;
addr . sin_family = AF_INET ;
//SERVERPORT 8888
//之所以需要这些函数是因为计算机数据表示存在两种字节顺序:网络字节顺序NBO(Network Byte Order)与主机字节顺序HBO(Host Byte Order)
//htons()--"Host to Network Short"
addr . sin_port = htons(SERVERPORT) ;
//LOCALHOST "127.0.0.1"
//in_addr_t inet_addr(const char* strptr);
//将点分十进制变成IP地址,-1 failure,不能用于广播(255.255.255.2555)
addr . sin_addr . s_addr = inet_addr(LOCALHOST) ;

// Name the socket.
int code ;
code = connect ( sock, ( struct sockaddr * ) & addr, sizeof ( struct sockaddr_in ) ) ;
if ( code == -1 ) {
perror ( "connect" ) ;
close ( sock ) ;
exit ( 1 ) ;
}

len = sizeof ( struct sockaddr_in ) ;
//获取sock绑定的本地地址
code = getsockname ( sock, ( struct sockaddr * ) & newAddr, & len ) ;
if ( code == -1 ) {
perror ( "getsockname" ) ;
close ( sock ) ;

exit ( 1 ) ;
}
len = sizeof ( struct sockaddr_in ) ;
//在TCP的服务器端accept成功后,通过getpeername()函数来获取当前连接的客户端的IP地址和端口号。
code = getpeername ( sock, ( struct sockaddr * ) & peerAddr, & len ) ;
if ( code == -1 ) {
perror ( "getpeername" ) ;
close ( sock ) ;
exit ( 1 ) ;
}

//char* inet_ntoa(struct in_addr invader);
//将IP地址转换为ASCII点十进制符号
printf ( " The local address bound to the current socket --> %s:%d \n" , inet_ntoa ( newAddr.sin_addr ), ntohs ( newAddr.sin_port ) ) ;
printf ( " The peer address bound to the peer socket --> %s:%d \n" , inet_ntoa ( peerAddr.sin_addr ), ntohs ( peerAddr.sin_port ) ) ;
// Return the socket ID.
return sock ;
}

int main ( int argc, char * argv [ ] )
{
// Ask for the string of characters.
char string [ MYMSGLEN ];



// Connect to the server.
int sock ;
sock = connecting ( ) ;
//利用connecting()实现socket创建,connect

// Ask the question and wait for the answer.
int ret ;
while ( 1 )
{
//每次用到字符串前,先初始化为0
memset ( string, 0, sizeof( string ) );
printf ( "Type a string to check: " ) ;
scanf ( "%s", string ) ;

//memcmp若两个字符串完全相同返回0,socket关闭
if ( !memcmp ( "quit#", string, 5 ) )
{
printf ( " Client exiting upon user request \n" ) ;
close ( sock ) ;
exit ( 1 ) ;
}

//在question函数中实现与服务器间的通信send,recv
ret = question ( sock, string ) ;

// Display the result.
if (ret == 1)
printf ( "Reply received: '%s' is a palindrom.\n", string ) ;
else
printf ( "Reply received: '%s' is not a alindrom.\n", string );
}

return ( 0 ) ;
}
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
服务器
# include <sys/socket.h>
# include <arpa/inet.h>
# include <netdb.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>

# define SERVERPORT 8888
# define LOCALHOST "127.0.0.1"
# define MYMSGLEN 2048

//功能函数,用来判断是否是回文
int palindrom ( char * s )
{
char * t = s + strlen ( s ) - 1 ;

while ( * s == * t )
s ++, t -- ;
return s >= t ;
}

//在无限循环中实现客户端数据的接受recv,处理palindrom和返回结果send
int answer_client ( int sock )
{
// Receive the length of the string.
int code, length ;
char string [ MYMSGLEN ];

while ( 1 )
{
// Receive the string of characters.
printf ( "Waiting for a string to process....\n" ) ;
memset ( string, 0, sizeof( string ) );
length = recv ( sock, string, sizeof ( string ), 0 ) ;

if ( length == -1 ) {
perror ( "recv" ) ;
code = close ( sock ) ;
if ( code == -1 )
perror ( "close" ) ;
return ( -1 ) ;
}

if ( length == 0 ) {

// Connection closed by remote peer, release ressources.
printf ( "Connection closed by remote peer ....\n" ) ;
code = close ( sock ) ;
if ( code == -1 ) {
perror ( "close" ) ;
return ( -1 ) ;

}
return ( 0 ) ;
}

printf ( "Going to process the received string '%s'\n", string ) ;

// Check the string of characters.
int ret ;
ret = palindrom ( string ) ;


// Return the answer.
printf ( "Processing done, sending back the answer\n" ) ;
code = send ( sock, & ret, sizeof ( int ), 0 ) ;
if ( code == -1 ) {
perror ( "send" ) ;
close ( sock ) ;
return ( -1 ) ;
}
}
}

//socket创建+设置+bind地址
int naming ( )
{
// Socket creation.
int sock ;
//AF_INET:IPv4 protocols。定义在<sys/socket.h>中,
//SOCK_STREAM: connected communication withend to end control flow
sock = socket ( AF_INET, SOCK_STREAM, 0 ) ;
if ( sock == -1 ) {
perror ( "socket" ) ;
exit ( 1 ) ;
}

// Reuse the same port (useful when developing...).
int code, enable = 1;
//SO_REUSEADDR:Allows other sockets to bind() to this port
code = setsockopt ( sock, SOL_SOCKET, SO_REUSEADDR, & enable, sizeof ( int ) ) ;
if (code == -1 ) {
perror ( "setsockopt" ) ;
close ( sock ) ;
exit ( 1 ) ;
}


code = setsockopt ( sock, SOL_SOCKET, SO_REUSEPORT, & enable, sizeof ( int ) ) ;
if (code == -1 ) {
perror ( "setsockopt" ) ;
close ( sock ) ;
exit ( 1 ) ;
}

// Initialisation of the sockaddr_in data structure.
struct sockaddr_in addr ;
memset ( & addr, 0, sizeof ( struct sockaddr_in ) ) ;
addr . sin_family = AF_INET ;
addr . sin_port = htons(SERVERPORT) ;
addr . sin_addr . s_addr = inet_addr(LOCALHOST) ;

// Name the socket.
code = bind ( sock, ( struct sockaddr * ) & addr, sizeof ( struct sockaddr_in ) ) ;
if ( code == -1 ) {
perror ( "bind" ) ;
close ( sock ) ;
exit ( 1 ) ;
}

// Return the socket ID.
return sock ;
}

int main ( int argc, char * argv [ ] )
{
// Name the socket.
int sock ;
sock = naming ( ) ;

// Set up listening on the port.
int code ;
//backlog为0时在linux上表明允许不受限制的连接数,这是一个缺陷,因为它可能会导致SYN Flooding(拒绝服务型攻击)
code = listen ( sock, 0 ) ;
if ( code == -1 ) {
perror ( "listen" ) ;
close ( sock ) ;
exit ( 1 ) ;
}

// Wait for incoming connections and deal with the clients.
int new ;
socklen_t len ;
struct sockaddr_in addr ;
struct sockaddr_in newAddr ;
struct sockaddr_in peerAddr ;

for ( ; ; ) {
printf ( "Server waiting for a new connection \n" ) ;
len = sizeof ( struct sockaddr_in ) ;
//获取到用来与客户端通信的socket
new = accept ( sock, ( struct sockaddr * ) & addr, & len ) ;
if ( new == -1 ) {
perror ( "accept" ) ;
close ( sock ) ;
exit ( 1 ) ;
}
printf ( " A new connection from --> %s:%d \n" ,inet_ntoa ( addr.sin_addr ), ntohs ( addr.sin_port ) ) ;
len = sizeof ( struct sockaddr_in ) ;
code = getsockname ( new, ( struct sockaddr * ) & newAddr, & len ) ;
if ( code == -1 ) {
perror ( "getsockname" ) ;
close ( sock ) ;
close ( new ) ;
exit ( 1 ) ;
}
len = sizeof ( struct sockaddr_in ) ;
code = getpeername ( new, ( struct sockaddr * ) & peerAddr, & len ) ;
if ( code == -1 ) {
perror ( "getpeername" ) ;
close ( sock ) ;
close ( new ) ;
exit ( 1 ) ;
}

printf ( " The local address bound to the new socket --> %s:%d \n" , inet_ntoa ( newAddr.sin_addr ), ntohs ( newAddr.sin_port ) ) ;
printf ( " The peer address bound to the new socket --> %s:%d \n" , inet_ntoa ( peerAddr.sin_addr ), ntohs ( peerAddr.sin_port ) ) ;

//以上打印连接信息
//以下调用answer_client()与client通信
if ( answer_client ( new ) == ( -1 ) ) {
close ( sock ) ;
exit ( 0 ) ;
}

}

// This point in the program will never be reached.
return ( 0 ) ;
}

总结:
客户端编程思路:
写2个子函数:connecting()负责socket创建,connect
questioning()通过参数传入socket和要询问的信息,负责socket的send和recv
在main()中循环外使用connecting()建立连接,循环内每次先判断是否满足断开连接要求,在调用questioning()。
注意:地址转换和网络字节顺序转换函数的使用。

服务器端编程思路:
写3个子函数:palindrom()用来处理收到的信息,判断是否是回文,用在answerclient中
int naming ( )负责socket创建+设置+bind地址,返回socket
int answer_client ( int sock ) 在无限循环中实现客户端数据的接受recv,处理palindrom和返回结果send。这里的循环会在客户端断开连接后跳出,是为了让server能处理多条client的消息。
在main()中先调用naming()设置好服务器端socket,并开启监听listen()
在无线循环中获取与客户端通信的socketaccept(),打印出各类连接信息,调用answer_client实现通信。这里的循环是为了确保服务器一直运行能与多个客户端相连。

获取socket信息

  • int getsockname(int sockfd,struct sockaddr addr,socklen_t addrlen);
    通过引用传递,获取sockfd绑定的地址。0,success。-1 error
  • int getpeername(int sockfd,struct sockaddr addr,socklen_t addrlen);
    通过引用传递,获取sockfd对端的地址。0,success。-1 error

实例四

在实例三的基础上应用struct保存信息,计算信息的价值

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# include <sys/socket.h>
# include <arpa/inet.h>
# include <netdb.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <unistd.h>


# define SERVERPORT 8888
# define LOCALHOST "127.0.0.1"

# define MYMSGLEN 2048

typedef struct {
uint16_t ret ;
uint16_t msgLen ;
uint32_t cost ;
char buff[ MYMSGLEN - 8 ] ;
} messageCost;

void question ( int sock, messageCost * msg )
{
int code, length, ret ;

// Send the length of the string.
length = strlen ( msg->buff ) ; // + 1 ;

// Send the string of characters.
code = send ( sock, msg->buff, length, 0 ) ;
if ( code == -1 ) {
perror ( "send" ) ;
exit ( 1 ) ;
}
printf ( "Message sent to server, waiting for the answer ..... \n " ) ;
// Wait for the answer.
code = recv ( sock, ( char * ) msg, sizeof ( messageCost ), 0 ) ;
if ( code == -1 ) {
perror ( "recv" ) ;
exit ( 1 ) ;
}

// Release ressources. XXX To remove
//code = close ( sock ) ;
//if ( code == -1 ) {
// perror ( "close" ) ;
// exit ( 1 ) ;
//}


}

int connecting ( )
{
// Socket creation.
int sock ;
sock = socket ( AF_INET, SOCK_STREAM, 0 ) ;
if ( sock == -1 ) {
perror ( "socket" ) ;
exit ( 1 ) ;
}

// Initialisation of the sockaddr_in data structure

struct sockaddr_in addr ;
socklen_t len ;

struct sockaddr_in newAddr ;
struct sockaddr_in peerAddr ;

memset ( & addr, 0, sizeof ( struct sockaddr_in ) ) ;
addr . sin_family = AF_INET ;
addr . sin_port = htons ( SERVERPORT ) ;
addr . sin_addr . s_addr = inet_addr ( LOCALHOST ) ;

// Name the socket.
int code ;
code = connect ( sock, ( struct sockaddr * ) & addr, sizeof ( struct sockaddr_in ) ) ;
if ( code == -1 ) {
perror ( "connect" ) ;
close ( sock ) ;
exit ( 1 ) ;
}

len = sizeof ( struct sockaddr_in ) ;
code = getsockname ( sock, ( struct sockaddr * ) & newAddr, & len ) ;
if ( code == -1 ) {
perror ( "getsockname" ) ;
close ( sock ) ;

exit ( 1 ) ;
}
len = sizeof ( struct sockaddr_in ) ;
code = getpeername ( sock, ( struct sockaddr * ) & peerAddr, & len ) ;
if ( code == -1 ) {
perror ( "getpeername" ) ;
close ( sock ) ;
exit ( 1 ) ;
}

printf ( " The local address bound to the current socket --> %s:%d \n" , inet_ntoa ( newAddr.sin_addr ), ntohs ( newAddr.sin_port ) ) ;
printf ( " The peer address bound to the peer socket --> %s:%d \n" , inet_ntoa ( peerAddr.sin_addr ), ntohs ( peerAddr.sin_port ) ) ;

// Return the socket ID.
return ( sock ) ;
}

int main ( int argc, char * argv [ ] )
{

// Ask for the string of characters.
char string [ MYMSGLEN ] ;
messageCost *msg = (messageCost *) string ;


// Connect to the server.
int sock ;
sock = connecting ( ) ;

while ( 1 )
{
memset ( string, 0, sizeof( string ) ) ;

//printf ( "String to check: " ) ;
//scanf ( "%s", msg->buff ) ;
printf ( "\n----------------------------------------------------------------------\n" ) ;
printf ( "Please type a message to transfer for processing:" ) ;
scanf ( "%[^\n]%*c", msg->buff ) ; //reads a hole line till a new line feed
printf ( "The message being sent is: '%s' \n", msg->buff ) ;
printf( "----------------------------------------------------------------------\n" ) ;


if ( !memcmp ( "quit#", string, 5 ) )
{
printf ( " Client exiting upon user request \n" ) ;
close ( sock ) ;
exit ( 1 ) ;
}

// Ask the question and wait for the answer.

question ( sock, msg ) ;


// Process the answer
msg->cost = ntohl ( msg->cost ) ;

msg->msgLen = ntohs ( msg->msgLen ) ;

msg->ret = ntohs ( msg->ret );


// Display the result.
printf ( "\n----------------------------------------------------------------------\n" ) ;

if ( msg->ret == 1 )
printf ( "'%s' is a palindrom.\n", msg->buff ) ;
else
printf ( "'%s' is not a palindrom.\n", msg->buff );

printf ( "The processed message length is: %d, with a cost of: %.2f Euros\n", msg->msgLen, ( ( float ) msg->cost / 100 ) ) ;

printf ( "----------------------------------------------------------------------\n" ) ;

}
return ( 0 ) ;
}
1
2
3
4
5
6
7
8
9
typedef struct {
uint16_t ret ;
uint16_t msgLen ;
uint32_t cost ;
char buff[ MYMSGLEN - 8 ] ;
} messageCost;

char string [ MYMSGLEN ] ;
messageCost *msg = (messageCost *) string ;

注意这里struct的用法,将字符串强制装换为messageCost,避免了使用malloc()和free(),msg和string指向同一块地址的起始位置。
其他与实例三相似,不加赘述。