美文网首页
socket编程小复习

socket编程小复习

作者: 转角遇见一直熊 | 来源:发表于2016-10-15 02:37 被阅读94次

准备做一个zmq专题,先复习一下socket的知识。

socket

我们知道两个进程如果需要进行通讯最基本的一个前提能能够唯一的标示一个进程,在本地进程通讯中我们可以使用PID来唯一标示一个进程,但PID只在本地唯一,网络中的两个进程PID冲突几率很大,这时候我们需要另辟它径了,我们知道IP层的ip地址可以唯一标示主机,而TCP层协议和端口号可以唯一标示主机的一个进程,这样我们可以利用ip地址+协议+端口号唯一标示网络中的一个进程。

能够唯一标示网络中的进程后,它们就可以利用socket进行通信了,什么是socket呢?我们经常把socket翻译为套接字,socket是在应用层和传输层之间的一个抽象层,它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。

socket

socket编程的基本流程

注意阻塞的部分

socket消息流程 三次握手

下面的例子对照图来看,可以大致说明整个编程模型。

服务端采用fork同时服务多个客户

注意服务器accept以后,返回的fd是在另一个进程中使用的。

服务器

/* server.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <netinet/in.h>

#include "wrap.h"

#define MAXLINE 80
#define SERV_PORT 8000

void sig_child(int signo);

int main(void)
{
        pid_t pid;
        struct sigaction act;
        struct sockaddr_in servaddr,cliaddr;
        socklen_t cliaddr_len;
        int listenfd,connfd;
        char buf[MAXLINE];
        char str[INET_ADDRSTRLEN];
        int i,n,m;
        
        memset(&act,0,sizeof(act));
        act.sa_handler = sig_child;
        
        if(sigaction(SIGCHLD,&act,0)){
                perror("Sigaction Error");
                return 1;
        }
        
        listenfd = Socket(AF_INET,SOCK_STREAM,0);
        bzero(&servaddr,sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
        servaddr.sin_port = htons(SERV_PORT);
        
        Bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr));
        
        Listen(listenfd,20);
        
        printf("Accepting connections ...\n");
        
        while(1)
        {
                cliaddr_len = sizeof(cliaddr);
                connfd = Accept(listenfd,(struct sockaddr *)&cliaddr,&cliaddr_len);
                m = fork();
                
                if(m == -1)
                {
                        perror("call to fork");
                        exit(1);
                } 
                else if(m == 0)
                {
                        
                        while(1)
                        {
                                n = Read(connfd,buf,MAXLINE);
                                if(n == 0){
                                        printf("the other side has been closed.\n");
                                        break;
                                }
                                printf("received from %d at PORT %d\n",inet_ntop(AF_INET,&cliaddr.sin_addr,str,sizeof(str)),ntohs(cliaddr.sin_port));
                                for(i=0;i<n;i++)
                                        buf[i] = toupper(buf[i]);
                                //printf("content %s",buf);
                                Write(connfd,buf,n);
                        }
                        
                        Close(connfd);
                        exit(0);
                }
                else
                {
                        Close(connfd);
                }
        }
}

// SIGCHLD handler 
void sig_child(int signo)
{
        pid_t pid;
        int stat;
        
        while((pid = waitpid(-1,&stat,WNOHANG)) > 0)
        {
                printf("chile %d terminated\n",pid);
        }
        
        return;
}

客户端

/* File Name: client.c */  
  
#include<stdio.h>  
#include<stdlib.h>  
#include<string.h>  
#include<errno.h>  
#include<sys/types.h>  
#include<sys/socket.h>  
#include<netinet/in.h>  
  
#define MAXLINE 4096  
  
  
int main(int argc, char** argv)  
{  
    int    sockfd, n,rec_len;  
    char    recvline[4096], sendline[4096];  
    char    buf[MAXLINE];  
    struct sockaddr_in    servaddr;  
  
  
    if( argc != 2){  
    printf("usage: ./client <ipaddress>\n");  
    exit(0);  
    }  
  
  
    if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){  
    printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);  
    exit(0);  
    }  
  
  
    memset(&servaddr, 0, sizeof(servaddr));  
    servaddr.sin_family = AF_INET;  
    servaddr.sin_port = htons(8000);  
    if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){  
    printf("inet_pton error for %s\n",argv[1]);  
    exit(0);  
    }  
  
  
    if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){  
    printf("connect error: %s(errno: %d)\n",strerror(errno),errno);  
    exit(0);  
    }  
  
  
    printf("send msg to server: \n");  
    fgets(sendline, 4096, stdin);  
    if( send(sockfd, sendline, strlen(sendline), 0) < 0)  
    {  
    printf("send msg error: %s(errno: %d)\n", strerror(errno), errno);  
    exit(0);  
    }  
    if((rec_len = recv(sockfd, buf, MAXLINE,0)) == -1) {  
       perror("recv error");  
       exit(1);  
    }  
    buf[rec_len]  = '\0';  
    printf("Received : %s ",buf);  
    close(sockfd);  
    exit(0);  
}  

accept函数

上面一个很重要的代码片段就是用了accept函数,而且accept函数返回了一个socket。我们有必要搞清楚这个反悔的socket是什么。

点击访问accept函数的说明的网站

  1. accept函数干嘛了
    The accept() system call is used with connection-based socket types (SOCK_STREAM, SOCK_SEQPACKET). It extracts the first connection request on the queue of pending connections for the listening socket, sockfd, creates a new connected socket, and returns a new file descriptor referring to that socket. The newly created socket is not in the listening state. The original socket sockfd is unaffected by this call.

  2. 返回了什么
    RETURN VALUE
    On success, these system calls return a nonnegative integer that is a file descriptor for the accepted socket. On error, -1 is returned, and errno is set appropriately.

由此可见accept函数用在tcp协议上,返回一个链接的socket的文件描述符,我们可以通过这个文件描述符来服务写入数据,这个数据来源于客户端,写入到远程。而服务器一开始创建的socket是专门用来监听客户端链接请求的。

所谓的accept函数,其实抽象的是TCP的连接建立过程。accept函数返回的新socket其实指代的是本次创建的连接,而一个连接是包括两部分信息的,一个是源IP和源端口,另一个宿IP和宿端口。每次accept返回的这些socket宿端口就可以都是一样的,这里并没有重新监听一个端口,端口主要用来区分进程,而源IP和源端口随着客户端不同显然不同。

总结

上面的图和代码都是网上copy的,服务器客户端不是配对的,代码也不能编译。只是找了两个觉得写得还比较清晰的。

主要目的是为了说明socket编程的基本模型,用来复习的。下面的文章说明zmq的时候,会进行比较,zmq的socket和这里的socket是不同的概念,zmq的socket会抽象出更高级的概念,zmq的socket会摆脱这里socket和socket一一对应通信的限制,大大简化网络通信方式,使我们能开发出很多高级的功能。

相关资料

管道
linux accept

相关文章

  • socket编程小复习

    准备做一个zmq专题,先复习一下socket的知识。 socket 我们知道两个进程如果需要进行通讯最基本的一个前...

  • 许世伟的Go语言基础 第五章总结

    第5章 网络编程 5.1 socket编程 以往socket编程: 建立socket:使用socket()函数。 ...

  • 网络编程

    python学习笔记-网络编程 socket编程: socket()函数:socket.socket([famil...

  • 4月5日TestServerSocket

    于2017/4/5复习学习的java - Socket网络编程. 本次完成的是网络聊天编程功能(没有GUI).我想...

  • TCP socket 编程

    TCP socket 编程 讲一下 socket 编程 步骤 使用 socket 模块 建立 TCP socket...

  • 网络编程

    网络 Socket 基于TCP协议的Socket编程 基于UDP协议的Socket编程

  • 网络编程

    Linux Socket编程(不限Linux) C/C++ socket编程教程:1天玩转socket通信技术 一...

  • 动脑学院架构篇-Java Socket编程基础及深入讲解

    【Socket】Java Socket编程基础及深入讲解 Socket是Java网络编程的基础,了解还是有好处的,...

  • Python网络编程

    Python网络编程 1、socket编程, 类:socket 1.server端 # socket第一个参数:地...

  • 2018-09-12 day18-网络编程和http请求

    网络编程 socket 网络编程就是socket编程,socket就是套接字,就是进行数据通信的两端(服务器和客户...

网友评论

      本文标题:socket编程小复习

      本文链接:https://www.haomeiwen.com/subject/xtmnyttx.html