1.套接字编程基础知识
1.1套接字地址结构
1.通用套接字数据结构
struct sockaddr{
//套接字地址结构
sa_family sa_family; //协议族
char sa_data[14]; //协议族数据
}
1.2实际使用的套接字数据结构
在网络程序设计中所使用的函数中几乎所有套接字函数都用struct sockaddr结构作为参数
int bind(int sockfd, //套接字文件描述符
const struct sockaddr *my_addr, //套接字地址结构
socklen_t addrlen); //套接字地址结构长度
但由于结构struct sockaddr不方便进行设置,因此,在以太网中,一般采用struct sockaddr_in进行设置
struct sockaddr_in{
//以太网套接字地址结构
u8 sin_len; //结构的长度,16
u8 sin_family; //通常为AF_INET
u16 sin_port; //16位的端口号,网络字节序
struct in_addr sin_addr; //IP地址32位
char sin_zero[8]; //未用
}
调用时需强转成sockaddr
struct sockaddr_in addr;
bind(fd,(struct sockaddr *)&addr, size);
2.TCP网络编程流程
2.2 创建网络插口函数
- 函数介绍
#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
成功:新套接字所对应的文件描述符,失败-1 errno
domain:用于设置网络通信的域,函数socket()根据这个参数选择通信协议的族
AF_INET :IPv4协议
AF_INET6 :IPv6协议
AF_UNIX :本地通信
type:用于设置套接字通信的类型
SOCK_STREAM:流式套接字(TCP)
SOCK_DGRAM:数据包套接字(UDP)
protocol:用于指定某个协议的特定类型,通常某个协议只有一种特定类型,因此protocol仅能设置为0
2.3 绑定一个地址端口对bind()函数
- 函数介绍
bind()函数将长度为addlen()的struct sockadd类型的参数my_addr与sockfd绑定一起,将sockfd绑定到某个端口上,如果使用connect()函数则没有绑定的必要。
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);
成功:0,失败-1 errno
sockfd是用socket()函数创建的文件描述符
my_addr是指向一个结构为sockaddr参数的指针
struct sockaddr_in addr;
addr.sin_family = AF_INET
addr.sin_port = htons(8888);
addr.sin_addr.s_addr = hton1(INADDR_ANY);
my_addr : (struct sockaddr *)&addr //需要强制转换
addrlen是my_addr结构的长度,可以设置成sizeof(addr)
2.4监听本地端口listen()
函数listen()用来初始化服务器可连接队列。设置监听上限,服务器处理客户端连接请求的时候是顺序处理的,同一时间仅能处理一个客户端的连接请求同时到来的时候,服务器并不是同时处理,而是将不能处理的客户端连接请求放到等待队列中,这个队列长度由listen()函数来定义。
- 函数介绍
#include <sys/socket.h>
int listen(int sockfd, int backlog);
成功:0,失败-1 errno
sockfd是用socket()函数创建的文件描述符
backlog:上限数值,最大128.
2.5接受一个网络请求accept()函数
阻塞等待客户端建立连接,通过accept()函数可以得到客户端的IP地址、端口和协议族等信息。
- 函数介绍
#include<sys/types.h>
#include<sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
成功 返回能与服务器进行数据通信的socket对应的文件描述符 失败 -1 errno
sockfd:socket()函数的返回值
addr:传入参数。成功与服务器建立连接的那个客户端的地址结构(IP+port)
addrlen:表示addr的长度,可以使用addr_len=sizeof(struct sockaddr_in)来获得,输入**&addr_len**。
2.6连接目标网络服务器connect()函数
用来连接服务器
- 函数介绍
#include <sys/types.h>
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
成功返回0,失败返回-1,设置errno
sockdf:socket文件描述符
addr:传入参数,指定服务器端地址信息,含IP地址和端口号
addrlen:传入参数,传入sizeof(addr)大小
3.简单例子
server
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <errno.h>
#include <ctype.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#define SERV_PORT 9527 //服务器端口地址
void sys_err(const char *str){
perror(str);
exit(1);
}
int main(int argc, char *argv[])
{
int lfd = 0 , cfd = 0; //lfd为服务器的socket描述符 ,cfd是客户端的socket描述符
int err; //返回值
int ret; //读取数据的长度
char buf[BUFSIZ] ,client_IP[1024]; //buf缓存读取的数据 ,client_IP缓存客户端IP
struct sockaddr_in serv_addr , clit_addr; //服务器和客户端的地址结构
serv_addr.sin_family = AF_INET; //协议族
serv_addr.sin_port = htons(SERV_PORT); //服务器端口
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); //本地地址
socklen_t clit_addr_len;
/*建立一个流失套接字*/
lfd = socket(AF_INET, SOCK_STREAM, 0);
if(lfd == -1){
sys_err("socket error");
}
/*绑定地址结构到套接字描述符*/
err = bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
if(err == -1){
sys_err("bind error");
}
/*设置侦听*/
err = listen(lfd,128);
if(err == -1){
sys_err("listen error");
}
/*接受网络请求*/
clit_addr_len = sizeof(clit_addr);
cfd = accept(lfd,(struct sockaddr *)&clit_addr, &clit_addr_len);
/*打印客户端地址*/
printf("client ip:%s port:%d\n",
inet_ntop(AF_INET,&clit_addr.sin_addr.s_addr,client_IP,sizeof(client_IP)),
ntohs(clit_addr.sin_port));
if(cfd == -1){
sys_err("accept error");
}
/*将读取的数据改为大写返回客户端*/
while(1){
ret = read(cfd , buf ,sizeof(buf));
write(STDOUT_FILENO,buf,ret);
for (int i = 0 ;i< ret ; i++)
{
buf[i] = toupper(buf[i]); //小写转大写
}
write(cfd,buf,ret);
}
close(lfd);
close(cfd);
return 0;
}
client
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <errno.h>
#define SERV_PORT 9527 //客户端地址
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int main(int argc,char *argv[]){
int cfd;
int err; //返回值
int conter = 10;
char buf[BUFSIZ];
struct sockaddr_in serv_addr;
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(SERV_PORT);
// inet_pton(AF_INET,"127.0.0.1",&serv_addr.sin_addr.s_addr);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
//建立流式套接字
cfd = socket(AF_INET,SOCK_STREAM,0);
if(cfd == -1)
sys_err("socket error");
/*连接服务器*/
err = connect(cfd,(struct sockaddr *)&serv_addr, sizeof(serv_addr));
if (err == -1){
printf("err = %d",err);
sys_err("connect error");
}
while(-- conter){
write(cfd,"hello\n",6);
int ret = read(cfd,buf,sizeof(buf));
write(STDOUT_FILENO,buf,ret);
sleep(1);
}
close(cfd);
return 0;
}
结果