目录
1、socket概述
2、地址及顺序处理
3、函数介绍
4、使用实例
1、socket概述
1、TCP协议通过三次握手协议建立连接
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;SYN:同步序列编号(Synchronize Sequence Numbers)。
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
一个完整的三次握手也就是: 请求---应答---再次确认。
对应的函数接口:
从图中可以看出,当客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。
2、编程流程
2、地址及顺序处理
1、地址结构相关处理
struct socketaddr{
unsigned short sa_family;
char sa_data[14]
}
struct socketaddr_in{
short int sa_family;//地址族,即使用什么样的地址,IPV4或者是IPV6
unsigned short int sin_port;
struct in_addr sin_addr;//存放IP地址
unsigned char sin_zero[8]//填充0以保持也struct socketaddr 同样大小
}
2、数据存储优先顺序
网络地址和主机地址转换
#include <netinet/in.h>
uint16_t htons(uint16_t host16bit)//主机地址向网络地址转换
uint32_t htols(uint32_t host32bit)
uint16_t ntohs(uint16_t net16bit)//网络地址向主机地址转换
uint32_t ntohl(uint32_t net32bit)
成功:返回要转换的字节序
出错:-1
3、地址格式转换
将十进制表示的地址转换成二进制
#include <arpa/inet.h>
int inet_pton(int family,//协议类型
const char *strptr,//要转化的值
void *addrptr)//转化后的地址
int inet_ntop(int family
void *addrptr
char *strptr
size_t len)//转化后值的大小
成功:0
出错:-1
4、名字地址转换
gethostbyname()将主机名转换为IP地址
gethostbyaddr()将IP地址转换为主机名
#include <netbd.h>
struct hostnet *gethostbyname(const char *honstname)
成功:hostnet类型指针
出错:-1
int getaddrinfo(const char *hostname
const char *service
const struct addrinfo *hints
struct addrinfo **result)//返回的结果
成功:0
出错:-1
struct hostnet {
char *h_name;//主机名
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list;//指向IPV4的地址指针数组
}
3、函数介绍
1、socket()
#include<sys/socket.h>
int socket(int family,int type,int protocal)
成功:非法套接字描述符
出错:-1;
2、bind()
#include<sys/socket.h>
int bind(int sockefd,struct sockaddr *my_addr,int addrlen);
成功:0;
出错:-1
3、listen()
#include<sys/socket.h>
int listen(int sockfd,int backlog)
4、accept()
#include<sys/socket.h>
int accept(int sockfd,struct sockaddr *addr,socklen_t *addrlen);
5、connect()
#include<sys/socket.h>
int connect(int sockfd,struct sockaddr *serv_addr,int addrlen);
成功:0
出错:-1
6、send()
int send(int sockfd,const void *msg,int len,int flags)
成功:发送的字节数
出错:-1
7、recv()
int recv(int sockfd,void *buff,int len,unsigned int flags)
成功:接受的字节数
出错:-1
8、sendto()
int sendto(int sockfd ,const void *msg,int len,unsigned int flags,struct sockaddr *to ,int * tolen)
成功:发送的字节数
出错:-1
9、recvfrom()
int recvfrom(int sockfd ,const void *msg,int len,unsigned int flags,struct sockaddr *from ,int * tolen)
成功:接受的字节数
出错:-1
4、简单实例
- /* File Name: server.c */
- #include<stdio.h>
- #include<stdlib.h>
- #include<string.h>
- #include<errno.h>
- #include<sys/types.h>
- #include<sys/socket.h>
- #include<netinet/in.h>
- #define DEFAULT_PORT 8000
- #define MAXLINE 4096
- int main(int argc, char** argv)
- {
- int socket_fd, connect_fd;
- struct sockaddr_in servaddr;
- char buff[4096];
- int n;
- //初始化Socket
- if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
- printf("create socket error: %s(errno: %d) ",strerror(errno),errno);
- exit(0);
- }
- //初始化
- memset(&servaddr, 0, sizeof(servaddr));
- servaddr.sin_family = AF_INET;
- servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。
- servaddr.sin_port = htons(DEFAULT_PORT);//设置的端口为DEFAULT_PORT
- //将本地地址绑定到所创建的套接字上
- if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
- printf("bind socket error: %s(errno: %d) ",strerror(errno),errno);
- exit(0);
- }
- //开始监听是否有客户端连接
- if( listen(socket_fd, 10) == -1){
- printf("listen socket error: %s(errno: %d) ",strerror(errno),errno);
- exit(0);
- }
- printf("======waiting for client's request====== ");
- while(1){
- //阻塞直到有客户端连接,不然多浪费CPU资源。
- if( (connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1){
- printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
- continue;
- }
- //接受客户端传过来的数据
- n = recv(connect_fd, buff, MAXLINE, 0);
- //向客户端发送回应数据
- if(!fork()){ /*紫禁城*/
- if(send(connect_fd, "Hello,you are connected! ", 26,0) == -1)
- perror("send error");
- close(connect_fd);
- exit(0);
- }
- buff[n] = ' ';
- printf("recv msg from client: %s ", buff);
- close(connect_fd);
- }
- close(socket_fd);
- }
客户端:
- /* File Name: client.c */
- #include<stdio.h>
- #include<stdlib.h>
- #include<string.h>
- #include<errno.h>
- #include<sys/types.h>
- #include<sys/socket.h>
- #include<netinet/in.h>
- #define MAXLINE 4096
- int main(int argc, char** argv)
- {
- int sockfd, n,rec_len;
- char recvline[4096], sendline[4096];
- char buf[MAXLINE];
- struct sockaddr_in servaddr;
- if( argc != 2){
- printf("usage: ./client <ipaddress> ");
- exit(0);
- }
- if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){
- printf("create socket error: %s(errno: %d) ", strerror(errno),errno);
- exit(0);
- }
- memset(&servaddr, 0, sizeof(servaddr));
- servaddr.sin_family = AF_INET;
- servaddr.sin_port = htons(8000);
- if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){
- printf("inet_pton error for %s ",argv[1]);
- exit(0);
- }
- if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){
- printf("connect error: %s(errno: %d) ",strerror(errno),errno);
- exit(0);
- }
- printf("send msg to server: ");
- fgets(sendline, 4096, stdin);
- if( send(sockfd, sendline, strlen(sendline), 0) < 0)
- {
- printf("send msg error: %s(errno: %d) ", strerror(errno), errno);
- exit(0);
- }
- if((rec_len = recv(sockfd, buf, MAXLINE,0)) == -1) {
- perror("recv error");
- exit(1);
- }
- buf[rec_len] = ' ';
- printf("Received : %s ",buf);
- close(sockfd);
- exit(0);
- }
测试:
编译server.c
gcc -o server server.c
启动进程:
./server
显示结果:
======waiting for client's request======
并等待客户端连接。
编译 client.c
gcc -o client server.c
客户端去连接server:
./client 127.0.0.1
等待输入消息
发送一条消息,输入:c++
此时服务器端看到:
客户端收到消息:
其实可以不用client,可以使用telnet来测试:
telnet 127.0.0.1 8000