1.创建套接字:socket()
int __domain:UNIX系统支持的地址族有:AF_UNIX、AF_INET、AF_NS等,而DOS、WINDOWS中仅支持AF_INET,它是网际网区域,Linux环境下AF_INET就可以了。
int __type:是一种类型,这里可以采用SOCK_STREAM
int __protocol:参数protocol说明该套接字使用的特定协议,如果调用者不希望特别指定使用的协议,则置为0,使用默认的连接模式。
根据这三个参数建立一个套接字,并将相应的资源分配给它,同时返回一个整型套接字号。
2.指定本地地址──bind()
当一个套接字用socket()创建后,存在一个名字空间(地址族),但它没有被命名。bind()将套接字地址(包括本地主机地址和本地端口地址)与所创建的套接字号联系起来,即将名字赋予套接字,以指定本地半相关。
eg:
这里的sin 就是我们定义的struct sockaddr_in,
socket_descriptor就是上面socket()返回的那个套接字号。
返回:
成功:0
失败:SOCKET_ERROR,我们可以通过PERROR这个函数来打印失败原因。
3.建立套接字连接──connect()与accept()
这两个系统调用用于完成一个完整相关的建立,其中connect()用于建立连接。无连接的套接字进程也可以调用connect(),但这时在进程之间没有实际的报文交换,调用将从本地操作系统直接返回。这样做的优点是程序员不必为每一数据指定目的地址,而且如果收到的一个数据报,其目的端口未与任何套接字建立“连接”,而accept()用于使服务器等待来自某客户进程的实际连接。
eg:
socket_descriptor:就是上面获取的套接字号,sin是我们定义的struct sockaddr_in,最后一个参数就是这种结构体的大小。
四个套接字系统调用,socket()、bind()、connect()、accept(),可以完成一个完全五元相关的建立。socket()指定五元组中的协议元,它的用法与是否为客户或服务器、是否面向连接无关。bind()指定五元组中的本地二元,即本地主机地址和端口号,其用法与是否面向连接有关:在服务器方,无论是否面向连接,均要调用bind();若采用面向连接,则可以不调用bind(),而通过connect()自动完成。若采用无连接,客户方必须使用bind()以获得一个唯一的地址。
4.监听连接──listen()
此调用用于面向连接服务器,表明它愿意接收连接。listen()需在accept()之前调用,其调用格式如下:
int PASCAL FAR listen(SOCKET s, int backlog);
参数s标识一个本地已建立、尚未连接的套接字号,服务器愿意从它上面接收请求。backlog表示请求连接队列的最大长度,用于限制排队请求的个数,目前允许的最大值为5。如果没有错误发生,listen()返回0。否则它返回SOCKET_ERROR。
listen()在执行调用过程中可为没有调用过bind()的套接字s完成所必须的连接,并建立长度为backlog的请求连接队列。
调用listen()是服务器接收一个连接请求的四个步骤中的第三步。它在调用socket()分配一个流套接字,且调用bind()给s赋于一个名字之后调用,而且一定要在accept()之前调用。
5 数据传输──send()与recv()
当一个连接建立以后,就可以传输数据了。常用的系统调用有send()和recv()。
send()调用用于套接字标识符s指定的已连接的数据报或流套接字上发送输出数据,格式如下:
int PASCAL FAR send(SOCKET s, const char FAR *buf, int len, int flags);
参数s为已连接的本地套接字描述符。buf 指向存有发送数据的缓冲区的指针,其长度由len 指定。flags 指定传输控制方式,如是否发送带外数据等。如果没有错误发生,send()返回总共发送的字节数。否则它返回SOCKET_ERROR。
recv()调用用于套接字标识符指定的已连接的数据报或流套接字上接收输入数据,格式如下:
int PASCAL FAR recv(SOCKET s, char FAR *buf, int len, int flags);
参数s 为已连接的套接字描述符。buf指向接收输入数据缓冲区的指针,其长度由len 指定。flags 指定传输控制方式,如是否接收带外数据等。如果没有错误发生,recv()返回总共接收的字节数。如果连接被关闭,返回0。否则它返回SOCKET_ERROR。
下面以一个简单的实例来记录我的学习
server.c
client.c
- /* Create a new socket of type TYPE in domain DOMAIN, using
- protocol PROTOCOL. If PROTOCOL is zero, one is chosen automatically.
- Returns a file descriptor for the new socket, or -1 for errors. */
- extern int socket (int __domain, int __type, int __protocol) __THROW;
int __domain:UNIX系统支持的地址族有:AF_UNIX、AF_INET、AF_NS等,而DOS、WINDOWS中仅支持AF_INET,它是网际网区域,Linux环境下AF_INET就可以了。
int __type:是一种类型,这里可以采用SOCK_STREAM
int __protocol:参数protocol说明该套接字使用的特定协议,如果调用者不希望特别指定使用的协议,则置为0,使用默认的连接模式。
根据这三个参数建立一个套接字,并将相应的资源分配给它,同时返回一个整型套接字号。
2.指定本地地址──bind()
- /* Give the socket FD the local address ADDR (which is LEN bytes long). */
- extern int bind (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)
当一个套接字用socket()创建后,存在一个名字空间(地址族),但它没有被命名。bind()将套接字地址(包括本地主机地址和本地端口地址)与所创建的套接字号联系起来,即将名字赋予套接字,以指定本地半相关。
eg:
- bind(socket_descriptor,(struct socketaddr *)&sin, sizeof(sin) );
这里的sin 就是我们定义的struct sockaddr_in,
socket_descriptor就是上面socket()返回的那个套接字号。
返回:
成功:0
失败:SOCKET_ERROR,我们可以通过PERROR这个函数来打印失败原因。
3.建立套接字连接──connect()与accept()
- /* Open a connection on socket FD to peer at ADDR (which LEN bytes long).
- For connectionless socket types, just set the default address to send to
- and the only address from which to accept transmissions.
- Return 0 on success, -1 for errors. */
- extern int connect (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)
- /* Await a connection on socket FD.
- When a connection arrives, open a new socket to communicate with it,
- set *ADDR (which is *ADDR_LEN bytes long) to the address of the connecting
- peer and *ADDR_LEN to the address's actual length, and return the
- new socket's descriptor, or -1 for errors. */
- extern int accept (int __fd, __SOCKADDR_ARG __addr,
- socklen_t *__restrict __addr_len)
这两个系统调用用于完成一个完整相关的建立,其中connect()用于建立连接。无连接的套接字进程也可以调用connect(),但这时在进程之间没有实际的报文交换,调用将从本地操作系统直接返回。这样做的优点是程序员不必为每一数据指定目的地址,而且如果收到的一个数据报,其目的端口未与任何套接字建立“连接”,而accept()用于使服务器等待来自某客户进程的实际连接。
eg:
- accept(socket_descriptor,(struct socketaddr *)&sin,sizeof(struct sockaddr_in));
- connect(socket_descriptor, (void *)&pin, sizeof(pin))
socket_descriptor:就是上面获取的套接字号,sin是我们定义的struct sockaddr_in,最后一个参数就是这种结构体的大小。
四个套接字系统调用,socket()、bind()、connect()、accept(),可以完成一个完全五元相关的建立。socket()指定五元组中的协议元,它的用法与是否为客户或服务器、是否面向连接无关。bind()指定五元组中的本地二元,即本地主机地址和端口号,其用法与是否面向连接有关:在服务器方,无论是否面向连接,均要调用bind();若采用面向连接,则可以不调用bind(),而通过connect()自动完成。若采用无连接,客户方必须使用bind()以获得一个唯一的地址。
4.监听连接──listen()
此调用用于面向连接服务器,表明它愿意接收连接。listen()需在accept()之前调用,其调用格式如下:
int PASCAL FAR listen(SOCKET s, int backlog);
参数s标识一个本地已建立、尚未连接的套接字号,服务器愿意从它上面接收请求。backlog表示请求连接队列的最大长度,用于限制排队请求的个数,目前允许的最大值为5。如果没有错误发生,listen()返回0。否则它返回SOCKET_ERROR。
listen()在执行调用过程中可为没有调用过bind()的套接字s完成所必须的连接,并建立长度为backlog的请求连接队列。
调用listen()是服务器接收一个连接请求的四个步骤中的第三步。它在调用socket()分配一个流套接字,且调用bind()给s赋于一个名字之后调用,而且一定要在accept()之前调用。
5 数据传输──send()与recv()
当一个连接建立以后,就可以传输数据了。常用的系统调用有send()和recv()。
send()调用用于套接字标识符s指定的已连接的数据报或流套接字上发送输出数据,格式如下:
int PASCAL FAR send(SOCKET s, const char FAR *buf, int len, int flags);
参数s为已连接的本地套接字描述符。buf 指向存有发送数据的缓冲区的指针,其长度由len 指定。flags 指定传输控制方式,如是否发送带外数据等。如果没有错误发生,send()返回总共发送的字节数。否则它返回SOCKET_ERROR。
recv()调用用于套接字标识符指定的已连接的数据报或流套接字上接收输入数据,格式如下:
int PASCAL FAR recv(SOCKET s, char FAR *buf, int len, int flags);
参数s 为已连接的套接字描述符。buf指向接收输入数据缓冲区的指针,其长度由len 指定。flags 指定传输控制方式,如是否接收带外数据等。如果没有错误发生,recv()返回总共接收的字节数。如果连接被关闭,返回0。否则它返回SOCKET_ERROR。
下面以一个简单的实例来记录我的学习
server.c
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <stdio.h>
- #include <string.h>
- int main(void)
- {
- int socket_descriptor;
- socket_descriptor = socket(AF_INET, SOCK_STREAM, 0);
- int port = 8000;
- struct sockaddr_in sin;
- int temp_socket_descriptor;
- int rval=1;
- char buf[1024];
- sin.sin_family = AF_INET;
- sin.sin_addr.s_addr = htonl(INADDR_ANY);
- sin.sin_port = htons(port);
- bind(socket_descriptor,(struct socketaddr *)&sin, sizeof(sin) );
- listen(socket_descriptor, 128);
- if(fork()==0)
- {
- while(1)
- {
- int len=sizeof(sin);
- temp_socket_descriptor = accept(socket_descriptor,(struct socketaddr *)&sin,
- &len);
- if (temp_socket_descriptor<0)
- {
- perror("accept");
- exit(0);
- }
- while (rval)
- {
- memset(buf, 0, sizeof(buf));
- rval=recv(temp_socket_descriptor,buf,1024,0);
- if(rval<0)
- {
- perror("recv");
- exit(1);
- }
- else if(rval==0)
- {
- printf("end receiving\n");
- }
- else if(memcmp(buf,"add",3)==0)
- {
- printf("add the function\n");
- }
- else
- {
- printf("delete the function\n");
- }
- }
- rval=1;
- close(temp_socket_descriptor);
- }
- }
- else
- {
- int i=0;
- for(i=0;i<100;i++)
- {
- sleep(1);
- printf("i am the parent \n");
- }
- wait(NULL);
- }
- return 0;
- }
client.c
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netdb.h>
- #include <stdio.h>
- int main(int argc,char *args[])
- {
- char host_name[1024] ="172.18.4.200";
- struct hostent * server_host_name;
- server_host_name = gethostbyname(host_name);
- int socket_descriptor;
- int port = 8000;
- struct sockaddr_in pin;
- if(argc!=2)
- {
- printf("the number is error\n");
- return 1;
- }
- else
- {
- printf("%s\n",args[1]);
- }
- pin.sin_family = AF_INET;
- pin.sin_addr.s_addr = inet_addr("127.0.0.1");
- pin.sin_port = htons(port);
- bzero(&(pin.sin_zero), 8);
- socket_descriptor = socket(AF_INET, SOCK_STREAM, 0);
- if(connect(socket_descriptor, (void *)&pin, sizeof(pin))==-1)
- {
- perror("connect");
- exit(1);
- }
- if (send(socket_descriptor,args[1],strlen(args[1]),0)<0)
- {
- perror("send");
- exit(2);
- }
- close(socket_descriptor);
- return 0;
- }