柳大的Linux游记·基础篇(4)网络编程基础
- Author: 柳大·Poechant
- Blog: Blog.CSDN.net/Poechant
- Email:zhongchao.usytc#gmail.com (#->@)
- Date:March 11th, 2012
- Copyright © 柳大·Poechant(钟超·Michael)
回顾
闲话
最近很忙,博文写的少。感谢一些博友的理解。
有博友发邮件说《柳大的Linux游记》希望继续写下去,希望了解一些与 socket 入门有关的内容。对此深表惭愧,当时也是应一个博友的来信而开始写这个系列的,但仅写了三篇就没继续了。与 socket 相关的文章,在网络上非常多,socket 编程也是基本功。要我来写的话,写出心意很难,我只希望能写系统一些,所以我想先介绍 socket 的基础,然后说说 select,poll 和 epoll 等 IO 复用技术,可能这样会系统一些,也更实用。
W. Richard Stevens 的 UNIX Network Programming Volume 1 中讲解例子的时候都使用了include "unp.h"
,这是 Stevens 先生在随书源码中的提供的一个包含了所有 UNIX 网络编程会用到的头文件的的一个头文件。但这样对于不了解 UNIX 网络编程以及 socket 的朋友来说,并不是一个好的学习途径。所以我想看完本文后读 Stevens 先生的杰出作品更好一些 :)
另外,《JVM深入笔记》的第四篇正在整理,最近确实空闲时间比较少,对此感到很抱歉。我会尽量抽时间多分享一些的。
言归正传,下面还是沿袭我的一贯风格,先以最简单的实例开始。
目录
- 快速开始
- 1.1 TCP C/S
- 1.1.1 TCP Server
- 1.1.2 TCP Client
- 1.2. UCP C/S
- 1.2.1 UDP Server
- 1.2.2 UDP Client
- 1.1 TCP C/S
- TCP 和 UCP 的 Socket 编程对比
- 2.1 Server
- 2.2 Client
- 2.3 所使用的 API 对比
- 裸用 socket 的性能很差
1 快速开始
1.1 TCP C/S
无论你是使用 Windows 还是 UNIX-like 的系统,操作系统提供给应用层的网络编程接口都是 Socket。在 5 层的 TCP/IP 网络结构或者 7 层的 OSI 网络结构中,都有传输层,TCP 和 UDP 协议就是为传输层服务的。而网络层的最常用协议就是 IP(IPv4 或 IPv6)在高层编写程序,就需要用到 TCP 协议和 UDP 协议。其直接使用,就是通过 Socket 来实现的。
先看一段简单的 TCP 通信的 Server 与 Client 例程。
1.1.1 TCP Server
下面是一个 TCP 连接的 Server 实例。
#include <string.h>
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
// Get Port option
if (argc < 2)
{
fprintf(stderr, "ERROR, no port provided\n");
exit(1);
}
int port_no = atoi(argv[1]);
// Get socket
int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
// Bind
struct sockaddr_in server_addr;
bzero((char *) &server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(port_no);
bind(socket_fd, (struct sockaddr *) &server_addr, sizeof(server_addr));
// Listen
listen(socket_fd, 5);
while (1) {
// Accept
struct sockaddr_in client_addr;
socklen_t client_addr_length = sizeof(client_addr);
int socket_to_client = accept(socket_fd, (struct sockaddr *) &client_addr, &client_addr_length);
// Read
char buffer[1024];
bzero(buffer, sizeof(buffer));
read(socket_to_client, buffer, sizeof(buffer) - 1);
printf("Here is the message: %s\n", buffer);
// Write
const char *data = "I got your message.";
write(socket_to_client, data, sizeof(data));
// Close
close(socket_to_client);
}
close(socket_fd);
return 0;
}
上面是 TCP 的 Client 的 Simplest Example。概括起来 Scoket Server 编程有如下几个步骤:
1.1.1.1 获取 Socket Descriptor:
// socket function is included in sys/socket.h
// AF_INET is included in sys/socket.h
// SOCK_STREAM is included in sys/socket.h
socket(AF_INET, SOCK_STREAM, 0);
通过sys/socket.h
中的socket
函数。第一个参数表示使用IPv4 Internet Protocol
,如果是AF_INET6
则表示IPv6 Internet Protocol
,其中AF
表示Address Family
,另外还有PF
表示Protocol Family
。第二个参数表示流传输Socket Stream
,流传输是序列化的、可靠的、双向的、面向连接的,Kernel.org 给出的解释是:“Provides sequenced, reliable, two-way, connection-based byte streams. An out-of-band data transmission mechanism may be supported.”
另外一个常用的是SOCK_DGRAM
表示Socket Diagram
,是无连接的、不可靠的传输方式,Kernel.org 给出的解释是“Supports datagrams (connectionless, unreliable messages of a fixed maximum length).”
第三个参数表示使用的协议族中的哪个协议。一般来说一个协议族经常只有一个协议,所以长使用“0”。具体参见Kernel.org 给出的解释。
1.1.1.2 绑定地址与端口
首先要创建一个struct sockaddr_in
,并设置地址族、监听的外来地址与本地端口号。如下:
// struct sockaddr_in is inclued in netinet/in.h
// bzero function is included in string.h
// atoi is include in stdlib.h
struct sockaddr_in server_addr;
bzero((char *) &server_addr, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(atoi(argv[1]))
然后将第 1 步创建的Socket
与这里创建的地址绑定(实际上直接用的是Socket Descriptor
)。