socket编程之客户--服务器通信程序学习

来源:互联网 发布:英雄联盟mac版美服 编辑:程序博客网 时间:2024/06/11 10:55

操作系统有五大功能,简单总结为:

操作系统的五大功能:1、进程管理2、内存管理3、文件系统4、网络管理5、设备管理

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
网络中的进程是通过socket来通信的,那什么是socket呢?socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来操作。我的理解就是Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的操作(读/写IO、打开、关闭)

一、UNIX网络编程

这里写图片描述

在socket编程中会遇到很多函数,比如socket()、bind()、listen()等等,我会边学边记录熟悉这些函数的用法,简略的概述都在注释里,方便以后翻阅和学习

二、服务器端

#include <sys/types.h>#include <sys/socket.h>#include <stdio.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <string.h>#include <stdlib.h>#include <fcntl.h>#include <sys/shm.h>#define MYPORT  8887#define QUEUE   20#define BUFFER_SIZE 1024int main(){    int server_sockfd = socket(AF_INET,SOCK_STREAM, 0); //定义服务器端socket函数    ///定义sockaddr_in    struct sockaddr_in server_sockaddr;    server_sockaddr.sin_family = AF_INET;    server_sockaddr.sin_port = htons(MYPORT);    server_sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);//需要监听的端口,这里选择ANY表示所有网络端口都监听/*http://blog.csdn.net/yaxiya/article/details/6722083htonl就是把本机字节顺序转化为网络字节顺序h---host 本地主机to  就是to 了n  ---net 网络的意思l 是 unsigned long同理可得ntohl就是网络字节序转化为本机字节序htonl和ntohl主要是为了防止在不同平台的机器数据存放格式不同http://bbs.csdn.net/topics/80351112  论坛讨论ntohl和htonl*/    /*    bind,成功返回0,出错返回-1    int bind(int sockfd,const struct sockaddr* myaddr,socklen_t addrlen)    当socket函数返回一个描述符时,只是存在于其协议族的空间中,并没有分配一个具体的协议地址(这里指IPv4/IPv6和端口号的组合),    bind函数可以将一组固定的地址绑定到sockfd上。其中:sockfd是socket函数返回的描述符;myaddr指定了想要绑定的IP和端口号,均要使用网络字节序-即大端模式;addrlen是前面struct sockaddr(与sockaddr_in等价)的长度。    */    if(bind(server_sockfd,(struct sockaddr *)&server_sockaddr,sizeof(server_sockaddr))==-1)    {        perror("bind");        exit(1);    }    /*listen,成功返回0,出错返回-1int listen(int sockfd, int backlog);开始监听套接字sockfd:套接字,成功返回后进入监听模式,当有新连接并accept后会再建立一个套接字保存新的连接;    backlog:暂且翻译为后备连接吧!下面详细介绍此参数:  1)  当TCP接收一个连接后(三次握手通过)会将此连接存在连接请求队列里面,并对队列个数+1,而backlog为此队列允许的最大个数,超过此值,则直接将新的连接删除,即不在接收新的连接。将这些处于请求队列里面的连接暂记为后备连接,这些都在底层自动完成,底层将连接添加到队列后等待上层来处理(一般是调用accept函数接收连接);  2)  当上层调用accept函数接收一个连接(处于请求队列里面的后备连接),队列个数会-1;  3)  那么这样一个加一个减,只要底层提交的速度小于上层接收的速度(一般是这样),很明显backlog就不能限制连接的个数,只能限制后备连接的个数。那为啥要用这个backlog呢?主要用于并发处理,当上层没来的及接收时,底层可以提交多个连接;  4)  backlog的取值范围 ,一般为0-5。    http://www.cnblogs.com/mddblog/p/4492784.html    */    if(listen(server_sockfd,QUEUE) == -1)   //开始监听    {        perror("listen");        exit(1);    }    ///客户端套接字    char buffer[BUFFER_SIZE];    struct sockaddr_in client_addr;    socklen_t length = sizeof(client_addr);    /*成功返回非负描述字,出错返回-1    int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);    开始等待客户端连过来套接字addr 和 addrlen 一般是NULL, 否则只允许addr指定的客户端连过来。    */    int conn = accept(server_sockfd, (struct sockaddr*)&client_addr, &length);    if(conn<0)    {        perror("connect");        exit(1);    }    while(1)    {        memset(buffer,0,sizeof(buffer));//清空buffer        int len = recv(conn, buffer, sizeof(buffer),0);//recv函数返回其实际copy的字节数,http://blog.csdn.net/tiandyoin/article/details/30044781        if(strcmp(buffer,"exit\n")==0)            break;        fputs(buffer, stdout);//把接收到的内容输出到屏幕上        send(conn, buffer, len, 0);    }    close(conn);    close(server_sockfd);    return 0;}

三、客户端

#include <sys/types.h>#include <sys/socket.h>#include <stdio.h>#include <netinet/in.h>#include <arpa/inet.h>#include <unistd.h>#include <string.h>#include <stdlib.h>#include <fcntl.h>#include <sys/shm.h>#define MYPORT  8887#define BUFFER_SIZE 1024int main(){    int sock_cli = socket(AF_INET,SOCK_STREAM, 0);//定义socket客户端函数/*  int socket(int domain, int type, int protocol);创建socket, 返回文件描述符domain: AF_INET、 AF_INET6、 AF_UNIX、 AF_UPSPEC       指明了协议族/域比如AF_INET就是协议版本4,AF_INET6就是协议版本6type: SOCK_DGRAM、 SOCK_RAW、 SOCK_SEQPACKET、 SOCK_STREAM     type是套接口类型SOCK_STREAM 提供有序的、可靠的、双向的和基于连接的字节流,使用带外数据传送机制,为Internet地址族使用TCP。SOCK_DGRAM 支持无连接的、不可靠的和使用固定大小(通常很小)缓冲区的数据报服务,为Internet地址族使用UDP。protocol: IPPROTO_IP、 IPPROTO_IPV6、 IPPROTO_ICMP、 IPPROTO_RAW、IPPROTO_TCP、 IPPROTO_UDP    protocol:套接口所用的协议。如调用者不想指定,可用0指定,表示缺省    */    //定义sockaddr_in结构体          http://blog.csdn.net/renchunlin66/article/details/52351751    struct sockaddr_in servaddr;    memset(&servaddr, 0, sizeof(servaddr));         //memset函数在驱动里见到过,清空某一段空间    servaddr.sin_family = AF_INET;      //sin_family指代协议族,在socket编程中只能是AF_INET    servaddr.sin_port = htons(MYPORT);  //服务器端口,避免冲突建议设置大一些,因为比较小的端口都是默认的    servaddr.sin_addr.s_addr = inet_addr("192.168.216.128");  //服务器ip    //连接服务器,成功返回0,错误返回-1    if (connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)    {        perror("connect");        exit(1);    }/*int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);连接指定的服务器, 其中addr指定连接服务器的IP和端口*/    char sendbuf[BUFFER_SIZE];    char recvbuf[BUFFER_SIZE];/*send类似于write    int send(IN SOCKET s, IN const char FAR * buf, IN int len, IN int flags)       失败时返回 -1/SOCKET_ERROR其中:       sockfd:发送端套接字描述符(非监听描述符)       buf:应用要发送数据的缓存       len:实际要发送的数据长度       flag:一般设置为0    recv类似于read    ssize_t recv(int sockfd,void *buf, size_t len,int flags)其中:sockfd:接收端套接字描述符;buf:指定缓冲区地址,用于存储接收数据;len:指定的用于接收数据的缓冲区长度;flags:一般指定为0其实在一般情况下可以互换,便于理解也可使用read和write */    while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)//fgets函数在之前获取IP的时候用到过,相当于检测是否clint有写入吧。    {        send(sock_cli, sendbuf, strlen(sendbuf),0); //客户端发送        if(strcmp(sendbuf,"exit\n")==0)//在蜂鸣器中有用到strcmp,用来比较两个字符串,若完全相同则返回0            break;        recv(sock_cli, recvbuf, sizeof(recvbuf),0); //接收        fputs(recvbuf, stdout);//fputs函数向指定的文件指针中写入字符串,这里是向屏幕中输出接收到的信息        /*stdout(Standardoutput)标准输出stdin(Standardinput)标准输入stderr(Standarderror)标准错误        */        memset(sendbuf, 0, sizeof(sendbuf));//清空使用的空间        memset(recvbuf, 0, sizeof(recvbuf));    }    close(sock_cli);//关闭socket    return 0;}

四、客户–服务器通信

服务器端接收:

这里写图片描述

客户端发送:

这里写图片描述

要先打开服务器端,然后再打开客户端连接

//http://blog.csdn.net/g_brightboy/article/details/12854117常用socket函数

阅读全文
0 0
原创粉丝点击