Socket网络编程

Socket——进程间(远程)通信的一种机制。

套接字(Socket):IP地址与端口号的组合。

  两方进行通信时,必须要知道对方主机的IP地址和进程的端口号。

  在 Linux 中的网络编程是通过 socket 接口来进行的。套接字(socket)是一种特殊的 I/O 接口,它也是一种文件描述符。socket 也有一个类似于打开文件的函数调用,该函数返回一个整型的 socket 描述符,随后的连接建立、数据传输等操作都是通过 socket 来实现的。

  套接字可分为3种类型:

  (1)字节流Socket(Stream Socket):基于TCP,提供可靠的字节流传输;

  (2)数据报Socket(Datagram Socket):基于UDP,提供不可靠的报文传输;

  (3)原始套接字 Raw Socket:基于IP,允许用户直接对IP操作;

 

Socket操作的常用系统调用:

  • socket():建立Socket端点,获得Socket描述符
  • bind():Server绑定Socket地址(IP地址+端口号)
  • listen():Server等待Client连接,是一个阻塞操作
  • connect():Client连接到Server
  • accept():Server获得连接请求的 Client的Socket地址
  • send()、recv():在已建立的连接上发送、接收数据(TCP方式)
  • sendto()、recvfrom():无需连接,直接发送、接收数据(UDP方式)
  • close():关闭Socket(单向/双向)

基于TCP的Socket程序流程:

  

具体步骤:

  1. 创建服务端socket,绑定建立连接的端口。
  2. 服务端程序在一个端口调用监听后,处于阻塞状态,等待客户机的连接。
  3. 创建客户端socket对象。
  4. 客户端指定主机名称或IP地址、连接端口号。
  5. 客户机socket发起连接请求。
  6. 建立连接。
  7. 利用send()和recv()进行数据传输。
  8. 关闭socket。

server.c

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>
int main()
{
    int sockfd,new_fd,numbytes;
    struct sockaddr_in my_addr;
    struct sockaddr_in their_addr;
    int sin_size;
    char buff[100];
    //服务器创建socket套接字描述符
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
    {
        perror("socket!");
        exit(1);
    }
    printf("socket Success!,sockfd=%d
",sockfd);
    //初始化sockaddr结构体,设置绑定4321端口
    my_addr.sin_family=AF_INET;
    my_addr.sin_port=htons(4321);//数据格式转换
    my_addr.sin_addr.s_addr=INADDR_ANY;//本机地址
    bzero(&(my_addr.sin_zero),8);
    //绑定套接字描述符sockfd
    if(bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))==-1)
    {
        perror("bind");
        exit(1);
    }
    printf("bind Success!
");
    //创建监听套接字
    if(listen(sockfd,10)==-1)
    {
        perror("listen");
        exit(1);
    }
    printf("Listen.....
");
    //服务器阻塞监听套接字,等待客户端连接
    while(1)
    {
        sin_size=sizeof(struct sockaddr_in);
        //如果建立连接,产生新套接字,用于与客户端通信
        if((new_fd = accept(sockfd,(struct sockaddr *)&their_addr,&sin_size))==-1)
        {
            perror("accept");
            exit(1);
        }
        //生成一个子进程来进行会话,父进程继续监听
        if(!fork())
        {
            //读取客户端发来的信息
            if((numbytes = recv(new_fd,buff,60,0))==-1)
            {
                perror("recv");
                exit(1);
            }
            printf("%s
",buff);
            //发送信息到客户端
            if(send(new_fd,"Welcome ,This is server.",60,0)==-1)
                perror("send");
            //本次通信结束
            close(new_fd);
            exit(0);
        }
        //
    }
    close(sockfd);
    return 0;
}

client.c

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
int main(int argc ,char * argv[])//命令行传递地址
{
    int sockfd,numbytes;
    char buff[100];
    struct hostent * he;
    struct sockaddr_in their_addr;
    int i=0;
    //命令行中的第二个参数为服务器IP
    he=gethostbyname(argv[1]);
    //客户端建立socket套接字描述符
    if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)
    {
        perror("socket!");
        exit(1);
    }
    //printf("socket Success!,sockfd=%d
",sockfd);
    their_addr.sin_family=AF_INET;
    their_addr.sin_port=htons(4321);
    their_addr.sin_addr=*((struct in_addr *)he->h_addr);
    bzero(&(their_addr.sin_zero),8);
    //向服务器发起连接
    if(connect(sockfd,(struct sockaddr *)&their_addr,sizeof(struct sockaddr))==-1)
    {
        perror("connect");
        exit(1);
    }
    //向服务器发送字符串
    if(send(sockfd,"hello!I am client.",26,0)==-1)
    {
        perror("send");
        exit(1);
    }
    //接收从服务器返回的信息
    if((numbytes = recv(sockfd,buff,100,0))==-1)
    {
        perror("recv");
        exit(1);
    }
    printf("recv is :%s
",buff);
    //通信结束
    close(sockfd);
    return 0;
}

 创建一个Web程序:

#include<stdio.h>-
#include<pthread.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>

int KEY_QUIT =0;
char referrer[128];
int content_length;
#define SERVER_PORT 80
static char copybuf[16384];
//复制一个word文档副本
int copy(FILE * read_f ,FILE * write_f)
{
    int n;
    int wrote;
    n=fread(copybuf ,1,sizeof(copybuf),read_f);
    wrote=fwrite(copybuf,n,1,write_f);
    return 0;
}
//发送HTML文件内容
int DoHTML(FILE * f ,char * name)
{
    char * buf;
    FILE * infile;
    infile=fopen(name,"r");
    copy(infile,f);
    fclose(infile);
    return 0;
}
//解析客户需求
int ParseReq(FILE * f ,char * r)
{
    char * bp;
    char * c;
#ifdef BEBUG
    printf("req is %s
",r);
#endif
while(*(++r)!=' ');
//判断是否为空白符(空格、换页、回车、制表符等)
while(isspace(*r)) r++;
while(*r=='/') r++;
bp=r;
while(*r &&(*(r)!=' ')&&(*(r)!='?'))
    r++;
*r=0;
c=bp;
DoHTML(f,c);
return 0;
}
//客户连接处理
int HandleConnect(int fd)
{
    FILE *f;
    char buf[160];
    f=fdopen(fd,"a+");
    setbuf(f,0);
    fgets(buf,150,f);
#ifdef DEBUG
    printf("buf=%s
",buf);
#endif
    ParseReq(f,buf);
    fflush(f);
    fclose(f);
    return 1;
}
//按Q键退出服务程序
void * Key_in(void * data)
{
    int c;
    for(;;)
    {
        c=getchar();
        if(c=='q'||c=='Q')
        {   KEY_QUIT=1;
            exit(10);
            break;
        }
    }
}
int main(int argc, char * argv[])
{
    int fd,sockfd;
    int len;
    volatile int true=1;
    struct sockaddr_in ec;
    struct sockaddr_in server_sockaddr;
    pthread_t th_key;
    printf("starting httpd...
");
    printf("press q to quit
");
    //建立socket套接字
    sockfd =socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
    setsockopt(sockfd,SOL_SOCKET, SO_REUSEADDR, (void *)&true ,sizeof(true));

    server_sockaddr.sin_family =AF_INET;
    server_sockaddr.sin_port=htons(SERVER_PORT);
    server_sockaddr.sin_addr.s_addr=htonl(INADDR_ANY);
    //把套接字socket与地址、端口号绑在一起
    bind(sockfd,(struct sockaddr *)&server_sockaddr,
            sizeof(server_sockaddr));
    //设置监听
    listen(sockfd,8*3);
//按Q键退出程序
//pthread_create(&th_key,NULL,Key_in,NULL); //等待客户端连接请求 printf("waitting for connection... "); while(1) { len=sizeof(ec); if((fd=accept(sockfd,(void *)&ec,&len))==-1) { exit(5); close(sockfd); } HandleConnect(fd); } }

然后再写一个网页脚本文件index.html。程序编译运行后,打开浏览器:http://IP地址/index.html。

原文地址:https://www.cnblogs.com/codercql/p/13052609.html