基于TCP的Socket网络编程

前言:
Socket通信是基于TCP/IP协议的通信。在工作和做项目中应用非常广,下面来介绍下Socket网络编程!

Socket的介绍

首先,在Socket网络编程中我们要了解两个重要的东西,ip和端口号,一台拥有IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等。这些服务完全可以通过1个ip地址来实现。主机是怎样区分不同的网络服务的?显然不能只靠ip地址,因为ip地址与网络服务的关系是一对多的关系。
实际上是通过“ip地址+端口号”来区分不同的服务的。一般端口号在5000~10000。

1.字节序

字节序:指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序。
Little endian(小端字节序):将低序字节存储在起始地址。
Big endian(大端字节序):将高序字节存储在起始地址。

举个例子:int a = 0x12345678;
0x78属于低地址,而0x12属于高地址
如果是大端模式,那输出方式是0x12 0x34 0x56 0x78,如果是小端模式,那么就是0x78 0x56 0x34 0x12

这个很容易理解,人类读写数据的习惯是大端字节序,而小端字节序是反着人类来的,而我们网络字节序传输是大端模式,因此要转为大端模式。

2.字节序转换api:
 #include <netinet/in.h>
uint16_t htons(uint16_t host16bitvalue);    //返回网络字节序的值uint32_t
 htonl(uint32_t host32bitvalue);    //返回网络字节序的值uint16_t ntohs(uint16_t
 net16bitvalue);     //返回主机字节序的值uint32_t ntohl(uint32_t net32bitvalue);
 //返回主机字节序的值

 h代表host,n代表net,s代表short(两个字节),l代表long4个字节),通过上面的4个函数可以实现主机字节序和网络字节序之间的转换。有时可以用INADDR_ANY,INADDR_ANY指定地址让操作系统自己获取

Socket的开发步骤

Socket服务器和客户端的开发步骤:
在这里插入图片描述

Linux提供的API简析

1.socket(创建套接字)

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

在这里插入图片描述

2.bind()函数
 #include <sys/types.h>          /* See NOTES */
 #include <sys/socket.h>

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

功能:用于绑定ip地址跟端口号到socketfd。
 参数说明:
 sockfd--是一个文件描述符
 addr:是一个指向包含有本地IP及端口号等信息等信息的sockaddr类型的指针,指向要绑定给socket的协议地址结构,这个地址结构根据地址创建socket时的地址协议族的不同而不同。

在这里插入图片描述

struct sockaddr_in结构体:

 struct sockaddr_in
 {  
	 sa_family_t sin_family; //指定AF_***,表示使用什么协议族的ip格式  __be16       sin_port;   //设置端口号  struct
	 in_addr  sin_addr;   //设置ip  
	 unsigned char  __pad[__SOCK_SIZE__ -sizeof(short int) - sizeof(unsigned short int) - sizeof(struct in_addr)];
 }; 

struct in_addr
{  
	__be32 s_addr;  //32位的无符号整型数据 
}; 

bind函数绑定实例:

此结构体因为成员中设置ip和端口的变量时分开的,所以设置方便,但是bind函数要求的是struct sockaddr类型的结构体变量,所以使用struct sockaddr_in设置以后要将其强制转化为struct sockaddr类型,然后传值给bind函数。

 struct sockaddr_in addr;  //首先定义结构体变量
 addr.sin_family = AF_INET;  //指定协议族为IPV4版本的TCP/IP协议族 addr.sin_port =
 htons(5006);  //指定端口号 addr.sin_addr.s_addr =
 inet_addr("192.168.1.10"); //指定IP ret = bind(sockfd,(struct
 sockaddr*)&addr,sizeof(sddr)); //进行套接字文件/ip/端口的绑定
htons函数

功能:将一个无符号短整型数值转换为网络字节序,即大端模式(big-endian);

 #include <arpa/inet.h>
uint16_t htons(uint16_t hostshort);
addr.sin_port = htons(5006);
inet_addr函数

功能:将一个字符串格式的ip地址转换成一个uint32_t数字格式。

 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
 in_addr_t inet_addr(const char *cp);
 eg:
 addr.sin_addr.s_addr = inet_addr("192.168.1.10");
地址转换API
  int inet_aton(const char* straddr,struct in_addr *addrp);
   把字符串形式的“192.168.1.123”转为网络能识别的格式
 
 
 char* inet_ntoa(struct in_addr inaddr);  把网络格式的ip地址转为字符串形式
3.listen()函数
 #include <sys/types.h>
 #include <sys/socket.h>
 
 int listen(int sockfd, int backlog);
功能:监听设置函数
 参数说明:
 sockfd:文件描述符
 backlog:指定在请求队列中允许的最大请求数
4.accept()函数
 #include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
 int accept(int sockfd, struct sockaddr *addr, socklen_t*addrlen);
 
 功能:accept函数由TCP服务器调用,用于从已完成连接队列队头返回下一个已完成连接,如果已完成连接队列为空,那么进程被投入睡眠。
 
 参数说明:
sockfd:文件描述符
addr:用来返回已连接的对端(客户端)的协议地址
addrled:客户端地址长度
返回值:accept函数返回值成功时返回非负值,失败时返回-1
5.1. 数据收发(recv 、send)
函数原型:ssize_t send(int sockfd, const void *buf, size_t len, int flags);
 
功能:向套接字中发送数据
参数:
 sockfd:向套接字中发送数据
 buf:要发送的数据的首地址
 len:要发送的数据的字节
  int flags:设置为MSG_DONTWAITMSG 时 表示非阻塞 设置为0时 功能和write一样
返回值:成功返回实际发送的字节数,失败返回 -1 


 函数原型:ssize_t recv(int sockfd, const void *buf, size_t len, int flags);

 功能:向套接字中发送数据 
 参数: 	  
sockfd:在哪个套接字接
buf:存放要接收的数据的首地址
len:要接收的数据的字节
int flags:设置为MSG_DONTWAITMSG 时 表示非阻塞设置为0时 功能和read一样        
返回值:成功返回实际发送的字节数,失败返回 -1
5.2. 数据收发(read 、write)
 #include <unistd.h>
 ssize_t read(int fd, void * buf, size_t nbytes);
 #include <unistd.h>
ssize_t write(int fd, const	void *buf, size_t nbytes);
6.客户端的connect函数
#include <sys/types.h> /* See NOTES */
 #include <sys/socket.h> int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 
 
 参数说明:
 sockfd: socket文件描述符
 addr: 传入参数,指定服务器端地址信息,含IP地址和端口号 
 addrlen: 传入参数,传入sizeof(addr)大小 
 返回值:成功返回0,失败返回-1,设置errno
7. 一个客户端与服务器连接的demo

服务器:

#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <stdlib.h>
//#include <linux/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char **argv)
{
        int s_fd;
        int c_fd;
        int ret;
        int n_read;
        char readBuf[128];
        char msg[128]={0};
        // char *writeBuf="I get your message!";
        int mark=0;
        //1.socket
        s_fd = socket(AF_INET,SOCK_STREAM ,0);
        if(s_fd == -1){

                perror("socket");
                exit(-1);
        }

        if(argc!=3){
                printf("pamrm error\n");
                exit(-1);
        }

        struct sockaddr_in a_addr;
        struct sockaddr_in c_addr;
        memset(&a_addr,0,sizeof(struct sockaddr_in));
        memset(&c_addr,0,sizeof(struct sockaddr_in));

        a_addr.sin_family=AF_INET;
        a_addr.sin_port=htons(atoi(argv[2]));
        a_addr.sin_addr.s_addr=inet_addr(argv[1]);
        //  inet_ntoa("172.20.10.3",&addr.sin_addr.s_addr);
        //2.bind
        ret = bind(s_fd,(struct sockaddr*)&a_addr,sizeof(struct sockaddr_in));
        if(ret==-1){
                perror("bind");
                exit(-1);
 }
        //3.listen
        int len=sizeof(struct sockaddr_in);
        listen(s_fd,10);

        while(1){
                //4.accept
                c_fd = accept(s_fd,(struct sockaddr*)&c_addr,&len);
                mark++;
                printf("get connect: %s\n",inet_ntoa(c_addr.sin_addr));
                if(fork()==0){
                        if(fork()==0){
                                while(1){
                                        sprintf(msg,"Welcome to No%d client\n",mark);
                                        write(c_fd,msg,strlen(msg));
                                        sleep(3);
                                }
                        }
                        //5.read
                        while(1){
                                memset(readBuf,0,sizeof(readBuf));
                                n_read=read(c_fd,readBuf,128);
                                if(n_read==-1){
                                        perror("read");
                                }else{
                                        printf("get:%s\n",readBuf);
                                }
                        }
                              break;
                }
                //6.write
        }
        return 0;
}

客户端:

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

int main(int argc,char **argv)
{
        int c_fd;
        int ret;
        int n_read;
        char msg[128]={0};
        char readBuf[128]={0};

        c_fd=socket(AF_INET,SOCK_STREAM,0);
        if(c_fd==-1){
                perror("socket");
                exit(-1);
        }

        struct sockaddr_in addr;
        addr.sin_family=AF_INET;
        addr.sin_port=htons(atoi(argv[2]));
        addr.sin_addr.s_addr=inet_addr(argv[1]);

        ret=connect(c_fd,(struct sockaddr*)&addr,sizeof(struct sockaddr_in));
        if(ret==-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,sizeof(msg));
                        }
                }
 while(1){

                        memset(readBuf,0,sizeof(readBuf));
                        n_read=read(c_fd,readBuf,sizeof(readBuf));
                        if(n_read==-1){

                                perror("read");
                        }else{
                                printf("get:%s\n",readBuf);
                        }

                }





        }





        return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值