bind()函数
  一旦你有一个套接字,你可能要将套接字和机器上的一定的端口关联 起来。(如果你想用listen()来侦听一定端口的数据,这是必要一步--MUD 告 诉你说用命令 "telnet x.y.z 6969"。)如果你只想用 connect(),那么这个步 骤没有必要。但是无论如何,请继续读下去。
这里是系统调用 bind() 的大概:
#include <sys/types.h>
#include <sys/socket.h>
int bind(int sockfd, struct sockaddr *my_addr, int addrlen); 
sockfd 是调用 socket 返回的文件描述符。my_addr 是指向数据结构 struct sockaddr 的指针,它保存你的地址(即端口和 IP 地址) 信息。 addrlen 设置为 sizeof(struct sockaddr)。 
简单得很不是吗? 再看看例子: 
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#define MYPORT 3490 
main()
   {
   int sockfd;
   struct sockaddr_in my_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0); /*需要错误检查 */
my_addr.sin_family = AF_INET; /* host byte order */ 
   my_addr.sin_port = htons(MYPORT); /* short, network byte order */ 
   my_addr.sin_addr.s_addr = inet_addr("132.241.5.10"); 
   bzero(&(my_addr.sin_zero),; /* zero the rest of the struct */ 
/* don't forget your error checking for bind(): */ 
   bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)); 
   . 
   . 
   . 
这里也有要注意的几件事情。my_addr.sin_port 是网络字节顺序, my_addr.sin_addr.s_addr 也是的。另外要注意到的事情是因系统的不同, 包含的头文件也不尽相同,请查阅本地的 man 帮助文件。
在 bind() 主题中最后要说的话是,在处理自己的 IP 地址和/或端口的 时候,有些工作是可以自动处理的。
my_addr.sin_port = 0; /* 随机选择一个没有使用的端口 */ 
  my_addr.sin_addr.s_addr = INADDR_ANY; /* 使用自己的IP地址 */ 
通过将0赋给 my_addr.sin_port,你告诉 bind() 自己选择合适的端 口。同样,将 my_addr.sin_addr.s_addr 设置为 INADDR_ANY,你告诉 它自动填上它所运行的机器的 IP 地址。
如果你一向小心谨慎,那么你可能注意到我没有将 INADDR_ANY 转 换为网络字节顺序!这是因为我知道内部的东西:INADDR_ANY 实际上就 是 0!即使你改变字节的顺序,0依然是0。但是完美主义者说应该处处一 致,INADDR_ANY或许是12呢?你的代码就不能工作了,那么就看下面 的代码:
my_addr.sin_port = htons(0); /* 随机选择一个没有使用的端口 */ 
my_addr.sin_addr.s_addr = htonl(INADDR_ANY);/* 使用自己的IP地址 */ 
你或许不相信,上面的代码将可以随便移植。我只是想指出,既然你 所遇到的程序不会都运行使用htonl的INADDR_ANY。
bind() 在错误的时候依然是返回-1,并且设置全局错误变量errno。 
在你调用 bind() 的时候,你要小心的另一件事情是:不要采用小于 1024的端口号。所有小于1024的端口号都被系统保留!你可以选择从1024 到65535的端口(如果它们没有被别的程序使用的话)。
你要注意的另外一件小事是:有时候你根本不需要调用它。如果你使 用 connect() 来和远程机器进行通讯,你不需要关心你的本地端口号(就象 你在使用 telnet 的时候),你只要简单的调用 connect() 就可以了,它会检 查套接字是否绑定端口,如果没有,它会自己绑定一个没有使用的本地端 口。

gethostname()函数
  甚至比 getpeername() 还简单的函数是 gethostname()。它返回你程 序所运行的机器的主机名字。然后你可以使用 gethostbyname() 以获得你 的机器的 IP 地址。
  下面是定义:
  #include <unistd.h>
int gethostname(char *hostname, size_t size);
参数很简单:hostname 是一个字符数组指针,它将在函数返回时保存
主机名。size是hostname 数组的字节长度。
函数调用成功时返回 0,失败时返回 -1,并设置 errno。

域名服务(DNS)
  如果你不知道 DNS 的意思,那么我告诉你,它代表域名服务(Domain Name Service)。它主要的功能是:你给它一个容易记忆的某站点的地址, 它给你 IP 地址(然后你就可以使用 bind(), connect(), sendto() 或者其它 函数) 。当一个人输入:
   $ telnet whitehouse.gov 
telnet 能知道它将连接 (connect()) 到 "198.137.240.100"。 
但是这是如何工作的呢? 你可以调用函数 gethostbyname(): 
#include <netdb.h>
  struct hostent *gethostbyname(const char *name); 
很明白的是,它返回一个指向 struct hostent 的指针。这个数据结构 是这样的:
   struct hostent {
   char *h_name;
   char **h_aliases;
   int h_addrtype;
   int h_length;
   char **h_addr_list;
   };
   #define h_addr h_addr_list[0] 
这里是这个数据结构的详细资料: 
struct hostent: 
  h_name – 地址的正式名称。
  h_aliases – 空字节-地址的预备名称的指针。
  h_addrtype –地址类型; 通常是AF_INET。 
  h_length – 地址的比特长度。
  h_addr_list – 零字节-主机网络地址指针。网络字节顺序。
  h_addr - h_addr_list中的第一地址。
gethostbyname() 成功时返回一个指向结构体 hostent 的指针,或者 是个空 (NULL) 指针。(但是和以前不同,不设置errno,h_errno 设置错 误信息。请看下面的 herror()。) 
但是如何使用呢? 有时候(我们可以从电脑手册中发现),向读者灌输 信息是不够的。这个函数可不象它看上去那么难用。
这里是个例子:

#include <stdio.h>
  #include <stdlib.h>
  #include <errno.h>
  #include <netdb.h>
  #include <sys/types.h>
  #include <netinet/in.h>
int main(int argc, char *argv[])
   {
   struct hostent *h;
if (argc != ) { /* 检查命令行 */
   fprintf(stderr,"usage: getip address\n");
   exit();
   }
if ((h=gethostbyname(argv[])) == NULL) { /* 取得地址信息 */
   herror("gethostbyname");
   exit();
   }
printf("Host name : %s\n", h->h_name);
  printf("IP Address : %s\n",inet_ntoa(*((struct in_addr *)h->h_addr)));
return ;
   }

在使用 gethostbyname() 的时候,你不能用 perror() 打印错误信息 (因为 errno 没有使用),你应该调用 herror()。
相当简单,你只是传递一个保存机器名的字符串(例如 "whitehouse.gov") 给 gethostbyname(),然后从返回的数据结构 struct hostent 中获取信息。 
唯一也许让人不解的是输出 IP 地址信息。h->h_addr 是一个 char *, 但是 inet_ntoa() 需要的是 struct in_addr。因此,我转换 h->h_addr 成 struct in_addr *,然后得到数据。

客户-服务器背景知识
  这里是个客户--服务器的世界。在网络上的所有东西都是在处理客户进 程和服务器进程的交谈。举个telnet 的例子。当你用 telnet (客户)通过23 号端口登陆到主机,主机上运行的一个程序(一般叫 telnetd,服务器)激活。 它处理这个连接,显示登陆界面,等等。

图2:客户机和服务器的关系
图 2 说明了客户和服务器之间的信息交换。 
注意,客户--服务器之间可以使用SOCK_STREAM、SOCK_DGRAM 或者其它(只要它们采用相同的)。一些很好的客户--服务器的例子有 telnet/telnetd、 ftp/ftpd 和 bootp/bootpd。每次你使用 ftp 的时候,在远 端都有一个 ftpd 为你服务。 
一般,在服务端只有一个服务器,它采用 fork() 来处理多个客户的连 接。基本的程序是:服务器等待一个连接,接受 (accept()) 连接,然后 fork() 一个子进程处理它。这是下一章我们的例子中会讲到的。

简单的服务器
  这个服务器所做的全部工作是在流式连接上发送字符串 "Hello, World!\n"。你要测试这个程序的话,可以在一台机器上运行该程序,然后 在另外一机器上登陆: 
   $ telnet remotehostname 3490 
remotehostname 是该程序运行的机器的名字。 
服务器代码:

/*
** server.c -- a stream socket server demo
*/ #include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h> #define PORT "3490" // the port users will be connecting to #define BACKLOG 10 // how many pending connections queue will hold void sigchld_handler(int s)
{
while(waitpid(-, NULL, WNOHANG) > );
} // get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
if (sa->sa_family == AF_INET) {
return &(((struct sockaddr_in*)sa)->sin_addr);
} return &(((struct sockaddr_in6*)sa)->sin6_addr);
} int main(void)
{
int sockfd, new_fd; // listen on sock_fd, new connection on new_fd
struct addrinfo hints, *servinfo, *p;
struct sockaddr_storage their_addr; // connector's address information
socklen_t sin_size;
struct sigaction sa;
int yes=;
char s[INET6_ADDRSTRLEN];
int rv; memset(&hints, , sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE; // use my IP if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != ) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return ;
} // loop through all the results and bind to the first we can
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -) {
perror("server: socket");
continue;
} if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
sizeof(int)) == -) {
perror("setsockopt");
exit();
} if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -) {
close(sockfd);
perror("server: bind");
continue;
} break;
} if (p == NULL) {
fprintf(stderr, "server: failed to bind\n");
return ;
} freeaddrinfo(servinfo); // all done with this structure if (listen(sockfd, BACKLOG) == -) {
perror("listen");
exit();
} sa.sa_handler = sigchld_handler; // reap all dead processes
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, NULL) == -) {
perror("sigaction");
exit();
} printf("server: waiting for connections...\n"); while() { // main accept() loop
sin_size = sizeof their_addr;
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
if (new_fd == -) {
perror("accept");
continue;
} inet_ntop(their_addr.ss_family,
get_in_addr((struct sockaddr *)&their_addr),
s, sizeof s);
printf("server: got connection from %s\n", s); if (!fork()) { // this is the child process
close(sockfd); // child doesn't need the listener
if (send(new_fd, "Hello, world!", , ) == -)
perror("send");
close(new_fd);
exit();
}
close(new_fd); // parent doesn't need this
} return ;
}

或简单版本:http://tsnc.zhongaokao.com/tsnc_wgrj/doc/socket.html

如果你很挑剔的话,一定不满意我所有的代码都在一个很大的main() 函数中。如果你不喜欢,可以划分得更细点。
你也可以用我们下一章中的程序得到服务器端发送的字符串。

简单的客户程序 
  这个程序比服务器还简单。这个程序的所有工作是通过 3490 端口连 接到命令行中指定的主机,然后得到服务器发送的字符串。 
客户代码:

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h> #define PORT 3490
#define MAXDATASIZE 100 //每次可以接收的最大字节 void main(int argc,char *argv)
{
int sockfd,numbytes;
char buf[MAXDATASIZE];
struct sockaddr_in their_addr;//connector's addr info if(argc!=)
{
printf("usage:./cient <ipaddr>\n");
exit(-);
}
if((sockfd=socket(AF_INET,SOCK_STREAM,))==-)
{
perror("socket");
exit();
}
memset(&their_addr,,sizeof(their_addr));
their_addr.sin_family=AF_INET;
their_addr.sin_port=htons(PORT); inet_aton(argv[],&their_addr.sin_addr); if(connect(sockfd,(struct sockaddr*)&their_addr,sizeof(struct sockaddr))==-)
{
perror("connect"); exit();
}
if((numbytes=recv(sockfd,buf,MAXDATASIZE,))==-)
{
perror("recv");
exit();
}
buf[numbytes]='\0';
printf("Received:%s",buf);
close(sockfd);
}

运行这个程序出错:段错误,核心已存储。

6.3. Datagram Sockets

We've already covered the basics of UDP datagram sockets with our discussion of sendto() and recvfrom(), above, so I'll just present a couple of sample programs: talker.cand listener.c.

listener sits on a machine waiting for an incoming packet on port 4950. talker sends a packet to that port, on the specified machine, that contains whatever the user enters on the command line.

Here is the source for listener.c:

Beej网络socket编程指南的更多相关文章

  1. Socket编程指南及示例程序(转)

    1         前言 在一些常用的编程技术中,Socket网络编程可以说是最简单的一种.而且Socket编程需要的基础知识很少,适合初学者学习网络编程.目前支持网络传输的技术.语言和工具繁多,但是 ...

  2. UDP协议网络Socket编程(java实现C/S通信案例)

    我的博客园:https://www.cnblogs.com/chenzhenhong/p/13825286.html 我的CSDN博客:https://blog.csdn.net/Charzous/a ...

  3. Java:基于TCP协议网络socket编程(实现C/S通信)

    目录 一.前言:TCP原理简介 二.Socket编程通信 三.TCP服务器端(具体代码) 四.TCP客户端(具体代码) 五.通信效果演示 六."创意"机器人:价值一个亿的AI核心代 ...

  4. java多线程实现TCP网络Socket编程(C/S通信)

    目录 开篇必知必会 一.多线程技术 二.实现多线程接收 1.单线程版本 2.多线程版本 三.多线程与进程的关系 四.客户端界面完整代码 五.多线程通信对比 最后 开篇必知必会 在前一篇<Java ...

  5. [转]C语言SOCKET编程指南

    1.介绍 Socket编程让你沮丧吗?从man pages中很难得到有用的信息吗?你想跟上时代去编Internet相关的程序,但是为你在调用 connect() 前的bind() 的结构而不知所措?等 ...

  6. C语言SOCKET编程指南

    1.介绍 Socket 编程让你沮丧吗?从man pages中很难得到有用的信息吗?你想跟上时代去编Internet相关的程序,但是为你在调用 connect() 前的bind() 的结构而不知所措? ...

  7. Java Socket 编程指南

    Socket,又称为套接字,Socket是计算机网络通信的基本的技术之一.如今大多数基于网络的软件,如浏览器,即时通讯工具甚至是P2P下载都是基于Socket实现的.本文会介绍一下基于TCP/IP的S ...

  8. Socket编程指南及示例程序

    例子代码就在我的博客中,包括六个UDP和TCP发送接受的cpp文件,一个基于MFC的局域网聊天小工具工程,和此小工具的所有运行时库.资源和执行程序.代码的压缩包位置是http://www.blogja ...

  9. 【java】网络socket编程简单示例

    package 网络编程; import java.io.IOException; import java.io.PrintStream; import java.net.ServerSocket; ...

随机推荐

  1. export命令和import命令 详解

    export命令 模块功能主要由两个命令构成:export和import. export命令用于用户自定义模块,规定对外接口: import命令用于输入其他模块提供的功能,同时创造命名空间(names ...

  2. JSON 与List转换类封装

    json与list转换小结: import java.util.ArrayList; import java.util.List; import com.google.gson.Gson; impor ...

  3. javascript 之封装技巧

    技术--- 闭包 javascript中的对象属性都是公开的,外界都可访问到,例: function cat(){ this.name = '猫'; this.climb = function(){ ...

  4. AudioStreamer使用之快速点击下/上一首按钮,音频会重复的问题解决。

    针对此问题,我的做法是在AudioStreamer.h文件新加入一个属性 @property (nonatomic,strong) NSURL *musicUrl; 和一个单例方法: +(id)sha ...

  5. redis基本数据类型【2】-Hash类型

    一.概述 1.散列是一种典型的字典结构,filed和value的映射,但value只能存储字符串,不支持其他类型 2.一个散列类型最多包含 2^32 -1个字段 3.散列适合存储对象:使用对象和ID构 ...

  6. Ubuntu_14.04安装docker

    Ubuntu_14.04安装docker $ sudo apt-get update $ sudo apt-get install apt-transport-https ca-certificate ...

  7. Token 的作用

    Token,就是令牌,最大的特点就是随机性,不可预测.一般黑客或软件无法猜测出来. 那么,Token有什么作用?又是什么原理呢? Token一般用在两个地方: 1)防止表单重复提交. 2)anti c ...

  8. scrapy, 自带命令行调用工具.

    #-*- coding:utf-8 -*- from scrapy import cmdline cmdline.execute("scrapy crawl dmoz".split ...

  9. JamCam创业故事:辞掉工作,去开发一个应用

    编者按:这是JamCam创始人的自述.这家初创公司提供的应用很简单,但是极为成功:有了JamCam,你所录制的视频会自动添加你正在iPhone中聆听的音乐,作为视频的背景音乐.和朋友分享时是不是方便多 ...

  10. android软件开发之webView.addJavascriptInterface循环渐进【一】

    本篇文章由:http://www.sollyu.com/android-software-development-webview-addjavascriptinterface-cycle-of-gra ...