1 /* socket->bind->listen->accept->recv/recvfrom->send/sendto->close 2 3 客户端:socket->connect->send/sendto->recv/recvfrom->close 4 5 其中服务器端首先建立起socket,然后调用本地端口的绑定,接着就开始与客服端建立联系,并接收客户端发送的消息。 6 客户端则在建立socket之后调用connect函数来建立连接。 7 8 服务器端的源代码如下所示: */ 9 10 /* "server.c" */ 11 12 #include<sys/types.h> 13 #include<sys/socket.h> 14 #include<stdio.h> 15 #include<stdlib.h> 16 #include<errno.h> 17 #include< string .h> 18 #include<unistd.h> 19 #include<netinet/ in .h> 20 21 #define PORT 3490 // 端口 22 23 #define BUFFER_SIZE 1024 // 缓冲区大小 24 25 #define MAX_QUE_CONN_NM 5 // 服务器等待连接队列的最大长度。 26 27 int main(){ 28 29 struct sockaddr_in server_sockaddr,client_sockaddr; // 分别定义服务器和客户端套接字 30 int sin_size,recvbytes; 31 int server_fd,client_fd; 32 char buf[BUFFER_SIZE]; // 缓冲区 33 34 /* 35 SOCKET PASCAL FAR socket( int af, int type, int protocol); 36 af:一个地址描述。目前仅支持AF_INET格式,也就是说ARPA Internet地址格式。 37 type:指定socket类型。新套接口的类型描述类型,如TCP(SOCK_STREAM)和UDP(SOCK_DGRAM)。 38 常用的socket类型有,SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等等。 39 protocol:顾名思义,就是指定协议。套接口所用的协议。如调用者不想指定,可用0。 40 常用的协议有,IPPROTO_TCP、IPPROTO_UDP、IPPROTO_SCTP、IPPROTO_TIPC等, 41 它们分别对应TCP传输协议、UDP传输协议、STCP传输协议、TIPC传输协议。 42 */ 43 if ((server_fd = socket(AF_INET,SOCK_STREAM, 0 ))== - 1 ){ // 建立socket连接www.linuxidc.com 44 perror( " create socket fail " ); 45 exit( 1 ); 46 } 47 48 printf( " Socket id=%d\n " ,server_fd); 49 50 /* 设置sockaddr_in结构体中的相关参数 */ 51 52 server_sockaddr.sin_family = AF_INET; 53 server_sockaddr.sin_port = htons(PORT); // 由于在写网络程序时字节的网络顺序和主机顺序会有问题 54 server_sockaddr.sin_addr.s_addr = INADDR_ANY; // 即0.0.0.0 任意地址 55 bzero(&(server_sockaddr.sin_zero), 8 ); 56 int i = 1 ; // 允许重复使用本地地址与套接字进行绑定 57 58 /* int PASCAL FAR setsockopt(SOCKET s,int level,int optname,const char FAR *optval,int optlen); 59 s:标识一个套接字的描述符。 60 level:选项定义的层次;目前仅支持SOL_SOCKET和IPPROTO_TCP层次。 61 optname:需设置的选项。 62 optval:指针,指向存放选项值的缓冲区。 63 optlen:optval缓冲区长度。 64 */ 65 setsockopt(server_fd,SOL_SOCKET,SO_REUSEADDR,&i, sizeof (i)); 66 67 /* 68 int bind(SOCKET socket, const struct sockaddr *address, 69 socklen_t address_len); 70 参数说明: 71 socket:是一个套接字。 72 address:是一个sockaddr结构指针,该结构中包含了要结合的地址和端口号。 73 address_len:确定address缓冲区的长度。 74 返回值:如果函数执行成功,返回值为0,否则为SOCKET_ERROR。 75 */ 76 if (bind(server_fd,( struct sockaddr *)&server_sockaddr, sizeof ( struct sockaddr)) == - 1 ){ // 绑定函数bind 77 perror( " bind fail " ); 78 exit( 1 ); 79 } 80 81 printf( " Bind success!\n " ); 82 83 /* 84 int PASCAL FAR listen( SOCKET s, int backlog); 85 S:用于标识一个已捆绑未连接套接口的描述字。 86 backlog:等待连接队列的最大长度。 87 */ 88 if (listen(server_fd,MAX_QUE_CONN_NM)== - 1 ){ // 调用listen函数,创建为处理请求的队列 89 perror( " listen fail " ); 90 exit( 1 ); 91 } 92 93 printf( " Listening......\n " ); 94 95 /* 96 SOCKET PASCAL FAR accept( SOCKET s, struct sockaddr FAR* addr,int FAR* addrlen); 97 s:套接口描述字,该套接口在listen()后监听连接。 98 addr:(可选)指针,指向一缓冲区,其中接收为通讯层所知的连接实体的地址。Addr参数的实际格式由套接口创建时所产生的地址族确定。 99 addrlen:(可选)指针,输入参数,配合addr一起使用,指向存有addr地址长度的整型数。 100 */ 101 if ((client_fd = accept(server_fd,( struct sockaddr *)&client_sockaddr,&sin_size))==- 1 ){ // 调用accept函数,等待客户端的接 102 perror( " accept fail " ); 103 exit( 1 ); 104 } 105 106 printf( " server: got connection from %s \n " ,inet_ntoa(client_sockaddr.sin_addr)); 107 108 memset(buf, 0 , sizeof (buf)); 109 /* 110 int PASCAL FAR recv( SOCKET s, char FAR* buf, int len, int flags); 111 s:一个标识已连接套接口的描述字。 112 buf:用于接收数据的缓冲区。 113 len:缓冲区长度。 114 flags:指定调用方式。通常写成0 115 */ 116 if ((recvbytes = recv(client_fd,buf,BUFFER_SIZE, 0 )) == - 1 ){ // 调用recv函数接收客户端的请求 117 perror( " recv fail " ); 118 exit( 1 ); 119 } 120 121 printf( " Received a message: %s\n " ,buf); 122 123 124 /* 向客户起写数据 */ 125 if (write(client_fd, " 客户端我收到你发来的数据了,你能收到这句应答吗?\n " , 1024 )==- 1 ) 126 perror( " write error! " ); 127 128 close(client_fd); 129 130 close(server_fd); 131 exit( 0 ); 132 } 133 134 135 136 137 138 /* 客户端 */ 139 /* client.c 运行方式:./client localhost */ 140 #include <stdio.h> 141 #include <stdlib.h> 142 #include <errno.h> 143 #include < string .h> 144 #include <netdb.h> 145 #include <sys/types.h> 146 #include <netinet/ in .h> 147 #include <sys/socket.h> 148 #define PORT 3490 149 #define MAXDATASIZE 5000 150 int main( int argc, char ** argv) 151 { 152 int sockfd,nbytes; 153 char buf[ 1024 ]; 154 struct hostent * he; 155 struct sockaddr_in srvaddr; 156 if (argc!= 2 ) 157 { 158 perror( " Usage:client hostname\n " ); 159 exit( 1 ); 160 } 161 /* 函数gethostbyname获得指定域名地址所对应的ip地址 */ 162 if ((he=gethostbyname(argv[ 1 ]))== NULL) 163 { 164 perror( " gethostbyname " ); 165 exit( 1 ); 166 } 167 /* 创建套接字,返回套接字描述符 */ 168 if ((sockfd=socket(AF_INET,SOCK_STREAM, 0 ))==- 1 ) 169 { 170 perror( " create socket error " ); 171 exit( 1 ); 172 } 173 bzero(&srvaddr, sizeof (srvaddr)); 174 /* 用获得的远程服务器进程的ip地址和端口号来填充一个internet套接字地址结构 */ 175 srvaddr.sin_family= AF_INET; 176 srvaddr.sin_port= htons(PORT); 177 srvaddr.sin_addr=*(( struct in_addr *)he-> h_addr); 178 /* 用connect于这个远程服务器建立一个internet连接 */ 179 if (connect(sockfd,( struct sockaddr *)&srvaddr, sizeof ( struct sockaddr))==- 1 ) 180 { 181 perror( " connect error " ); 182 exit( 1 ); 183 } 184 185 186 if ((send(sockfd, " 客户端向服务端发送数据,服务端你收到了吗? " , 1024 , 0 )) == - 1 ) 187 { 188 perror( " send error " ); 189 exit( 1 ); 190 } 191 192 193 194 /* 调用read函数读取服务器write过来的信息 */ 195 if ((nbytes=read(sockfd,buf,MAXDATASIZE))==- 1 ) 196 { 197 perror( " read error " ); 198 exit( 1 ); 199 } 200 buf[nbytes]= ' \0 ' ; 201 printf( " read: %s " ,buf); 202 close(sockfd); 203 }
运行方式: gcc -o service service.c
gcc -o client client.c
chmod +x service
chmod +x client
在一个终端运行:./service
在另一个终端运行:./client localhost
服务端输出:
Socket id=3 Bind success! Listening...... server: got connection from 127.0.0.1 Received a message: 客户端向服务端发送数据,服务端你收到了吗?
客户端输出:
read: 客户端我收到你发来的数据了,你能收到这句应答吗?