前言:
最近写个代码要应用到socket通信,之前一直都在搞linux底层驱动,好久没接触到应用层了,复习一下。
一、socket通信简介
socket是一个网络传输的基本接口,类似于io中open,socket实际打开一个网络设备,返回一个fd。而socke可分类为SOCK_STREAM:流式套接字,传输数据的时候是一字节流传输(通过面向连接,tcp)、SOCK_DGRAM :数据包套接字,传输的时候以数据报(长度不定)(通过面向非连接,udp)、SOCK_RAW :原始套接字,主要是去操作底层的协议(ping的实现, icmp协议)。其实就是“open—write/read—close”模式的一种实现,那么socket就提供了这些操作对应的函数接口。下面以TCP为例,介绍几个基本的socket接口函数。
二、重要函数
1.创建套接字
int socket(int domain, int type, int protocol);
参数: 通信域--本机/跨机器通信
AF_UNIX/AF_LOCAL: 本机通信
AF_INET :跨机器通信--用的协议族ipv4
参数2:传输什么类型是数据
SOCK_STREAM: 传输是字节流---tcp
SOCK_DGRAM :传输的是数据报-- udp
SOCK_RAW : 传输的是内部协议--icmp
参数3:具体是哪个协议--0
用法:
tcp编程:
socket(AF_INET, SOCK_STREAM, 0);
udp编程:
socket(AF_INET, SOCK_DGRAM, 0);
2, bind将进程和端口号和ip地址(当前主机)进行绑定,就可以被别人连接
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数1:当前的创建的socket
参数2:地址和端口的信息
参数3:地址和信息的长度
地址和端口的信息:struct sockaddr *addr (通用地址)
实际用: struct sockaddr_in
struct sockaddr_in
{
u_short sin_family; // 地址族, AF_INET,2 bytes
u_short sin_port; // 端口,2 bytes, 自己选择,转换成网络端字节序( 大端)htons();
struct in_addr sin_addr; // IPV4地址,4 bytes
char sin_zero[8]; // 8 bytes unused,作为填充
};
struct in_addr
{
in_addr_t s_addr; // u32 network address
};
初始化struct sockaddr_in
struct sockaddr_in self_addr;
self_addr.sin_family = AF_INET;
self_addr.sin_port = htons(9999);
self_addr.sin_addr.s_addr = inet_addr("192.168.7.5");
使用: grep -rHn 'struct sockaddr_in' /usr/include/
#include <linux/in.h>
int bind(fd, (const struct sockaddr * )&self_addr, sizeof(self_addr));
3, listen();表示监听,设置在同一时间点,可以同时连接的最大的客户端的个数
int listen(int sockfd, int backlog);
参数2:最大的客户端的个数, 5-10
4,accept()等待客户端的连接,阻塞
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数1: 服务端的创建的socket对应的fd
参数2,3表示客户端的信息,当然也可以不关心对方是谁,就可以填NULL, NULL
返回值:返回一个新的文件描述符,和客户端进行数据通信的
最简单的用法:
int new_fd = accept(fd, NULL, NULL);
5.int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
参数2:表示连接到哪个服务器,需要给出信息
三、A for B to C 小程序编写
#ifndef __NET_H__
#define __NET_H__
#include <stdio.h>
#include <string.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/select.h>
#include <poll.h>
#include "list.h"
struct node_list{
int fd;
char mark;
/*client node*/
struct list_head node;
};
client:
#include "net.h"
#define SRV_PORT 9999
int main(int argc, char *argv[])
{
if(argc < 2)
{
printf("usage : ./client serverip\n");
exit(1);
}
int ret;
char keybuf[128];
char rbuf[128];
int len = 0;
int fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd < 0)
{
perror("socket");
exit(1);
}
// connect
struct sockaddr_in peer;
peer.sin_family = AF_INET;
peer.sin_port = htons(SRV_PORT);
//inet_aton(SRV_IP, &peer.sin_addr);
inet_aton(argv[1], &peer.sin_addr);
ret = connect(fd, (struct sockaddr *)&peer, sizeof(peer));
if(ret < 0)
{
perror("connect");
exit(1);
}
// 监控多个文件
struct pollfd pfd[2];
pfd[0].fd = 0;
pfd[0].events |= POLLIN;
pfd[1].fd = fd;
pfd[1].events |= POLLIN;
while(1)
{
ret = poll(pfd, 2, -1);
if(ret > 0)
{
if(pfd[0].revents & POLLIN) //键盘有输入
{
printf("请输入信息:");
len = read(0, keybuf, 128);
write(fd, keybuf, len); //发送到服务端
bzero(keybuf,sizeof(keybuf));
printf("\n");
}
if(pfd[1].revents & POLLIN)
{
ret = read(fd, rbuf, 128);
if(ret == 0)
{
printf("服务器失连!!\n");
exit(1);
}
rbuf[ret] = '\0';
printf("接收到");
printf("%s\n", rbuf);
}
}
}
close(fd);
return 0;
}
server:
#include "net.h"
#include "list.h"
#define SRV_PORT 9999
int main(int argc, char *argv[])
{
if(argc < 1)
{
printf("Usage: ./server port");
exit(1);
}
int srv_fd = -1;
int new_fd = -1;
int ret = -1;
char buf[128];
char ch = 'A';
LIST_HEAD(client_list);//创建链表client_list
//创建sockt接口
srv_fd = socket(AF_INET, SOCK_STREAM, 0);
if(srv_fd < 0)
{
perror("socket");
exit(1);
}
//配置socket接口
int on = 1;
ret = setsockopt(srv_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if(ret < 0)
{
perror("setsockopt");
exit(1);
}
//端口绑定
struct sockaddr_in self;
self.sin_family = AF_INET;
self.sin_port = htons(SRV_PORT);
//self.sin_addr.s_addr = inet_addr("192.168.7.5");
self.sin_addr.s_addr = htonl(INADDR_ANY); //本机ip
ret = bind(srv_fd, (const struct sockaddr *)&self, sizeof(self));
if(ret < 0)
{
perror("bind");
exit(1);
}
//监听
listen(srv_fd, 6);
printf("|-_-| :等待client连接~~~~~\n");
// 初始化select
int maxfd = srv_fd;
fd_set rd_set;
FD_ZERO(&rd_set);
while(1)
{
//FD_SET(0, &rd_set);//监控键盘
FD_SET(srv_fd, &rd_set);
struct node_list *pnode;
if(!list_empty(&client_list))//查看链表内部是否为空
{//遍历链表,并设置每个fd监听
list_for_each_entry(pnode,&client_list,node)
{
FD_SET(pnode->fd, &rd_set);
}
}
ret = select(maxfd+1, &rd_set, NULL, NULL, NULL);
if(ret > 0) //表示有响铃--有数据
{
/*if(FD_ISSET(0, &rd_set))//表示有键盘输入
{
fgets(buf, 128, stdin); //此时不需要等了,一定可以拿到数据
if(new_fd > 0)
{
write(new_fd, buf, 128);
}
else
{
printf("keybuf = %s\n", buf);
}
}*/
if(FD_ISSET(srv_fd, &rd_set))//表示有client连接
{
struct sockaddr_in client_addr;
socklen_t len = sizeof(client_addr);
new_fd = accept(srv_fd, (struct sockaddr *)&client_addr, &len);
if(new_fd < 0)
{
perror("accept");
exit(1);
}
//加入到链表中统一管理
struct node_list *new_node = malloc(sizeof(struct node_list));
new_node->fd = new_fd;
new_node->mark = ch;
ch = (char)(ch + 1);
list_add_tail(&new_node->node, &client_list);//将已连接的客户fd以及标志存入链表client_list中
printf("%c客户端(%s)已连接---\n",new_node->mark,inet_ntoa(client_addr.sin_addr));
//设定最大值
struct node_list *pnode;
list_for_each_entry(pnode,&client_list, node)
{
if(maxfd < pnode->fd)
maxfd = pnode->fd;
}
maxfd+=1;
}
struct node_list *pnode,*tnode,*pnode_tag;
list_for_each_entry(pnode,&client_list, node)
{
if(FD_ISSET(pnode->fd, &rd_set)) //表示客户端有数据
{
//读取客户端发来的数据
ret = read(pnode->fd, buf, 128);
buf[ret] = '\0';
printf("%c--->%s \n",pnode->mark,buf);
if(ret < 0)
{
perror("read");
exit(1);
}
if(ret == 0)
{
printf("%c客户端已断开连接---\n",pnode->mark);
FD_CLR(pnode->fd,&rd_set);//从取消该fd监控
list_del_init(&pnode->node);// 从链表中移除
close(pnode->fd);//关闭连接
free(pnode);
ch=(char)(ch-1);//移除客户端编号
break;
}
list_for_each_entry(pnode_tag,&client_list,node)
{
if(pnode_tag->mark == buf[0])
{
//write(pnode_tag->fd,buf,128);
tnode=pnode_tag;
}
}
//发送数据到目标客户端
buf[0]=pnode->mark;
int ret=write(tnode->fd,buf,128);
if(ret < 0)
{
perror("write");
exit(1);
}
}
}
}
}
close(srv_fd);
return 0;
}