网络编程1 - socket基本函数

学习教材:Linux C编程从初学到精通 - 张繁等编著
Linux C语言网络编程1 - socket基本函数

所有代码均在ubuntu12系统,gcc编译器环境下运行通过,理论上支持绝大多数Linux/Unix系统。
1.创建套接口
#include
#include
int socket (int family, int type, int protocol);

family: IPv4为PF_INET,IPv6为PF_INET6
type: TCP传输时为SOCK_STREAM, UDP位SOCK_DGRAM
protocol: 一般设为0

函数的意义类似于使用new操作符“构造”了一个“连接”的对象,返回的是连接对象的编号。


2.绑定端口
int bind (int sockfd, const struct sockaddr * my_addr, socklen_t addrlen);
绑定成功返回0,失败返回-1.

bind.c代码包含(1)创建套接口和(2)绑定端口的功能,代码如下:

#include
#include
#include
#include
#include
#include
#include
#include

#define PORT (2345)

int main()
{
    int sockfd;    //套接口描述符
    struct sockaddr_in addr; //IPv4地址数据结构
    //int addr_len = sizeof(struct sockaddr_in);
    if ( (sockfd=socket(AF_INET,SOCK_STREAM,0)) < 0 ) //建立socket
    {
        perror("socket created error!");
        exit(1);
    }
    else
    {
        printf("socket created successfully!\n");
        printf("socket id:%d\n",sockfd);
    }


    bzero(&addr,sizeof(struct sockaddr_in)); //清空地址信息
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT); // host to number shortint: 端口号为PORT
    addr.sin_addr.s_addr = htonl(INADDR_ANY); // IP为本机IP
    if( bind(sockfd, (struct sockaddr*)(&addr),
            sizeof(struct sockaddr)) < 0 )
    {
        perror("bind error!");
        exit(1);
    }
    else
    {
        printf("bind port successfully!\n");
        printf("local port:%d\n",PORT);
    }
    return 0;
}

运行结果如下:
socket created successfully!
socket id:3
bind port successfully!
local port:2345


3.等待监听函数
#include
int listen (int sockfd, int backlog);
sockfd表示已经建立好的套接口,backlog表示进入队列的连接数量,成功返回0,失败返回-1。

属服务器操作,用来监听网络中的客户机,等待某一客户机发送请求。
listen函数只适用于type为SOCK_STREAM或SOCK_SEQPACKET的socket。对于family为AF_INET的socket,backlog上限为128,也就是最多可以同时接受128个客户端请求。


4.接受连接函数
int accept (int sockfd, struct sockaddr *addr, socklen_t *addr_len);
sockfd为服务器上处于监听状态的socket,系统会把远程客户端的信息保存到addr所指向的结构提中,addrlen为sockaddr的内存长度,可以用sizeof获得。
当使用accept接受一个连接时,会返回一个新的sockfd,以后传输通过这个新的sockfd来处理,原来参数中的socket也可以继续使用。如果处理失败返回-1。

程序listen_accept.c中应用了listen和accept函数,代码如下:

#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 2345

int main()
{
    int sockfd, newsockfd;
    struct sockaddr_in addr;
    if( (sockfd=socket(AF_INET,SOCK_STREAM,0)) < 0 )
    {
        perror("socket create error!");
        exit(1);
    }
    else
    {
        printf("socket create successfully!\n");
        printf("socket id:%d\n",sockfd);
    }

    bzero(&addr,sizeof(struct sockaddr_in));
    addr.sin_family=AF_INET;
    addr.sin_port=htons(PORT);
    //htons,htonl,ntohs,ntohl四个函数用于本地和网络字节序的转换
    //h:host, n:network, s:short, l:long
    //short用uint16_t类型,long用uint32_t类型
    addr.sin_addr.s_addr=htonl(INADDR_ANY);
    if( bind(sockfd,(struct sockaddr*)(&addr), sizeof(struct sockaddr))
        < 0 )
    {
        perror("bind error!");
        exit(2);
    }
    else
    {
        printf("bind port successfully!");
        printf("local port:%d\n",PORT);
    }

    const int max_connection = 5;
    if( listen(sockfd,max_connection) < 0 )
    {
        perror("listen error");
        exit(1);
    }
    else
    {
        printf("listenning.....\n");
    }

    socklen_t addr_len=sizeof(struct sockaddr_in);
    if( (newsockfd=accept(sockfd,(struct sockaddr*)(&addr),
            &addr_len)) < 0 )
    {
        perror("accpet error!");
    }
    else
    {
        printf("accept a new connection!\n");
        printf("new socket id:%d\n",newsockfd);
    }
    return 0;
}

运行结果如下:

socket create successfully!
socket id:3
bind port successfully!local port:2345
listenning.....

程序运行到这里后停止了,在浏览器地址栏中输入:
http://127.0.0.1:2345/
然后按回车,结果如下:

accept a new connection!
new socket id:4

可以看到建立了新的套接口,编号为4。


5.请求连接函数
int connect (int sockfd, const struct sockaddr *ser_addr, socklen_t *addr_len);
sockfd表示已经建立的socket,serv_addr存储服务器的IP和端口信息,addr_len是sockadrr的长度,可以用sizeof函数获取。

程序remote_connect_baidu.c使用connect从本地客户端连接baidu的服务器,代码如下:

#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 80
#define BAIDU "119.75.217.56"

int main()
{
    int sockfd;
    struct sockaddr_in addr;
    if( (sockfd=socket(AF_INET,SOCK_STREAM,0)) < 0 )
    {
        perror("socket create error!");
        exit(1);
    }
    else
    {
        printf("socket create successfully!\n");
        printf("socket id:%d\n",sockfd);
    }

    bzero(&addr,sizeof(struct sockaddr_in));
    addr.sin_family=AF_INET;
    addr.sin_port=htons(PORT);
    addr.sin_addr.s_addr=inet_addr(BAIDU);
    if ( connect(sockfd, (struct sockaddr*)(&addr), sizeof(struct sockaddr)) < 0 )
    {
        perror("connect error!");
        exit(1);
    }
    else
    {
        printf("connected successfully!\n");
    }
    return 0;
}

运行结果如下:
socket create successfully!
socket id:3
connected successfully!
结果表明成功从本地连接到百度的服务器。


6.数据发送函数
int send (int sockfd, const void *msg, int len , unsigned int flags);
返回:若成功则返回发送的字节数,失败则返回-1。
参数:sockfd为socket编号,msg指向待发送的数据,len表示数据长度,flags一般设为0。


7.数据接收函数
int recv (int sockfd, void *buf, int len, unsigned int flags);
返回:若成功则返回接收的字节数,失败则返回-1。
参数:sockfd为socket编号,buf用来接收数据,len表示数据长度,flags一般设为0。

使用端口号为21的FTP服务来测试数据接收函数。由于国内FTP服务器使用中文编码的问题,可能出现乱码,先用英文的FTP服务器进行测试。
在终端中输入如下指令:
ping ftp.gnu.org

结果为:
PING ftp.gnu.org (208.118.235.20) 56(84) bytes of data.
64 bytes from ftp.gnu.org (208.118.235.20): icmp_req=1 ttl=42 time=356 ms

说明ftp.gnu.org的IP地址为208.118.235.20,同时知道FTP默认端口为21,测试程序如recv_from_ftp.c所示:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 21
#define FTP "208.118.235.20"

int main()
{
    int sockfd;
    struct sockaddr_in addr;
    if( (sockfd=socket(AF_INET,SOCK_STREAM,0)) < 0 )
    {
        perror("socket create error!");
        exit(1);
    }
    else
    {
        printf("socket create successfully!\n");
        printf("socket id:%d\n",sockfd);
    }

    bzero(&addr,sizeof(struct sockaddr_in));
    addr.sin_family=AF_INET;
    addr.sin_port=htons(PORT);
    addr.sin_addr.s_addr=inet_addr(FTP);
    if ( connect(sockfd, (struct sockaddr*)(&addr), sizeof(struct sockaddr)) < 0 )
    {
        perror("connect error!");
        exit(1);
    }
    else
    {
        printf("connected successfully!\n");
        printf("ftp server ip:%s\n",FTP);
        printf("ftp transfer port:%d\n",PORT);
    }
    char buf[1024];
    recv(sockfd,buf,sizeof(buf),0);
    printf("%s\n",buf);
    return 0;
}

运行结果如下:
socket create successfully!
socket id:3
connected successfully!
ftp server ip:208.118.235.20
ftp transfer port:21
220 GNU FTP server ready.


程序表明成功连接到FTP服务器,并得到了返回信息。


8.write和read函数
#include
ssize_t write (int fd, const void *buf, size_t count);
ssize_t read (int fd, void *buf, size_t count);
返回:成功则返回已经写入或读取的字节数,出错返回-1,由于size_t为非负数,而返回值可能会-1,所以返回值类型为ssize_t(也就是signed size_t)。
参数:fd代表socket,buf为内存指针,count表示读写内存的长度。
可以看到,使用write和read函数读写套接口的方法和读写文件的方法是一致的。
程序remote_read.c使用了read函数。程序中服务器监听一个端口,如果客户端请求连接这个端口,则服务器接受,并使用read函数从请求中读取客户端发来的信息,最后输出这些数据。这里的客户端请求仍然可以使用浏览器输入IP地址和端口的方法来进行。
remote_read.c代码如下:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 5566

int main()
{
    int sockfd;
    struct sockaddr_in addr;
    if( (sockfd=socket(AF_INET,SOCK_STREAM,0)) < 0 )
    {
        perror("socket create error!");
        exit(1);
    }
    else
    {
        printf("socket create successfully!\n");
        printf("socket id:%d\n",sockfd);
    }

    bzero(&addr,sizeof(struct sockaddr_in));
    addr.sin_family=AF_INET;
    addr.sin_port=htons(PORT);
    addr.sin_addr.s_addr=htonl(INADDR_ANY);
    if( bind(sockfd,(struct sockaddr*)(&addr),sizeof(struct sockaddr))<0 )
    {
        perror("bind error!");
        exit(1);
    }
    else
    {
        printf("bind port successfully!\n");
        printf("local port:%d\n",PORT);
    }
    const int max_connection = 5;
    if(listen(sockfd,max_connection)<0)
    {
        perror("listen error!");
        exit(1);
    }
    else
    {
        printf("listenning....\n");

    }
    socklen_t addr_len = sizeof(struct sockaddr_in);
    char msgbuf[1024];
    int newsockfd;
    if( (newsockfd = accept(sockfd,(struct sockaddr*)(&addr),&addr_len))<0 )
    {
        perror("accept error!");
    }
    else
    {
        printf("connect from %s\n", inet_ntoa(addr.sin_addr));
        if(read(newsockfd,msgbuf,sizeof(msgbuf))<=0)
        {
            perror("accept error!");
        }
        else
        {
            printf("message:\n%s\n",msgbuf);
        }
    }

    return 0;
}

运行结果如下:
socket create successfully!
socket id:3
bind port successfully!
local port:5566
listenning....

浏览器地址栏输入:
http://127.0.0.1:5566/

结果如下:
connect from 127.0.0.1
message:
GET / HTTP/1.1
Host: 127.0.0.1:5566
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:11.0) Gecko/20100101 Firefox/11.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive

可以看到,客户端从浏览器输入地址和端口后,服务器可以使用read函数接受请求信息,请求信息中包含了操作系统、浏览器、语言、编码等相关信息。





你可能感兴趣的:(Linux)