前言
读者可根据需要选择不同的小节进行阅读。

附录一 Linux 服务端网络编程
#include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <netdb.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h>#define PORT_NUM 8888int main(int argc, char *argv[]){int sockfd,new_fd;struct sockaddr_in server_addr; // 服务器地址struct sockaddr_in client_addr; // 客户端地址int sin_size;char hello[]="Hello\n";/* 服务器端开始建立sockfd描述符 */if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) // 使用AF_INET协议栈代表TCP/IP,SOCK_STREAM表明使用协议栈中的TCP协议{fprintf(stderr,"Socket error:%s\n\a",strerror(errno));exit(1);}/* 构建服务端的sockaddr结构 */bzero(&server_addr,sizeof(struct sockaddr_in)); // 初始化server_addr结构体server_addr.sin_family=AF_INET; // 设置协议栈为TCP/IPserver_addr.sin_addr.s_addr=htonl(INADDR_ANY); // 网络上传输数据使用大端序进行描述,我们需要通过htonl函数将字节序转为大端序,INADDR_ANY 表示可以接收任意IP地址的数据,即绑定到所有的IP//server_addr.sin_addr.s_addr=inet_addr("192.168.1.1"); 用于绑定到一个固定IP,inet_addr用于把数字加格式的ip转化为整形ipserver_addr.sin_port=htons(PORT_NUM); // 设置端口号/* 绑定端口信息 */if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1){fprintf(stderr,"Bind error:%s\n\a",strerror(errno));exit(1);}/* 设置允许连接的最大客户端数 */if(listen(sockfd,5)==-1){fprintf(stderr,"Listen error:%s\n\a",strerror(errno));exit(1);}// 循环处理接收到的客户端请求while(1){/* 服务器阻塞,直到客户程序建立连接 */sin_size=sizeof(struct sockaddr_in);if((new_fd=accept(sockfd,(struct sockaddr *)(&client_addr),&sin_size))==-1){fprintf(stderr,"Accept error:%s\n\a",strerror(errno));exit(1);}fprintf(stderr,"Server get connection from %s\n",inet_ntoa(client_addr.sin_addr)); // 将网络地址转换成.字符串,并打印到输出终端//向客户端程序写入hello数组里的字符if(write(new_fd,hello,strlen(hello))==-1){fprintf(stderr,"Write Error:%s\n",strerror(errno));exit(1);}/* 这个通讯已经结束 */close(new_fd);/* 循环下一个 */}/* 结束通讯 */close(sockfd);exit(0);}
附录二 Linux 客户端网络编程
#include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <netdb.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h>#define PORT_NUM 8888 服务端端口号int main(int argc, char *argv[]){int sockfd;char buffer[1024];struct sockaddr_in server_addr; //描述服务器的地址struct hostent *host;int nbytes;/* 使用hostname查询host名字 */if(argc!=2){fprintf(stderr,"Usage:%s hostname \a\n",argv[0]);exit(1);}if((host=gethostbyname(argv[1]))==NULL){fprintf(stderr,"Gethostname error\n");exit(1);}/* 客户程序开始建立 sockfd描述符 */if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) // AF_INET:Internet;SOCK_STREAM:TCP{fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));exit(1);}/* 初始化并设置服务端链接地址 */bzero(&server_addr,sizeof(server_addr)); // 初始化,置0server_addr.sin_family=AF_INET; // 设置协议栈server_addr.sin_port=htons(PORT_NUM); // 端口号server_addr.sin_addr=*((struct in_addr *)host->h_addr); // 服务端IP/* 链接服务端 */if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1){fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));exit(1);}/* 读取服务端响应数据 */if((nbytes=read(sockfd,buffer,1024))==-1){fprintf(stderr,"Read Error:%s\n",strerror(errno));exit(1);}buffer[nbytes]='\0';printf("client received:%s\n",buffer);/* 结束通讯 */close(sockfd);exit(0);}
附录三 Java AIO涉及到的Native JNI实现
1、socket0
JNIEXPORT int JNICALLJava_sun_nio_ch_Net_socket0(JNIEnv *env, jclass cl, jboolean preferIPv6,jboolean stream, jboolean reuse){int fd;int type = (stream ? SOCK_STREAM : SOCK_DGRAM);int domain = AF_INET;fd = socket(domain, type, 0);if (fd < 0) {return handleSocketError(env, errno);}...return fd;}
2、bind0
JNIEXPORT void JNICALLJava_sun_nio_ch_Net_bind0(JNIEnv *env, jclass clazz, jobject fdo, jboolean preferIPv6,jboolean useExclBind, jobject iao, int port){SOCKADDR sa;int sa_len = SOCKADDR_LEN;int rv = 0;if (NET_InetAddressToSockaddr(env, iao, port, (struct sockaddr *)&sa, &sa_len, preferIPv6) != 0) {return;}rv = NET_Bind(fdval(env, fdo), (struct sockaddr *)&sa, sa_len);if (rv != 0) {handleSocketError(env, errno);}}JNIEXPORT int JNICALLNET_Bind(int s, struct sockaddr *him, int len){int rv;rv = bind(s, him, len);if (rv == SOCKET_ERROR) {if (WSAGetLastError() == WSAEACCES) {WSASetLastError(WSAEADDRINUSE);}}return rv;}
3、listen
JNIEXPORT void JNICALLJava_sun_nio_ch_Net_listen(JNIEnv *env, jclass cl, jobject fdo, jint backlog){if (listen(fdval(env, fdo), backlog) < 0)handleSocketError(env, errno);}
4、accept0
JNIEXPORT jint JNICALLJava_sun_nio_ch_ServerSocketChannelImpl_accept0(JNIEnv *env, jobject this,jobject ssfdo, jobject newfdo,jobjectArray isaa){jint ssfd = (*env)->GetIntField(env, ssfdo, fd_fdID);jint newfd;struct sockaddr *sa;int alloc_len;jobject remote_ia = 0;jobject isa;jint remote_port;NET_AllocSockaddr(&sa, &alloc_len); // 初始化sockaddrfor (;;) {socklen_t sa_len = alloc_len;newfd = accept(ssfd, sa, &sa_len);if (newfd >= 0) {break;}if (errno != ECONNABORTED) {break;}}// 没有可用TCP三次握手的socket信息if (newfd < 0) {free((void *)sa);if (errno == EAGAIN)return IOS_UNAVAILABLE;if (errno == EINTR)return IOS_INTERRUPTED;JNU_ThrowIOExceptionWithLastError(env, "Accept failed");return IOS_THROWN;}// 设置Java对象newfdo的fd_fdID(*env)->SetIntField(env, newfdo, fd_fdID, newfd);// 获取远程client信息remote_ia = NET_SockaddrToInetAddress(env, sa, (int *)&remote_port);free((void *)sa); // 释放用于接收的sockaddr// 创建isa_class指定的Java对象,这里对应于InetSocketAddress,可以参考前面的源码描述isa = (*env)->NewObject(env, isa_class, isa_ctorID,remote_ia, remote_port);// 设置数组元素(*env)->SetObjectArrayElement(env, isaa, 0, isa);return 1;}
关于作者:
—————— 进行业交流群 👇推荐关注👇 有趣的行业资讯 干货技术分享 程序员的日常生活 ...... 干就完了
文章转载自马士兵,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。








