基于多台linux主机通过1台服务器进行socket通讯小程序编写

前言:

最近写个代码要应用到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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值