Linux Socket

1. API

#include <sys/types.h>
#include <sys/socket.h>


/************************************************************************
 * 常用socket结构体
 ***********************************************************************/

// 通用函数类型:
struct sockaddr{
  sa_family_t  sa_family;
  char         sa_data[14];
}

//如ipv4对应的是:
struct sockaddr_in {
    sa_family_t    sin_family; /* address family: AF_INET */
    in_port_t      sin_port;   /* port in network byte order 2字节*/
    struct in_addr sin_addr;   /* internet address 4字节*/
  unsigned char sin_zero[8];
};

//Internet address
struct in_addr {
    uint32_t       s_addr;     /* address in network byte order */
};

//ipv6对应的是: 
struct sockaddr_in6 { 
    sa_family_t     sin6_family;   /* AF_INET6 */ 
    in_port_t       sin6_port;     /* port number */ 
    uint32_t        sin6_flowinfo; /* IPv6 flow information */ 
    struct in6_addr sin6_addr;     /* IPv6 address */ 
    uint32_t        sin6_scope_id; /* Scope ID (new in 2.4) */ 
};
struct in6_addr { 
    unsigned char   s6_addr[16];   /* IPv6 address */ 
};

//Unix域对应的是: 
#define UNIX_PATH_MAX    108
struct sockaddr_un { 
    sa_family_t sun_family;               /* AF_UNIX */ 
    char        sun_path[UNIX_PATH_MAX];  /* pathname */ 
};

/************************************************************************
 * 工具方法
 ***********************************************************************/

//将字符串形式的IP地址转化为整数型的IP地址(网络字节序)
int_addr_t indet_addr(const char *cp);

//将整数形式的IP地址转化为字符串形式的IP地址
char *inet_ntoa(struct in_addr);
    
//网络字节序定义:收到的第一个字节被当作高位看待,这就要求发送端发送的第一个字节应当是高位。
//而在发送端发送数据时,发送的第一个字节是该数字在内存中起始地址对应的字节。
//可见多字节数值在发送前,在内存中数值应该以大端法存放。 
//网络字节序说是大端字节序。

//将32位的数据从主机字节序转换为网络字节序
uint32_t htonl(uint32_t hostlong);
  
//将16位的数据从主机字节序转换为网络字节序
uint16_t htons(uint16_t hostshort);
  
//将32位的数据从网络字节序转换为主机字节序
uint32_t ntohl(uint32_t netlong);
  
//将16位的数据从网络字节序转换为主机字节序
uint16_t ntohs(uint16_t netshort);
  
/************************************************************************
 * API
 ***********************************************************************/

/**
 * 创建socket
 *
 * @param domain IPv4直接填写AF_INET
 * @param type TCP使用SOCK_STEAM, UDP使用SOCK_DGRAM
 * @param protocol 填0即可
 * @return 成功返回socket句柄,失败返回-1
 **/
int socket(int domain,int type,int protocol);

/**
 * 一旦你有了一个套接口以后,下一步就是把套接口绑定到本地计算机的某一个端口上。
 * 但如果你只想使用connect()则无此必要。
 *
 * @param addrlen sizeof(struct sockaddr)
 * @return 失败返回-1
 **/
int bind(int sockfd, struct sockaddr *my_addr, int addrlen);

/**
 * 绑定端口后就可以开启监听了
 * @param backlog 进入队列中允许的连接的个数。
 * 		进入的连接请求在使用系统调用accept()应答之前要在进入队列中等待。
 * 		这个值是队列中最多可以拥有的请求的个数。大多数系统的缺省设置为20。你可以设置为5或者10。
 * @return 失败返回-1
 **/
int listen(int sockfd,int backlog);

/**
 * 如果你使用connect()系统调用,那么你不必知道你使用的端口号。
 * 当你调用connect()时,它检查套接口是否已经绑定,如果没有,它将会分配一个空闲的端口。
 *
 * @param addrlen sizeof(struct sockaddr)
 * @return 成功返回0,失败返回-1
 **/
int connect(int sockfd,struct sockaddr *serv_addr, int addrlen);

/**
 * 服务端阻塞监听客户端连接
 *
 * @return 成功返回连接句柄,出错返回-1
 **/
int accept(int sockfd,void *addr,int *addrlen);

/**
 * 发送数据,服务端发送接收数据使用accept()返回的句柄
 *
 * @param msg 发送数据的指针
 * @param len 发送数据长度
 * @param flags 设0
 * @return 返回实际发送的数据长度,如果没法送完要接着下次发送
 **/
int send(int sockfd,const void* msg,int len,int flags);

/**
 * 接收数据,服务端接收接收数据使用accept()返回的句柄
 *
 * @param len 接收最大长度
 * @return 返回实际接收长度,出错返回-1
 **/
int recv(int sockfd,void* buf,int len,unsigned int flags);

/**
 * 发送数据,除了两个参数以外,其他的参数和系统调用send()时相同。
 *
 * @param to 包含目的IP地址和端口号的数据结构sockaddr的指针
 * @return 同send()
 **/
int sendto(int sockfd, const void* msg, int len, unsigned int flags,
           const struct sockaddr *to, int tolen);

/**
 * 接收数据,除了两个参数以外,其他的参数和系统调用recv()时相同
 *
 * @param from 包含源IP地址和端口号的数据结构sockaddr的指针
 * @return 同recv()
 **/
int recvfrom(int sockfd,void* buf, int len, unsigned int flags,
		     struct sockaddr* from,int* fromlen);

/**
 * 关闭连接
 **/
int close(int sockfd);

/**
 * 使用系统调用shutdown(),可有更多的控制权。它允许你在某一个方向切断通信,或者切断双方的通信
 * 
 * @param how 0,1,2
 * @return 成功返回0,失败返回-1
 **/
int shutdown(int sockfd,int how);

/**
 * 返回对方ip
 **/
int getpeername(int sockfd,struct sockaddr *addr,int *addrlen);

/**
 * 返回当前计算机名字
 * 
 * @return 成功返回0,失败返回-1
 **/
int gethostname(char *hostname,size_t size);

  

2. 建立连接

服务端

服务器端先初始化socket,然后与端口绑定,对端口进行监听,调用accept阻塞,等待客户端连接。

socket() -> bind() -> listen() -> accept()

客户端

客户端先初始化socket,然后与服务端连接,服务端监听成功则连接建立完成

socket() -> connect()

3. 示例

服务端

/* File Name: server.c */  
#include<stdio.h>  
#include<stdlib.h>  
#include<cstring>  

#include<errno.h>  
#include<sys/types.h>  
#include<sys/socket.h>  

#define DEFAULT_PORT 8000  
#define MAXLINE 4096  
int main(int argc, char** argv)  
{  
    int socket_fd, connect_fd;  
    struct sockaddr_in servaddr;  
    char buff[4096];  
    int  n;  
    
    //初始化Socket  
    if( (socket_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){  
    	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_addr.s_addr = htonl(INADDR_ANY);//IP地址设置成INADDR_ANY,让系统自动获取本机的IP地址。  
    servaddr.sin_port = htons(DEFAULT_PORT);//设置的端口为DEFAULT_PORT  
  
    //将本地地址绑定到所创建的套接字上  
    if( bind(socket_fd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){  
   	 	printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);  
    	exit(0);  
    }  
    //开始监听是否有客户端连接  
    if( listen(socket_fd, 10) == -1){  
    	printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);  
    	exit(0);  
    }  
    
    printf("======waiting for client's request======\n");  
    while(1){  
		//阻塞直到有客户端连接,不然多浪费CPU资源。  
        if( (connect_fd = accept(socket_fd, (struct sockaddr*)NULL, NULL)) == -1){  
        	printf("accept socket error: %s(errno: %d)",strerror(errno),errno);  
        	continue;  
   		 }  
		//接受客户端传过来的数据  
    	n = recv(connect_fd, buff, MAXLINE, 0);  
		//向客户端发送回应数据  
    	if(!fork()){//子进程进入if 
        	if(send(connect_fd, "Hello,you are connected!\n", 26,0) == -1)  
        		perror("send error");  
        	close(connect_fd);  
        	exit(0);//退出子进程
    	}  
    	buff[n] = '\0';  
    	printf("recv msg from client: %s\n", buff);  
    	close(connect_fd);  
    }  
    
    close(socket_fd);  
}  

客户端

/* 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);  
} 

  

posted @ 2018-10-04 10:55  cs_wu  阅读(300)  评论(0编辑  收藏  举报