Sockt服务器和客户端开发示例

Sockt 服务器开发步骤

运作示意图

  1. 创建套接字(sockt)
  2. 为套接字添加信息(IP地址和端口号)
  3. 监听网络连接
  4. 监听到有客户端接入,接受一个连接
  5. 数据互交
  6. 关闭套接字,断开连接

相关API使用示例

socket

  1. 原型
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int socket(int domain, int type, int protocol);
//返回值类似于文件标识符,后续的监听等操作都基于该返回值
  • int domain —— 指明所使用的协议族,通常为AF_INET表示互联网协议族(TCP/IP)
  • int type —— socket的类型(SOCK_STREAM、SOCK_DGRAM、SOCK_RAW等)
  • int protocol —— 通常赋值0,根据以上参数自动选出对应传输协议
  1. 示例
int s_fd;

s_fd = socket(AF_INET, SOCK_STREAM,0);

bind

  1. 原型
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr,
                socklen_t addrlen);
  • int sockfd —— socket返回值
  • const struct sockaddr *addr —— 结构体内包含所需的IP地址和端口号
  • socklen_t addrlen —— 对应的是地址的长度

其中 struct sockaddr 结构体如下:

struct sockaddr {
               sa_family_t sa_family;		//协议族
               char        sa_data[14];		//IP+端口
           }

通常使用struct sockaddr_in 结构体同等替换(使用时需要强转类型):

struct sockaddr_in {
    sa_family_t    sin_family;		//协议族
    in_port_t      sin_port;   		//端口号
    struct in_addr sin_addr;   		//IP地址结构体
    unsigned char sin_zero[8];		//填充,为了与sockaddr结构内存对齐,两者才能相互替换
};

struct in_addr {
    uint32_t       s_addr;     
};
  1. 示例
//argv[1]为本机IP地址,argv[2]为端口号
struct sockaddr_in s_addr;

s_addr.sin_family = AF_INET;			//赋值协议
s_addr.sin_port = htons(atoi(argv[2]));		//字节序小端转大端
net_aton(argv[1],&s_addr.sin_addr);		//将IP地址转化网络能识别的格式

bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));

listen

  1. 原型
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int listen(int sockfd, int backlog);
  • int sockfd —— socket返回值
  • int backlog —— 监听的数量
  1. 示例
int s_fd;

s_fd = socket(AF_INET, SOCK_STREAM,0);
listen(s_fd,10);

accept

  1. 原型
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//返回值可以用来后续的数据读写
  • int sockfd —— socket返回值
  • struct sockaddr *addr —— 用来返回已连接的对端(客户端)的协议地址
  • socklen_t *addrlen —— 客户端地址长度
  1. 示例
struct sockaddr_in c_addr;
int clen = sizeof(struct sockaddr_in);

s_fd = socket(AF_INET, SOCK_STREAM,0);		
c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);

connect

  1. 原型
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);
  • int sockfd —— socket的返回值
  • const struct sockaddr *addr —— 用来返回已连接的对端(客户端)的协议地址
  • socklen_t addrlen —— 客户端地址长度
  1. 示例
struct sockaddr_in c_addr;

c_fd = socket(AF_INET, SOCK_STREAM,0);
connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)

实现多方消息收发

服务器端server.c

#include<stdio.h>
#include<sys/types.h>          /* See NOTES */
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include <unistd.h>

int main(int argc,char **argv)
{
        int s_fd;
        int c_fd;
        int n_read;
        int mark = 0;
        
        char readBuf[128]char msg[128] = {0};
        
        struct sockaddr_in s_addr;
        struct sockaddr_in c_addr;
        
        if(argc != 3){			//判断传参数量是否够
                printf("param is not good\n");
                exit(-1);
        }
        
        memset(&s_addr,0,sizeof(struct sockaddr_in));		//清空s_addr
        memset(&c_addr,0,sizeof(struct sockaddr_in));		//清空c_addr
        
        s_fd = socket(AF_INET, SOCK_STREAM,0);			//创建套接字
        
        if(s_fd == -1)						//判断是否创建成功
        {
                perror("socket");
                exit(-1);
        }
        
        s_addr.sin_family = AF_INET;				//赋值协议
        s_addr.sin_port = htons(atoi(argv[2]));			//字节序小端转大端
        inet_aton(argv[1],&s_addr.sin_addr);			//将IP地址转化网络能识别的格式
        
        bind(s_fd,(struct sockaddr *)&s_addr,sizeof(struct sockaddr_in));	//将IP等信息录入其中
        
        listen(s_fd,10);		//监听请求
        
        int clen = sizeof(struct sockaddr_in);
        
        while(1){
        
                c_fd = accept(s_fd,(struct sockaddr *)&c_addr,&clen);		//接受请求
                
                if(c_fd != -1)		//判断是否接受成功
                {
                        perror("accept");
                }
                
                mark++;		//只有接受请求后才会++,否则accept将会阻塞进程
                
                printf("get connect:%s\n",inet_ntoa(c_addr.sin_addr));		//打印本机IP地址
                
                if(fork() == 0){		//建立父子进程
                
                        if(fork() == 0){
                        
                        while(1)
                                {
                                        sprintf(msg,"welcome NO.%d client\n",mark);	
                                        write(c_fd,msg,strlen(msg));		//写数据
                                        sleep(3);
                                }
                        }
                        
                        while(1){
                                memset(readBuf,0,sizeof(readBuf));		//清空读区
                                
                                n_read = read(c_fd,readBuf,128);		//读取数据
                                
                                if(n_read == -1)		//判断是否读取成功
                                {
                                        perror("read");
                                }else{
                                        printf("get message:%d,%s\n",n_read,readBuf);
                                }
                        }
                        break;
                }
        }
        return 0;
}

客户端client.c

#include<stdio.h>
#include<sys/types.h>          /* See NOTES */
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc,char **argv)
{
        int c_fd;
        int n_read;
        
        char readBuf[128];
        char msg[128] = {0};
        
        struct sockaddr_in c_addr;
        
        memset(&c_addr,0,sizeof(struct sockaddr_in));		//清空c_addr
        
        if(argc != 3){			//判断传参数量是否够
                printf("param is not good\n");
                exit(-1);
        }
        
        c_fd = socket(AF_INET, SOCK_STREAM,0);		//创建一个套接字
        
        if(c_fd == -1)			//判断是否创建成功
 	{
                perror("socket");	//打出错误原因
                exit(-1);
        }
        
        c_addr.sin_family = AF_INET;			//赋值协议
        c_addr.sin_port = htons(atoi(argv[2]));		//字节序小端转大端
        inet_aton(argv[1],&c_addr.sin_addr);		//将IP地址转化网络能识别的格式
        
        if(connect(c_fd,(struct sockaddr *)&c_addr,sizeof(struct sockaddr)) == -1){	//向服务器发送请求
                perror("connect");		//打印失败原因
                exit(-1);
        }
        
        while(1){
                if(fork() == 0){		//创建父子进程
                        while(1){		//父进程循环
                        
                                memset(msg,0,sizeof(msg));	//清空接收区
                                printf("input:");
                                
                                gets(msg);		//获取消息数据
                                write(c_fd,msg,strlen(msg));		//将信息写入
                                
                                 }
                }
                
                while(1){		//子进程循环
                
                        memset(readBuf,0,sizeof(readBuf));	//清空读区
                        
                        n_read = read(c_fd,readBuf,128);	//读取信息
                        
                        if(n_read == -1)
                        {
                                perror("read");
                        }else{
                                printf("get message from server:%d,%s\n",n_read,readBuf);
                        }
                }
        }
        return 0;
}

运行效果
打开服务端后,可以打开多个客户端进行消息发送,服务端仅作为消息的接收区域,多个客户端的消息都可以显示在服务端上

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值