socket那些事----以linux C实现一个服务器端

数据发到网络环境通常通过交换机,再由交换机转发至路由器
但凡提到套接字,必定有两端:数据的发送端和接收端,
ip地址在网络环境中可以唯一的表示一台主机,ip+端口号可以在网络环境中唯一标识一个进程。
socket通信原理:
    套接字是linux操作系统中的一种文件类型----伪文件
    linux中有7种文件类型:其中普通文件,目录文件,软链接三种文件类型占用内存,字符设备,块设备,管道,套接字
    管道,fd[0]读端,fd[1]写端,半双工,如同对讲机
    socket乃是双向全双工,一个文件描述符,两个缓冲区(一个读,一个写)
对于数字8910.67
低地址存高位----大端法按照内存地址顺序由近及远依次存入8,9,1,0,.6.7
低地址存低位----小端法(与上边相反)x86就是如此,小端法存储,源于windows基于Intel x86平台
理论上是这样,不同操作系统可能不尽相同。

网络数据流采用了大端字节序,操作系统采用了小端法存储,这样一来网卡发送的数据字节序发生了变化,这样一来,接收端在解包的过程中无法正确解析IP地址等关键信息,这就需要能转换网络字节序和主机字节序的系列函数

uint32_t htonl(uint32_t hostlong)
uint16_t htons(uint16_t hostshort)
uint32_t ntohl(uint32_t netlong)
uint16_t ntohs(uint16_t netshort)

由于点分十进制是我们表达IP的惯用手法,但在网络环境中,这种方法显然无法满足要求,这就需要一组函数完成两种(点分十进制和网络字节序)规制的相互转换
int inet_pton(int domain,char * src, void* des) 第一个参数代表net版本类型 ipv4,或是ipv6
char * inet_ntop(int domain,const void * src, char* des socklen_t size)

描述ipv4地址协议的结构体(sockaddr_in)
典故与现实,早期描述ipv4协议的结构体是sockaddr,网络协议绑定函数bind()参数列表中就是这种结构体类型指针,然而定义ipv4协议类型结构体又只能使用 sockaddr_in类型,所以,为了兼容bind()函数要强转类型:
struct sockaddr_in sockipv4;
bind(struct sockaddr* &sockipv4)
linux 结构体查看命令 man 7 ip


socket
int socket(domain,type,protocol)
domain: ipv4 ipv6
type:流式协议,报式协议……
protocol: 默认0
成功,返回整型文件描述符,失败返回-1

int bind(int sockfd,const struct sockaddr* addr,socklen_t addrlen);往socket上绑定ip和端口号,成功返回0,失败返回-1

int listen()//指定监听上线数,允许同时建立连接(而非保持)的数量(可能允许的数量还未达到支持上限)
int accept(int fd,struct sockaddr* addr, socklen_t * size)//返回一个新的文件描述符,这一新的文件描述符,作为socket的accept端,
参数1:指定(我们创建的)套接字(以便基于此后续建立accept套接字)
参数2:传出参数,与我建立连接的客户端的地址信息。
参数3:传入传出参数,传入的读一次,传出写一次
返回:一个全新的socket文件描述符,用来同客户端进行通信

int connect(int sockfd,const struct sockaddr* addr,socklen_t addrlen)
参数1:客户端新建的socket
参数2:传入参数,指定服务器的地址信息,含ip地址和端口号。
参数3:传入参数,传入的addr大小。
  • 一个服务端的实现---功能:把客户端的输入字符转为小写
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#define SERV_PORT 6666//1024-65535
#define BUFSIZE 1024
int main()
{
    struct sockaddr_in serv_addr,client_addr;
    int sfd,cfd,n;
    char buf[BUFSIZE];
    sfd = socket(AF_INET,SOCK_STREAM,0);
    serv_addr.sin_family=AF_INET;
    serv_addr.sin_port=htons(SERV_PORT);
    serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    socklen_t lenofserv_addr = sizeof(serv_addr);
    bind(sfd,(const struct sock_addr*)(&serv_addr),lenofserv_addr);
    listen(sfd,128);
    socklen_t lenofclient_addr = sizeof(client_addr);
    cfd=accept(sfd,(const struct sockaddr*)(&client_addr),&lenofclient_addr);

   while(1)//while循环,要写在accept()函数后面,否则除非不断开启客户端,转换完大小写后就会阻塞等待客户端请求
   {
       n = read(cfd,buf,sizeof(buf));
       for(int i=0;i<n;i++)
       {
           buf[i] = tolower(buf[i]);
       }
       write(cfd,buf,n);
   }
    close(sfd);
    close(cfd);
    printf("Hello World!
");
    return 0;
}
  • 编译,启动,warning警示,

 

  • 如果你还没有写好一个客户端,可以开启一个shell终端,输入命令(当然,你的ip和端口号可能与我不同)
nc 127.0.0.1 6666

然后尽情使用吧

原文地址:https://www.cnblogs.com/saintdingspage/p/12267539.html