文章目录
TCP编程API
socket()函数 创建fd
bind()函数 绑定
一个小demo
listen()函数 把主动套接字转变为被动套接字
accept() 阻塞等待客户端连接请求
connect() 客户端的连接函数
最终demo
写在最后
TCP编程API
下面是一个总览图。我们主要学的就是其中的五个主要的api。
socket()函数 创建fd
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int socket(int domain, int type, int protocol);
参数:
1.domain
2.type
3。protocol:一般为0,原始套接字编程时需填充
返回值:
RETURN VALUE
On success, a file descriptor for the new socket is returned. On error, -1 is returned, and errnois set appropriately.
成功的时候会返回一个文件描述符。失败返回-1。
bind()函数 绑定
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数:
sockfp:通过socket()函数拿到的fd
addr:struct sockaddr的结构体变量的地址
addrlen:地址长度
addr的通用结构体
struct sockaddr { sa_family_t sa_family;//2字节 char sa_data[14];//14字节 }
基于Internet通信的结构体
struct sockaddr_in { sa_family_t sin_family; /* address family: AF_INET */ //2字节 in_port_t sin_port; /* port in network byte order */ //2字节 struct in_addr sin_addr; /* internet address */ //4字节 }; /* Internet address. */ struct in_addr { uint32_t s_addr; /* address in network byte order */ };
其中sin_zero 必须填充为0!
返回值:
On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
一个小demo
int fd = -1; struct sockaddr_in sin; /*创建sockt fd*/ if((fd = socket(AF_INET, SOCK_STREAM, 0) ) < 0){ perror("socket"); exit(1); } /*2.绑定 */ /*2.1填充struct sockaddr_in 结构体变量*/ bzero(&sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(SERV_PORT); //网络字节序的端口号转换 //sin.sin_addr = inet_addr(SERV_IP_ADDR); //IPV4 if(inet_pton(AF_INET, SERV_IP_ADDR,(void *)&sin.sin_addr) != 1){ perror("inet_pton"); exit(1); } /*2.2绑定*/ if(bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0){ perror("bind"); exit(1); }; /*3.调用listen()把主动套接字转变为被动套接字*/ if(listen(fd, BACKLOG) < 0){ perror("listen"); exit(1); }
如果是IPV6的编程可以man 7 ipv6,通常更通用的方法通过struct sockaddr_storage来编程。因为长度问题
listen()函数 把主动套接字转变为被动套接字
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int listen(int sockfd, int backlog);
参数:
sockfd : 通过socket()拿到的fd
backlog : 一般填5(同时允许几路客户端和服务器进行正在连接的过程,测试得知,ARM最大值为8)
内核中服务器套接字fd会维护2个链表
正在三次握手的客户端链表(数量= 2*backlog +1)
已经建立好连接的客户端链表(已经三次握手分配好了newfd)
返回值: On success, zero is returned. On error, -1 is returned, and errno is set appropriately.
accept() 阻塞等待客户端连接请求
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数:
sockfd:经过前面socket()创建的fd
addr和addrlen:获取到客户端的ip地址和端口号
返回值:
On success, these system calls return a nonnegative integer that is a file descriptor for the ac‐cepted socket. On error, -1 is returned, errno is set appropriately, and addrlen is left un‐changed.
成功时返回已经建立好连接的新的newid
connect() 客户端的连接函数
#include <sys/types.h> /* See NOTES */ #include <sys/socket.h> int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
connect()函数和bind()函数类似
最终demo
Clent:
#include "net.h" #define QUIT_STR "quit" int main(void){ int fd = -1; struct sockaddr_in sin; /*创建sockt fd*/ if((fd = socket(AF_INET, SOCK_STREAM, 0) ) < 0){ perror("socket"); exit(1); } /*2.连接服务器*/ bzero(&sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(SERV_PORT); //网络字节序的端口号转换 //sin.sin_addr = inet_addr(SERV_IP_ADDR); //IPV4 if(inet_pton(AF_INET, SERV_IP_ADDR,(void *)&sin.sin_addr) != 1){ perror("inet_pton"); exit(1); } if(connect(fd, (struct sockaddr *)&sin, sizeof(sin)) <0){ perror("connect"); exit(1); } /*3.读写文件*/ char buf[BUFSIZ]; while(1){ bzero(buf, BUFSIZ); if(fgets(buf, BUFSIZ - 1, stdin) == NULL){ continue; } write(fd, buf, strlen(buf)); if(!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR))){ printf("Client is exiting!\n"); break; } } /*4.关闭服务器*/ close(fd); return 0; }
Server:
#include "net.h" int main(void){ int fd = -1; struct sockaddr_in sin; /*创建sockt fd*/ if((fd = socket(AF_INET, SOCK_STREAM, 0) ) < 0){ perror("socket"); exit(1); } /*2.绑定 */ /*2.1填充struct sockaddr_in 结构体变量*/ bzero(&sin, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(SERV_PORT); //网络字节序的端口号转换 //sin.sin_addr = inet_addr(SERV_IP_ADDR); //IPV4 if(inet_pton(AF_INET, SERV_IP_ADDR,(void *)&sin.sin_addr) != 1){ perror("inet_pton"); exit(1); } /*2.2绑定*/ if(bind(fd, (struct sockaddr *)&sin, sizeof(sin)) < 0){ perror("bind"); exit(1); }; /*3.调用listen()把主动套接字转变为被动套接字*/ if(listen(fd, BACKLOG) < 0){ perror("listen"); exit(1); } int newfd = -1; /*4.阻塞等待客户端连接请求 */ newfd = accept(fd, NULL, NULL); if(newfd < 0){ perror("accept"); exit(1); } /*5.读写*/ //和newfd进行数据读写 int ret = -1; char buf[BUFSIZ]; while(1){ bzero(buf, BUFSIZ); do{ ret = read(newfd, buf, BUFSIZ); }while(ret < 0 && EINTR == errno); if(ret < 0){ perror("read"); exit(1); } if(!ret){ //对方关闭 break; } printf("Receive data : %s\n",buf); if(!strncasecmp(buf, QUIT_STR, strlen(QUIT_STR))){ //用户退出 printf("Client is exiting!\n"); break; } } close(newfd); return 0; }
net.h
#ifndef __NET_H__ #define __NET_H__ #include <stdio.h> #include <string.h> #include <unistd.h> #include <stdlib.h> #include <strings.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netinet/ip.h> #include <arpa/inet.h> #include <errno.h> #define SERV_PORT 5001 #define SERV_IP_ADDR "127.0.0.1" #define BACKLOG 5 #define QUIT_STR "quit" #endif
写在最后
今天开始网络编程,这部分非常重要,所有文件我都放在了gitee哦,需要自取,我尽量一天一更,大家和我一起变强呀!最后三连即可提高学习效率!!!