一 最简例程实现socket网络传输
- 服务器端( 比客户端先运行)
vi socket_server.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <errno.h>
4 #include <string.h>
5 #include <sys/types.h>
6 #include <netinet/in.h>
7 #include <sys/socket.h>
8 #include <sys/wait.h>
9 #include <arpa/inet.h>
10 #include <signal.h>
11
12 #define SERVPORT 3333 /* port */
/*系统默认值队列大小为5*/
13 #define BACKLOG 5 /* max client connet queue*/
14
15 int main ()
16 {
17 int server_sockfd,client_sockfd;
18 int server_len,client_len;
19 struct sockaddr_in server_address,client_address;
20
21 server_sockfd = socket(AF_INET,SOCK_STREAM,0);
22 server_address.sin_family = AF_INET;
23 server_address.sin_port = htons(SERVPORT);
/* INADDR_ANY 表示服务器所有的网卡IP都监听*/
24 server_address.sin_addr.s_addr = INADDR_ANY;
25 bzero(&(server_address.sin_zero),8);
26
27 server_len = sizeof(server_address);
28
29 bind(server_sockfd,(struct sockaddr *)&server_address,server_len);
30 listen(server_sockfd, BACKLOG);
/* 忽略子进程停止或结束信号 */
31 signal(SIGCHLD,SIG_IGN);
32 while(1) {
33 int ch;
34 client_len = sizeof(client_address);
35 printf("wait client connet ...\n");
/* 收不到client connect 请求,则一直阻塞等待 */
36 client_sockfd = accept(server_sockfd,(struct sockaddr *)&client_address,&client_len);
37 printf("accept a client connet \n");
38 if(fork() == 0) {
39 read(client_sockfd,&ch,sizeof(ch));
40 // sleep(5);
41 ch++;
42 write(client_sockfd,&ch,sizeof(ch));
43 close(client_sockfd);
44 exit(0);
45 } else {
46 close(client_sockfd);
47 }
48 }
49 }
- 客户端
vi socket_client.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <string.h>
5 #include <sys/types.h>
6 #include <netinet/in.h>
7 #include <sys/socket.h>
8
9 #define SERVPORT 3333
10
11 int main () {
12 int clientfd;
13 struct sockaddr_in server_address;
14
15 clientfd = socket(AF_INET,SOCK_STREAM,0);
16 if (clientfd == -1) {
17 printf("socketfd get faild! \n");
18 exit(0);
19 }
20 server_address.sin_family = AF_INET;
21 server_address.sin_port = htons(SERVPORT);
/* 远程服务器 IP */
22 server_address.sin_addr.s_addr = inet_addr("192.168.2.167");
23 bzero(&(server_address.sin_zero),8);
24
25 if(connect(clientfd,(struct sockaddr *)&server_address,sizeof(struct sockaddr)) == -1) {
26 printf("connect error !\n");
27 exit(1);
28 }
29
30 int data;
31
32 write(clientfd,&data,sizeof(data));
33 printf("write %d\n",data);
34 read(clientfd,&data,sizeof(data));
35 printf("read %d\n",data);
36 sleep(2);
37
38 close(clientfd);
39 }
二 所涉及到的函数名词解释
1. include文件
○ #include <netdb.h> /* gai_strerror */
○ #include <sys/socket.h>
2. Socket是什么?
a. Socket是应用层与TCP/IP协议族通讯的中间软件抽象层,它是一组接口
User Process
Socket
TCP/UDP
IP
Ethernet
3. Socket的基本函数
○ socket (服务器/客户端)返回一个socket描述符(类似于open函数)
§ int socket(int domain,int type,int protocol)
□ domain:协议族(family),常用AF_INET(32位ipv4+16位port),AF_INET6
□ type:socket类型,常用SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)
□ protocol:协议,常用IPPROTO_TCP、IPPTORO_UDP
○ bind 服务器绑定到指定地址
§ int bind(int sockfd,const struct sockaddr *addr,socklen_t addrlen)
□ sockfd:即socket描述符(socket函数返回值)
□ addr:指向要绑定给sockfd的协议地址(ipv4与ipv6不同)
□ addrlen:对应地址长度
○ listen服务器监听
§ int listen(int sockfd,int backlog)
□ 函数作用:将套接字(sockfd)变成被动的连接监听套接字(被动等待客户端的连接),并且将该套接字和套接字对应的连接队列长度告诉Linux内核。
□ backlog:为相应socket可以排队的最大连接个数(未完成连接队列+已完成连接队列),默认值为5
○ connect客户端请求连接
§ int connect(int sockfd,const struct sockaddr *addr,socklen_t addrlen)
□ addr:为服务器的socket地址
○ accept服务器阻塞等待client连接
§ int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen)
□ 从处于established状态的连接队列头部取出一个已完成的连接(client connet 成功),如果这个队列没有已经完成的连接,accept()函数就会阻塞。TCP的连接队列满后,Linux不会如书中所说的拒绝连接,只是有些会延时连接。
□ sockfd:为服务器的socket描述字
□ addr:用于返回客户端的协议地址
□ *addrlen:&对应地址长度
□ 返回值:是内核自动生成的一个全新的描述字,代表与client的TCP连接
§ 服务器仅仅只创建一个监听socket描述字,而为每个连接客户创建一个已连接socket描述字,服务结束则关闭。
○ read、write(服务器/客户端)数据传输
§ ssize_t read(int fd,void *buf,size_t count)
§ ssize_t write(int fd,const void *buf,size_t count)
§ ssize_t sendto(int sockfd,const void *buf,size_t len,int flags,const struct sockaddr *dest_addr,socklen_t addrlen)
□ buf:数据报缓存地址
□ len:数据报长度
□ flags:指定发送形式,一般为0
□ dest_addr:目的的套接字地址
□ addrlen:所指地址的长度
□ 返回值:已发送的字节数
§ ssize_t recvfrom(int sockfd,void *buf,size_t len,int flags,struct sockaddr *src_addr,socklen_t *addrlen)
○ close(服务器/客户端)关闭socketfd
§ int close(int fd)
□ 完成了连接后的读写操作,就要关闭相应的socket描述字
4. 相关函数
○ getaddrinfo 将主机和服务转换到socket地址
§ int getaddrinfo(const char *restrict nodename,const char *restrict servname,const struct addrinfo **restrict hints,struct addrinfo **restrict res)
□ nodename:host或者IP地址,作为服务可为空
□ servname:十进制端口号或服务名称ftp,http
□ hints:获取信息要求设置
□ res:获取信息结果
□ 返回值:返回一个addrinfo结构体由套接字函数直接使用
○ freeaddrinfo
§ void freeaddrinfo(struct addrinfo *ai)
○ getnameinfo 将socket地址转换到主机和服务
§ int getnameinfo(const struct sockaddr *sa,socklen_t salen,char *host,size_t hostlen,char *serv,size_t servlen,int flags)
○ 地址转换
§ int inet_aton(const char *cp, struct in_addr *inp) 字符串转32整数表示
§ char *inet_ntoa(struct in_addr in) 整数转字符串
○ 网络字节序统一采用 big endian 先传低字节
§ 从主机转网络序
□ htons(unsigned short)
□ htonl (unsigned long)
® h:host 本地主机
® to:
® n:net网络的意思
® l: unsigned long
§ 从网络序转换到主机序
□ ntohs (unsigned short)
□ ntohl(unsigned long)
§ 网络中标识一台主机可以用IP地址 ,也可用主机名
□ struct hostent *gethostbyname( const char *hostname)
Program Flow Chart 模型图