Socket的创建和连接

“一切皆Socket”!!!!

Socket的主要工作原理即创建socket连接和发送接收数据。

socket在实现过程中分为服务器端和客户端两部分:

服务器端:

  工作原理:调用Socket函数创建套接字;声明struct sockaddr_in结构实体;之后调用bind将套接字编号与实体进行绑定;调用listen函数将创建的套接字设置成被动监听(所谓被动监听,是指当没有客户端请求时,套接字处于“睡眠”状态,只有当接收到客户端请求时,套接字才会被“唤醒”来响应请求。);然后调用accept函数对端口进行监听。一旦接收到客户端发来的消息之后,socket便返回客户端的ip+端口绑定的socket编号,两个进程之间此时建立连接。可以进行read/write之类的对文件的操作(因为,在linux中不分socket文件和普通文件)。收发操作结束之后调用close函数。

注意:listen() 只是让套接字进入监听状态,并没有真正接收客户端请求,listen() 后面的代码会继续执行,直到遇到 accept()。accept() 会阻塞程序执行(后面代码不能被执行),直到有新的请求到来。

客户端:

  工作原理:调用Socket函数创建套接字;然后将socket编号与服务器端的ip+端口进行绑定,之后可以进行read/write之类的对文件的操作,操作结束之后调用close函数关闭socket。

函数说明:

int socket(int af, int type, int protocol);

1. af 为地址族(Address Family),也就是 IP 地址类型,常用的有 AF_INET 和 AF_INET6。AF 是“Address Family”的简写,INET是“Inetnet”的简写。AF_INET 表示 IPv4 地址,例如 127.0.0.1;AF_INET6 表示 IPv6 地址,例如 1030::C9B4:FF12:48AA:1A2B。
大家需要记住127.0.0.1,它是一个特殊IP地址,表示本机地址,后面的教程会经常用到。
2. type 为数据传输方式,常用的有 SOCK_STREAM 和 SOCK_DGRAM
3. protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议

int bind(int sock, struct sockaddr *addr, socklen_t addrlen); 

int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen); 

1. sock 为 socket 文件描述符

2. addr 为 sockaddr 结构体变量的指针

3. addrlen 为 addr 变量的大小,可由 sizeof() 计算得出

int listen(int sock, int backlog); 

1. sock 为需要进入监听状态的套接字

2. backlog 为请求队列的最大长度。

int accept(int sock, struct sockaddr *addr, socklen_t *addrlen)

accept() 返回一个新的套接字来和客户端通信

1. sock 是服务器端的套接字

2. addr 保存了客户端的IP地址和端口号

3. addrlen是addr的字节长度

大家注意区分。后面和客户端通信时,要使用这个新生成的套接字,而不是原来服务器端的套接字。

示例代码:

服务器端:

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<errno.h>

#include<sys/types.h>

#include<sys/socket.h>

#include<netinet/in.h>

#include<unistd.h>

#define MAXLINE 4096

int main(int argc, char** argv){

    int  listenfd, connfd;

    struct sockaddr_in  servaddr;

    char  buff[4096];

    int  n;

    if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){

        printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);

        return 0;

    }

    memset(&servaddr, 0, sizeof(servaddr));

    servaddr.sin_family = AF_INET;

    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

    servaddr.sin_port = htons(6666);

    if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){

        printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);

        return 0;

    }

    if( listen(listenfd, 10) == -1){

        printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);

        return 0;

    }

    printf("======waiting for client's request======\n");

    while(1){

        if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1){

            printf("accept socket error: %s(errno: %d)",strerror(errno),errno);

            continue;

        }

        n = recv(connfd, buff, MAXLINE, 0);

        buff[n] = '\0';

        printf("recv msg from client: %s\n", buff);

        close(connfd);

    }

    close(listenfd);

    return 0;

}

客户端:

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#include<errno.h>

#include<sys/types.h>

#include<sys/socket.h>

#include<netinet/in.h>

#include<arpa/inet.h>

#include<unistd.h>

#define MAXLINE 4096

int main(int argc, char** argv){

    int   sockfd, n;

    char  recvline[4096], sendline[4096];

    struct sockaddr_in  servaddr;

    if( argc != 2){

        printf("usage: ./client <ipaddress>\n");

        return 0;

    }

    if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){

        printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);

        return 0;

    }

    memset(&servaddr, 0, sizeof(servaddr));

    servaddr.sin_family = AF_INET;

    servaddr.sin_port = htons(6666);

    if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0){

        printf("inet_pton error for %s\n",argv[1]);

        return 0;

    }

    if( connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0){

        printf("connect error: %s(errno: %d)\n",strerror(errno),errno);

        return 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);

        return 0;

    }

    close(sockfd);

    return 0;

}

makefile文件:

all:server client

server:server.o

    g++ -g -o server server.o

client:client.o

    g++ -g -o client client.o

server.o:server.cpp

    g++ -g -c server.cpp

client.o:client.cpp

    g++ -g -c client.cpp

clean:all

    rm all

执行make命令后,生成server和client两个可执行文件。分别打开两个终端窗口,一个执行./server命令,一个执行./client 127.0.0.1命令,表示连上本机的6666端口,执行./server命令的要先执行。执行./client 127.0.0.1命令后,会提示说要发给server的内容,输入“hello”后,client客户端执行完毕,这时可以看到server的那个终端窗口输出“recv msg from client: hello”。

TCP协议通信交互流程:

 

注意:

网络层的“IP地址 + 端口”可唯一标识一个进程;

从通信意义上说“IP地址 + 端口 + 协议”,可以完成进程间的通信 ;  //协议的目的是因为不同的协议使用的端口号存储方式可能不同。

猜你喜欢

转载自www.cnblogs.com/ruigelwang/p/12528323.html