文章目录
网络编程主要函数介绍
1、socket函数
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
此函数用于创建一个套接字
参数:
domain 是网络程序所在的主机采用的通讯协族(AF_UNIX 和 AF_INET 等)。
①AF_UNIX 只能够用于单一的 Unix 系统进程间通信
②AF_INET 是针 对 Internet 的,因而可以允许远程通信使用。
type 是网络程序所采用的通讯协议(SOCK_STREAM,SOCK_DGRAM 等)。
①SOCK_STREAM 表明用的是 TCP 协议,这样会提供按顺序的,可靠,双向,面向连接的比特流。
②SOCK_DGRAM 表明用的是 UDP 协议,这样只会提不可靠,无连接的通信。
关于 protocol,由于指定了 type,所以这个地方一般只要用 0 来代替就可以了。
返回值:
执行成功分返回文件描述符,失败时返回-1
2、bind函数
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
从函数用于将地址绑定到一个套接字。
参数:
①sockfd 是由 socket 函数调用返回的文件描述符。
②my_addr 是一个指向 sockaddr 的指针。
③addrlen 是 sockaddr 结构的长度。
sockaddr 的定义:
struct sockaddr{
unisgned short as_family;
char sa_data[14];
};
不 过 由 于 系 统 的 兼 容 性 , 我 们 一 般 使 用 另 外 一 个 结 构 (struct sockaddr_in) 来代替。 sockaddr_in 的定义:
struct sockaddr_in{
unsigned short sin_family;
unsigned short sin_port;
struct in_addr sin_addr; // // 结构体套结构体 sin_addr.s_addr
unsigned char sin_zero[8];
}
如果使用 Internet
sin_family 一般为 AF_INET。
sin_addr 设置为自身的IP。
sin_port 是要监听的端口号。
bind 将本地的端口同 socket 返回的文件描述符捆绑在一起.
成功则返回 0, 失败则情况和 socket 一样。
==注意:==端口号和IP填入函数需要使用参数的特定形式
转换端口和IP形式的函数
端口形式转换:
htons(8989);
8989-端口号
IP形式 转换:
inet_aton("192.168.163.128",&s_addr.sin_addr);
192.168.163.128——IP号
&s_addr.sin_addr——将IP形式转换后赋给&s_addr.sin_addr
IP形式逆转换
char* inet_ntoa(struct in_addr num);
返回值:返回IP原来格式,类似于192.168.163.128
inet_ntoa(c_addr.sin_addr);
3、listen函数
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
此函数宣告服务器可以接受连接请求。
参数:
①sockfd 是 bind 后的文件描述符。
②backlog 设置请求排队的最大长度。当有多个客户端程序和服务端相连时, 使用这个表示可以介绍的排队长度。
listen 函数将 bind 的文件描述符变为监听套接字,返回的情况和 bind 一 样。
4、accept函数
int accept(int sockfd, struct sockaddr *addr,int *addrlen);
服务器使用此函数获得连接请求,并且建立连接。
参数:
addr,addrlen 是用来给客户端的程序填写的,服务器端只要传递指针就可以了, bind,listen 和 accept 是服务器端用的函数。配合转换函数就可以知道连接人的IP以及其他信息!(后面会示范)
accept 调用时,服务器端的程序会一直阻塞到有一个客户程序发出了连接。
accept 成功时返回最后的服务器端的文件描述符,这个时候服务器端可以向该描述符写信息了,失败时返回-1 。
网络模式(TCP&UDP)
1、面向连接的TCP流模式
2、UDP用户数据包模式
编写一个简单服务端编程
前面学习了好几个函数了,足够我们写出一个简单的可供链接的服务端了!
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <stdio.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
//int listen(int sockfd, int backlog);
//int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
//int socket(int domain, int type, int protocol);
int main(int argc, char** argv)
{
int s_fd;
struct sockaddr_in s_addr;
struct sockaddr_in c_addr;
//socket
s_fd = socket(AF_INET, SOCK_STREAM, 0);
if(s_fd == -1)//创建一个套接字失败
{
perror("socket");//打印错误信息
exit(-1);
}
/* 配置 */
s_addr.sin_family = AF_INET;
s_addr.sin_port = htons(8989);
inet_aton("192.168.163.128",&s_addr.sin_addr);//转换格式
//bind
bind(s_fd, (struct sockaddr *)&s_addr, sizeof(struct sockaddr_in));
//listen
listen(s_fd,10);
//accept
int len = sizeof(struct sockaddr_in);
int c_fd=accept(s_fd,(struct sockaddr *)&c_addr,&len);
printf ("get %s\r\n",inet_ntoa(c_addr.sin_addr));//逆转换并打印
printf("connet succes\n");
//read
char readbuf[128];
read(c_fd, readbuf, 128);//读数据,没读到会一直阻塞
printf("%s\r\n",readbuf);
//write
write(c_fd, "man", 128);
while(1);
return 0;
}
我们使用虚拟机连接试试看:
使用telnet指令
试着互相发送:
①为客户发送给服务端
②为服务端不阻塞后写数据给客户
这样一个简单的服务端就写好了!
5、connect函数
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, struct sockaddr * serv_addr,int addrlen);
可以用 connect 建立一个连接(客户端所用),在 connect 中所指定的地址是想与之通信的服务器的地址。sockfd 是 socket 函数返回的文件描述符。serv_addr 储存了服务器端的连接信息,其中 sin_add 是服务端的地址。addrlen 是 serv_addr 的长度connect 函数是客户端用来同服务端连接的.成功时返回 0,sockfd 是同服 务端通讯的文件描述符,失败时返回-1。
编写一个简单客户端编程
学习了connect函数,我们就可以写一个简单的客户端了,并且与前面的服务端进行通信:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <stdio.h>
//#include <linux/in.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char** argv)
{
int c_fd;
struct sockaddr_in c_addr;
//socket
c_fd = socket(AF_INET, SOCK_STREAM, 0);
if(c_fd == -1)
{
perror("socket");
exit(-1);
}
c_addr.sin_family = AF_INET;
c_addr.sin_port = htons(8989);
inet_aton("192.168.163.128",&c_addr.sin_addr);
//connect
if(connect(c_fd, (struct sockaddr *)&c_addr, sizeof(struct sockaddr)) == -1)
{
perror("connect");
exit(-1);
}
//write
write(c_fd, "from client",128);
//read
char readbuf[128];
read(c_fd, readbuf, 128);
printf("%s\r\n",readbuf);
while(1);
return 0;
}
运行:
超级客户端&服务端(TCP)
后面学习了韦东山老师的Linux课程,发现韦老师教的非常不错,就学习了一下韦老师的写法!
1、服务端
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <sys/types.h>
5#include <sys/socket.h>
6#include <netinet/in.h>
7#include <arpa/inet.h>
8#include <unistd.h>
9#include <signal.h>
10
11#define SERVER_PORT 8180
12#define C_QUEUE 10
13
14/************************************************************
15*函数功能描述:从8180端口接收客户端数据
16*输入参数:无
17*输出参数:打印客户IP以及发来的信息
18*返回值:无
19*修改日期 版本号 修改人 修改内容
20*2020/05/13 v1.0.0 zonghzha reat
21*************************************************************/
22
23int main(int argc, char **argv)
24{
25 char buf[512];
26 int len;
27 int duty_socket;
28 int customer_socket;
29 struct sockaddr_in socket_server_addr;
30 struct sockaddr_in socket_client_addr;
31 int ret;
32 int addr_len;
33
34 signal(SIGCHLD, SIG_IGN);
35
36 /* 服务器端开始建立socket描述符 */
37 duty_socket = socket(AF_INET, SOCK_STREAM, 0);
38 if (duty_socket == -1)
39 {
40 printf("socket error");
41 return -1;
42 }
43
44 /* 服务器端填充 sockaddr_in结构 */
45 socket_server_addr.sin_family = AF_INET;
46 /*端口号转换为网络字节序*/
47 socket_server_addr.sin_port = htons(SERVER_PORT);
48 /*接收本机所有网口的数据*/
49 socket_server_addr.sin_addr.s_addr = INADDR_ANY;
50 memset(socket_server_addr.sin_zero, 0, 8);
51
52 /* 捆绑sockfd描述符 */
53 ret = bind(duty_socket, (const struct sockaddr *)&socket_server_addr, sizeof(struct sockaddr));
54 if (ret == -1)
55 {
56 printf("bind error!\n");
57 return -1;
58 }
59 ret = listen(duty_socket, C_QUEUE);
60 if (ret == -1)
61 {
62 printf("listen error!\n");
63 return -1;
64 }
65
66 while (1)
67 {
68 addr_len = sizeof(struct sockaddr);
69 /* 服务器阻塞,直到客户程序建立连接 */
70 customer_socket = accept(duty_socket, (struct sockaddr *)&socket_client_addr, &addr_len);
71 if (customer_socket != -1)
72 {
73 /*inet_ntoa的作用是将一个32位Ipv4地址转换为相应的点分十进制数串*/
74 printf("Get connect from %s\n", inet_ntoa(socket_client_addr.sin_addr));
75 }
76 if (!fork())
77 {
78 while (1)
79 {
80 memset(buf, 512, 0);
81 /*接收数据*/
82 len = recv(customer_socket, buf, sizeof(buf), 0);
83 buf[len] = '\0';
84 if (len <= 0)
85 {
86 close(customer_socket);
87 return -1;
88 }
89 else
90 {
91 printf("Get connect from %s, Msg is %s\n", inet_ntoa(socket_client_addr.sin_addr), buf);
92 }
93 }
94 }
95 }
96
97 close(duty_socket);
98 return 0;
99}
2、客户端
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <sys/types.h>
5#include <sys/socket.h>
6#include <netinet/in.h>
7#include <arpa/inet.h>
8#include <unistd.h>
9
10#define SERVER_PORT 8180
11/************************************************************
12*函数功能描述:向指定IP的8180端口发送数据
13*输入参数:点分十进制服务器IP
14*输出参数:无
15*返回值:无
16*修改日期 版本号 修改人 修改内容
17*2020/05/13 v1.0.0 zonghzha creat
18*************************************************************/
19
20int main(int argc, char **argv)
21{
22 unsigned char buf[512];
23 int len;
24 struct sockaddr_in socket_server_addr;
25 int ret;
26 int addr_len;
27 int client_socket;
28
29
30 if (argc != 2)
31 {
32 printf("Usage:\n");
33 printf("%s <server_ip>\n", argv[0]);
34 return -1;
35 }
36
37 /* 客户程序开始建立 sockfd描述符 */
38 client_socket = socket(AF_INET, SOCK_STREAM, 0);
39 if (client_socket == -1)
40 {
41 printf("socket error");
42 return -1;
43 }
44
45 /* 客户程序填充服务端的资料 */
46 socket_server_addr.sin_family = AF_INET;
47 /*主机字节序转换为网络字节序*/
48 socket_server_addr.sin_port = htons(SERVER_PORT);
49 if (inet_aton(argv[1], &socket_server_addr.sin_addr) == 0)
50 {
51 printf("invalid server ip\n");
52 return -1;
53 }
54 memset(socket_server_addr.sin_zero, 0, 8);
55 /* 客户程序发起连接请求 */
56 ret = connect(client_socket, (const struct sockaddr *)&socket_server_addr, sizeof(struct sockaddr));
57 if (ret == -1)
58 {
59 printf("connect error!\n");
60 return -1;
61 }
62
63
64 while (1)
65 {
66 if (fgets(buf, sizeof(buf), stdin))
67 {
68 len = send(client_socket, buf, strlen(buf), 0);
69 if (len <= 0)
70 {
71 close(client_socket);
72 return -1;
73 }
74 }
75 }
76
77 close(client_socket);
78 return 0;
79}
超级客户端&服务端(UDP)
1、服务端
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4//#include <sys/type.h>
5#include <sys/socket.h>
6#include <netinet/in.h>
7#include <arpa/inet.h>
8#include <unistd.h>
9#include <signal.h>
10
11/*服务器端口为8180*/
12#define SERVER_PORT 8180
13
14/************************************************************
15*函数功能描述:从8180端口接收客户端数据
16*输入参数:无
17*输出参数:打印客户IP以及发来的信息
18*返回值:无
19*修改日期 版本号 修改人 修改内容
20*2020/05/13 v1.0.0 zonghzha creat
21*************************************************************/
22
23
24int main(int argc, char **argv)
25{
26 unsigned char buf[512];
27 int len;
28 int duty_socket;
29 int customer_socket;
30 struct sockaddr_in socket_server_addr;
31 struct sockaddr_in socket_client_addr;
32 int ret;
33 int addr_len;
34
35 /*创建数据报套接字*/
36 duty_socket = socket(AF_INET, SOCK_DGRAM, 0);
37 if (duty_socket == -1)
38 {
39 printf("socket error");
40 return -1;
41 }
42
43 /* 服务器端填充 sockaddr_in结构 */
44 socket_server_addr.sin_family = AF_INET;
45 socket_server_addr.sin_port = htons(SERVER_PORT);
46 socket_server_addr.sin_addr.s_addr = INADDR_ANY;
47 memset(socket_server_addr.sin_zero, 0, 8);
48
49 /*绑定套接字*/
50 ret = bind(duty_socket, (const struct sockaddr *)&socket_server_addr, sizeof(struct sockaddr));
51 if (ret == -1)
52 {
53 printf("bind error!\n");
54 return -1;
55 }
56
57
58 while (1)
59 {
60 addr_len = sizeof(struct sockaddr);
61 /* 接收客户端数据报,返回的为接收到的字节数 */
62 len = recvfrom(duty_socket, buf, sizeof(buf), 0, (struct sockaddr *)&socket_client_addr, &addr_len);
63 if (len > 0)
64 {
65 buf[len] = '\0';
66 printf("Get Msg from %s : %s\n", inet_ntoa(socket_client_addr.sin_addr), buf);
67 }
68
69 }
70
71 close(duty_socket);
72 return 0;
73}
74
2、客户端实现1(使用connect)
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <sys/socket.h>
5#include <netinet/in.h>
6#include <arpa/inet.h>
7#include <unistd.h>
8
9/*服务器端口为8180*/
10#define SERVER_PORT 8180
11
12/************************************************************
13*函数功能描述:向指定IP的8180端口发送数据
14*输入参数:点分十进制服务器IP
15*输出参数:无
16*返回值:无
17*修改日期 版本号 修改人 修改内容
18*2020/05/13 v1.0.0 zonghzha creat
19*************************************************************/
20
21int main(int argc, char **argv)
22{
23 unsigned char buf[512];
24 int len;
25 struct sockaddr_in socket_server_addr;
26 int ret;
27 int addr_len;
28 int client_socket;
29
30
31 if (argc != 2)
32 {
33 printf("Usage:\n");
34 printf("%s <server_ip>\n", argv[0]);
35 return -1;
36 }
37
38 /*创建数据报套接字*/
39 client_socket = socket(AF_INET, SOCK_DGRAM, 0);
40 if (client_socket == -1)
41 {
42 printf("socket error");
43 return -1;
44 }
45
46 socket_server_addr.sin_family = AF_INET;
47 socket_server_addr.sin_port = htons(SERVER_PORT);
48 if (inet_aton(argv[1], &socket_server_addr.sin_addr) == 0)
49 {
50 printf("invalid server ip\n");
51 return -1;
52 }
53 memset(socket_server_addr.sin_zero, 0, 8);
54
55 ret = connect(client_socket, (const struct sockaddr *)&socket_server_addr, sizeof(struct sockaddr));
/* 使用了connect函数,发送数据就使用send函数 */
56 if (ret == -1)
57 {
58 printf("connect error!\n");
59 return -1;
60 }
61
62
63 while (1)
64 {
65 if (fgets(buf, sizeof(buf), stdin))
66 {
67 len = send(client_socket, buf, strlen(buf), 0);
68 if (len <= 0)
69 {
70 close(client_socket);
71 return -1;
72 }
73 }
74 }
75
76 close(client_socket);
77 return 0;
78}
79
3、客户端实现2(不使用connect)
1#include <stdio.h>
2#include <stdlib.h>
3#include <string.h>
4#include <sys/socket.h>
5#include <netinet/in.h>
6#include <arpa/inet.h>
7#include <unistd.h>
8
9#define SERVER_PORT 8180
10
11/************************************************************
12*函数功能描述:向指定IP的8180端口发送数据
13*输入参数:点分十进制服务器IP
14*输出参数:无
15*返回值:无
16*修改日期 版本号 修改人 修改内容
17*2020/05/13 v1.0.0 zonghzha creat
18*************************************************************/
19
20int main(int argc, char **argv)
21{
22 unsigned char buf[512];
23 int len;
24 struct sockaddr_in socket_server_addr;
25 int ret;
26 int addr_len;
27 int client_socket;
28
29
30 if (argc != 2)
31 {
32 printf("Usage:\n");
33 printf("%s <server_ip>\n", argv[0]);
34 return -1;
35 }
36
37 /*创建套接字*/
38 client_socket = socket(AF_INET, SOCK_DGRAM, 0);
39 if (client_socket == -1)
40 {
41 printf("socket error");
42 return -1;
43 }
44
45 /* 填充服务端的资料 */
46 socket_server_addr.sin_family = AF_INET;
47 socket_server_addr.sin_port = htons(SERVER_PORT);
48 if (inet_aton(argv[1], &socket_server_addr.sin_addr) == 0)
49 {
50 printf("invalid server ip\n");
51 return -1;
52 }
53 memset(socket_server_addr.sin_zero, 0, 8);
54
55
56
57
58 while (1)
59 {
60 if (fgets(buf, sizeof(buf), stdin))
61 {
62 // len = send(client_socket, buf, strlen(buf), 0);
63 /*向服务器端发送数据报*/
64 addr_len = sizeof(struct sockaddr);
65 len = sendto(client_socket, buf, sizeof(buf), 0, (struct sockaddr *)&socket_server_addr, addr_len);
/* 不使用connect函数,就要使用sendto函数 */
66 if (len <= 0)
67 {
68 close(client_socket);
69 return -1;
70 }
71 }
72 }
73
74 close(client_socket);
75 return 0;
76}
77
客户端1和2的区别
客户端1使用了connect函数,指定了要发送数据的目标,所以发送数据使用send就可以了,客户端2没有使用connect函数,所以没有指定目标,要使用sendto函数才可以发送数据!
#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);