一:api详解
二:服务端:api接口说明
1.int socket(AF_INET/AF_INET6, SOCK_STREAM/SOCK_DGRAM, IPPROTO_TCP/IPPTOTO_UDP)
参数一:AF_INET(ipv4) AF_INET6(ipv6)
参数二: SOCK_STREAM(面向连接的传输方式). SOCK_DGRAM(面向无连接传输方式)
参数三:IPPROTO_TCP(tcp协议) IPPTOTO_UDP(udp协议)
返回值:小于0 表示创建socket套接字失败
2.int bind(socket,(struct sockaddr *)&addr_info,sizeof(addr_info))
参数一:socket套接字
参数二:本级ip地址+端口号信息结构体
信息结构体的定义及初始化:1. struct sockaddr_in addr_info; 2. addr_info.sin_family = AF_INET; //表示使用ipv4
3.addr_info.sin_addrs.sin_addr = htonl(INADDR_ANY);INADDR_ANY表示能收到任意网卡连接的
4.addr_info.sin_port = htons(端口号);
参数三:参数二结构体的大小
返回值:小于0表示绑定失败
3.int listen(socket,最大连接数量)
参数一:socket
参数二:最大客户端连接数
返回值:小于0表示绑定失败
4.int accept(socket, (struct sockaddr *)&accept_addr_info,&len)
参数一:socket
参数二:表示连接的客户端信息的结构体
参数三:参数二结构体的大小
返回值:返回值为通信套接字,用于服务器与客户端之间通信
定义客户端信息结构体:1. struct sockaddr_in * accept_addr_info;2. socklen_t len = sizeof(accept_addr_info);
5.收发消息send()/recv() read()/write()
ssize_t send(accept,buf,sizeof(buf)); recv()函数同理;
参数一:accept套接字
参数二:接受数据的字符串数组 buf
参数三:buf大小
返回值:成功返回写入/读出的字节数,失败返回-1;
socket缓冲区及阻塞模式,数据的写入和读取有两个区,分为写入缓存区和读入缓存区。当send/write时,数据不是直接发送到网络而是先把数据写入缓存区,写入成功函数返回值就默认成功,此时关闭套接字数据仍会发送并且数据并不是写入一次就立即发送,会受到网络环境和进程的影响。recv/read 读取数据同理,也是从输入缓存区读取
6.tcp进阶函数
int setsockopt(int socket, int level, int optname, void * optval, socklen_t * optlen)
作用:设置套接字描述符的属性
参数一:socket套接字
参数二:表示套接字使用的协议(如,ipv4、 ipv6、tcp、stcp,通用协议用SOL_SOCKET)
参数三:与参数二是对应关系,参数二不同的协议下面有多个选项,通过参数三进行设置
参数四:指向某个变量的指针,该变量是要设置新值的缓冲区。可以是一个结构体,也可以是普通变量
参数五:socklen_t * optlen,即参数四值的大小
返回值:成功返回0,失败返回-1;
补充:参数二与参数三的对应关系
level = SOL_SOCKET
optname = SO_BROADCAST(允许发送广播数据) SO_DEBUG(允许调试) SO_LINGER(延迟关闭连接) SO_OOBINLINE(带外数据放入正常数据流) SO_RCVBUF(接收缓冲区大小) SO_SNDBUF(发送缓冲区大小) SO_RCVLOWAT(接收缓冲区下限) SO_SNDLOWAT(发送缓冲区下限) SO_RCVTIMEO(接收超时) SO_SNDTIMEO(发送超时) SO_REUSEADDR(ip+端口复用) SO_REUSEPORT(ip+端口复用)
level = IPPROTO_IP
optname = IP_HDRINCL(在数据包中包含IP首部). IP_OPTINOS(IP首部选项). IP_TTL(生存时间)
level = IPPRO_TCP
optname = TCP_MAXSEG(TCP最大数据段的大小). TCP_NODELAY(不使用Nagle算法)
7.服务端代码(添加poll+线程 i/o多路复用)
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <ctype.h>
#include <poll.h>
#include <arpa/inet.h>
#include <pthread.h>
pthread_t ptid;
using namespace std;
int connect_fd,accept_fd;
struct sockaddr_in connect_addr;
struct sockaddr_in accept_addr;
char buf[32];
int max_t = 3;
socklen_t len = sizeof(accept_addr);
struct pollfd poll_fds[10];
int init_connect(){
connect_fd = socket(AF_INET, SOCK_STREAM, 0);
if (connect_fd < 0){
perror("******* create socket fd error ***\n");
return -1;
}
printf("======== socket_fd ok ============\n");
connect_addr.sin_addr.s_addr = htonl(INADDR_ANY);
connect_addr.sin_family = AF_INET;
connect_addr.sin_port = htons(8888);
int on = 1;
setsockopt(connect_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
if (bind(connect_fd,(struct sockaddr *)&connect_addr,sizeof(connect_addr)) < 0){
perror("******** ip地址绑定失败 **********\n");
return -1;
}
printf("======== bind ip ok ============\n");
if (listen(connect_fd,10) < 0){
perror("****** 本机IP地址监听失败 ********\n");
return -1;
}
printf("======== listener ip ok ============\n");
return connect_fd;
}
void *accept_msg_while(void *fd){
int ffd = *(int *)fd;
while(1){
if (read(ffd, buf, sizeof(buf)) <= 0){
printf("^^^^^^^^ip %u 连接断开或连接出错 ^^^^^^^^^^^^\n",ntohl(accept_addr.sin_addr.s_addr));
printf("^^^^^^^^ 等待下次连接 ^^^^^^^^^^^^^^^^^^\n");
break;
}
else{
printf("^^^^ accept fd = %d ^^^^^^\n",ffd);
printf("*** recv ip %d msg : %s \n",(int)accept_addr.sin_addr.s_addr,buf);
}
memset(buf,0,sizeof(buf));
}
close(ffd);
}
int accept_msg(){
while(1){
accept_fd = accept(connect_fd,(struct sockaddr *)&accept_addr,&len);
if(accept_fd < 0){
perror("******* accept error ********\n");
return -1;
}
printf("======== accept ok ==== fd val = %d =======\n",accept_fd);
if (accept_fd > max_t){
poll_fds[max_t].fd = accept_fd;
poll_fds[max_t].events = POLLIN;
max_t = max_t +1;
}
else{
poll_fds[accept_fd - 1].fd = accept_fd;
poll_fds[accept_fd].events = POLLIN;
}
if (poll(poll_fds,max_t,0) < 0){
perror("--- poll err -----\n");
}
for(int i=0;i < max_t+1;i++){
if (poll_fds[i].fd == accept_fd && (poll_fds[i].events && POLLIN)){
pthread_create(&ptid, NULL, accept_msg_while, (void *)&accept_fd);
memset(buf,0,sizeof(buf));
}
}
memset(buf,0,sizeof(buf));
}
pthread_exit(NULL);
return 0;
}
int main(){
init_connect();
if (connect_fd < 0){
return -1;
}
accept_msg();
for(int i=3;i < max_t;i++){
close(poll_fds[i].fd);
printf("^^^^^ 已关闭accpect fd %d ^^^^^^^^^^\n",poll_fds[i].fd);
}
close(connect_fd);
return 0;
}
8.客户端代码,待优化
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
#include <signal.h>
#include <arpa/inet.h>
#define MAXSIZE 1024
#define IP_ADDR "127.0.0.1"
#define IP_PORT 8888
int connect_fd = -1;
int init_connect(){
struct sockaddr_in con_sock;
if((connect_fd = socket(AF_INET, SOCK_STREAM, 0) ) < 0) //建立套接字
{
printf("=====socket connect Error: %s (errno: %d) ====\n", strerror(errno), errno);
return -1;
}
memset(&con_sock, 0, sizeof(con_sock));
con_sock.sin_family = AF_INET; //IPv4协议
//IP地址转换(直接可以从物理字节序的点分十进制 转换成网络字节序)
if(inet_pton(AF_INET, IP_ADDR, &con_sock.sin_addr) <= 0)
{
printf("inet_pton Error: %s (errno: %d)\n", strerror(errno), errno);
return -1;
}
con_sock.sin_port = htons(IP_PORT); //端口转换(物理字节序到网络字节序)
if(connect(connect_fd, (struct sockaddr*)&con_sock, sizeof(con_sock)) < 0) //主动向设置的IP和端口号的服务端发出连接
{
printf("connect Error: %s (errno: %d)\n", strerror(errno), errno);
return -1;
}
return 0;
}
int send_to_sevice(){
char msg[32];
fflush(stdin);
printf("--- 请输入消息内容 ---\n");
fgets(msg, MAXSIZE, stdin); //获取终端输入
printf("msg size = %lu \n",sizeof(msg));
if(strncmp(msg,"quit",4)==0){
printf("======= 客户端即将结束 ======\n");
close(connect_fd);
//break;
}
printf("will send: %s", msg);
if(write(connect_fd, msg, sizeof(msg)) < 0) //发送数据
{
printf("write Error: %s (errno: %d)\n", strerror(errno), errno);
exit(1);
}
else{
printf("==== 消息发送成功======\n");
}
memset(msg, 0, sizeof(msg));
return 0;
}
int quit(){
char msg[32];
fgets(msg, MAXSIZE, stdin); //获取终端输入
if(strncmp(msg,"quit",4)==0){
printf("======= 客户端即将结束 ======\n");
close(connect_fd);
return 3;
}
return 3;
}
int main()
{
printf("+++++++++++++++++++++++++++++++++++++++\n");
printf("+++++++++++++++++++++++++++++++++++++++\n");
printf("+++++++ 欢迎使用xxx客户端 +++++++++++++++\n");
printf("+++++++++++++++++++++++++++++++++++++++\n");
printf("++ 请选择功能 : \n");
printf("1. 连接 2。发送消息 3.退出 \n");
printf("+++++++++++++++++++++++++++++++++++++++\n");
int flag,connect_status;
int q;
while((scanf("%d",&flag)) != EOF){
printf("您输入的选项为 %d \n",flag);
switch(flag){
case 1:
init_connect();
break;
case 2:
send_to_sevice();
break;
case 3:
q = quit();
break;
default:
printf("----输入参数有误--\n");
}
if(q == 3)
break;
}
return 0;
}