基于UDP c/s通信模型
- 客户端(socket;sendto ...)
- 服务器端 ---把自己的地址公布出去 (socket;bind //绑定; recvfrom ...)
1.recvfrom函数:
ssize_t recvfrom( int sockfd, //socket的fd
void *buf, //保存数据的一块空间的地址
size_t len, //这块空间的大小
int flags, // 0 默认的接收方式 --- 阻塞方式
struct sockaddr *src_addr, //用来保存发送方的地址信息
socklen_t *addrlen //表示发送方实际的地址信息大小 );
返回值:成功:返回接收到的字节数; 失败:-1。
struct sockaddr_in srcaddr;
socklen_t addrlen = sizeof(srcaddr);char buf[1024];while(1)
{recvfrom(fd,buf,sizeof(buf),0,(struct sockaddr *)&srcaddr,&addrlen);printf("IP = %s\n",inet_ntoa(srcaddr.sin_addr));printf("post = %d\n",ntohs(srcaddr.sin_port));printf("buf = %s\n",buf);
}
2.bind函数:
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
(1)功能:如果该函数在服务器端调用,则表示将参数1相关的文件描述符文件与参数2 指定的接口地址关联,用于从该接口接受数据。如果该函数在客户端调用,则表示要将数据从参数1所在的描述符中取出并从参数2所在的接口设备上发送出去。
注意:如果是客户端,则该函数可以省略,由默认接口发送数据。
(2)参数:sockfd之前通过socket函数创建的文件描述符,套接字id; my_addr 是物理接口的结构体指针。表示该接口的信息。
struct sockaddr 通用地址结构 --- ip + 端口
{u_short sa_family; 地址族char sa_data[14]; 地址信息
};转换成网络地址结构如下:
struct sockaddr_in ///网络地址结构
{u_short sin_family; //地址族u_short sin_port; //地址端口struct in_addr sin_addr; //地址IP //"192.168.1.123"char sin_zero[8]; //占位
};in_addr_t === unsigned int
struct in_addr
{in_addr_t s_addr;
}
练习:
任务1 从键盘获得数据发给服务器 --fgets
任务2 从服务器不断获取数据打印 --recvfrom
1.客户端:
int c_fd = 0;
void child_handler(int signo)
{close(c_fd);printf("child--exit--\n");exit(0);}void father_handler(int signo)
{close(c_fd);printf("father--exit--\n");exit(0);
}int main(int argc, const char *argv[])
{//1.socket 创建通信的一端 int fd = socket(AF_INET,SOCK_DGRAM,0);if (fd < 0){perror("socket fail");return -1;}printf("fd = %d\n",fd);struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50000);seraddr.sin_addr.s_addr = inet_addr("192.168.1.135");char buf[1024] = {0};//区分任务 pid_t pid = fork();if (pid < 0){perror("fork fail");return -1;}if (pid > 0){signal(SIGCHLD,father_handler);c_fd = fd;while (1){printf(">");fgets(buf,sizeof(buf),stdin);int ret = sendto(fd,buf,strlen(buf)+1,0,(const struct sockaddr *)&seraddr,sizeof(seraddr));printf("ret = %d\n",ret);if (strncmp(buf,"quit",4) == 0){kill(pid,SIGUSR1);wait(NULL);close(fd);exit(0);}}}else if (pid == 0) {c_fd = fd;signal(SIGUSR1,child_handler);while (1){recvfrom(fd,buf,sizeof(buf),0,NULL,NULL);printf("buf = %s\n",buf);if (strncmp(buf,"quit",4) == 0){close(fd);exit(0);}}}//close(fd); return 0;
}
2.服务器端:
int c_fd = 0;
void child_handler(int signo)
{close(c_fd);printf("child--exit--\n");exit(0);}void father_handler(int signo)
{close(c_fd);printf("father--exit--\n");exit(0);
}int main(int argc, const char *argv[])
{//1.socket 创建通信的一端 int fd = socket(AF_INET,SOCK_DGRAM,0);if (fd < 0){perror("socket fail");return -1;} struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50000);seraddr.sin_addr.s_addr = inet_addr("192.168.1.135");//2.bindif (bind(fd,(struct sockaddr *)&seraddr,sizeof(seraddr)) < 0){perror("bind fail");return -1;}struct sockaddr_in srcaddr;socklen_t addrlen = sizeof(srcaddr);char buf[1024];//3.接收数据recvfrom(fd,buf,sizeof(buf),0,(struct sockaddr*)&srcaddr,&addrlen);printf("--------------------\n");printf("IP = %s\n",inet_ntoa(srcaddr.sin_addr));printf("post = %d\n",ntohs(srcaddr.sin_port));printf("buf = %s\n",buf);printf("--------------------\n");pid_t pid = fork();if (pid < 0){perror("fork fail");return -1;}if (pid > 0){signal(SIGCHLD,father_handler);c_fd = fd;while (1){//3.接收数据recvfrom(fd,buf,sizeof(buf),0,(struct sockaddr*)&srcaddr,&addrlen);printf("--------------------\n");printf("IP = %s\n",inet_ntoa(srcaddr.sin_addr));printf("post = %d\n",ntohs(srcaddr.sin_port));printf("buf = %s\n",buf);printf("--------------------\n");if (strncmp(buf,"quit",4) == 0){kill(pid,SIGUSR1);wait(NULL);close(fd);exit(0);}}}else if (pid == 0){signal(SIGUSR1,child_handler);while (1){printf(">");fgets(buf,sizeof(buf),stdin);sendto(fd,buf,strlen(buf) + 1, 0, (const struct sockaddr*)&srcaddr,sizeof(srcaddr));if (strncmp(buf,"quit",4) == 0){close(fd);exit(0);}}} return 0;
}
基于TCP c/s通信模型
tcp 客户端:
(1)建立连接:
- socket //买了一部手机;
- bind //可选 //插上sim卡;
- connect //拨打电话 ....
(2)通信过程:(read;write;close)
connect函数:
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
(1)功能:该函数固定有客户端使用,表示从当前主机向目标主机发起链接请求。
(2)参数:sockfd:本地socket创建的套接子id;addr:程目标主机的地址信息;
addrlen: 参数2的长度。
(3)返回值:成功 0;失败 -1;
int main(int argc, const char *argv[])
{int fd = socket(AF_INET,SOCK_STREAM,0);if (fd < 0)handle_error("socket fail"); struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50000);seraddr.sin_addr.s_addr = inet_addr("192.168.1.135");if (connect(fd,(const struct sockaddr*)&seraddr,sizeof(seraddr)) < 0)handle_error("connect fail");return 0;
}
tcp服务器:
(1)建立连接
- socket //买了一部手机
- bind //插上sim卡
- listen //监听 客户端的连接请求
- accept //接听 ---这一步完成之后 连接就建立好了,之后就可以收发数据
(2)通信过程 (read;write;close)
listen函数:
int listen(int sockfd, int backlog);
accept函数:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
int main(int argc, const char *argv[])
{int listen_fd = socket(AF_INET,SOCK_STREAM,0);if (listen_fd < 0)handle_error("socket fail"); struct sockaddr_in seraddr;seraddr.sin_family = AF_INET;seraddr.sin_port = htons(50000);seraddr.sin_addr.s_addr = inet_addr("192.168.1.135");if (bind(listen_fd,(const struct sockaddr*)&seraddr,sizeof(seraddr)) < 0)handle_error("bind fail");if (listen(listen_fd,5) < 0)handle_error("listen fail");int connfd = accept(listen_fd,NULL,NULL);if (connfd < 0)handle_error("accept fail");printf("connfd = %d\n",connfd);return 0;
}
struct sockaddr *addr //通用地址结构类型,并没有实际去用,实际用到都是具体地址结构类型
struct sockaddr_in {
sa_family_t sin_family; /* address family: AF_INET */
in_port_t sin_port; /* port in network byte order */
struct in_addr sin_addr; /* internet address */
};/* Internet address. */
struct in_addr {
uint32_t s_addr; /* address in network byte order */
};