您的位置:首页 > 运维架构 > Linux

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

2017-01-05 12:06 555 查看
前言:

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

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: