socket这个词可以表示很多概念:

在TCP/IP协议中,“IP地址+TCP或UDP端口号”唯一标识网络通讯中的一个进程“IP地址+端口号”就称为socket。

在TCP协议中,建立连接的两个进程各自有一个socket来标识,那么这两个socket组成的socket pair就唯一标识一个连接

socket本身有“插座”的意思,因此用来描述网络连接的一对一关系。

TCP/IP协议最早在BSD UNIX上实现,为TCP/IP协议设计的应用层编程接口称为socket API。

预备知识

网络字节序

我们已经知道,内存中的多字节数据相对于内存地址有大端和小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大端小端之分。

网络数据流同样有大端小端之分,那么如何定义网络数据流的地址呢?发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出

接收主机把从网络上接到的字节依次保存在接收缓冲区中,也是按内存地址从低到高的顺序保存,因此,网络数据流的地址应这样规定:先发出的数据是低地址,后发出的数据是高地址。

TCP/IP协议规定,网络数据流应采用大端字节序,即低地址高字节。例如UDP段格式,地址0-1是16位的源端口号,如果这个端口号是1000(0x3e8),则地址0是0x03,

地址1是0xe8,也就是先发0x03,再发0xe8,这16位在发送主机的缓冲区中也应该是低地址存0x03,高地址存0xe8。但是,如果发送主机是小端字节序的,这16位被解释成0xe803,而

不是1000。因此,发送主机把1000填到发送缓冲区之前需要做字节序的转换。同样地,接收主机如果是小端字节序的,接到16位的源端口号也要做字节序的转换。如果主机是大端字节

序的,发送和接收都不需要做转换。同理,32位的IP地址也要考虑网络字节序和主机字节序的问题。

为使网络程序具有可移植性,使同样的C代码在大端和小端计算机上编译后都能正常运行,可以调用以下库函数做网络字节序和主机字节序的转换。

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
h表示host,n表示network,l表示32位长整数,s表示16位短整数。
如果主机是小端字节序,这些函数将参数做相应的大小端转换然后返回,如果主机是大端字节序,这些函数不做转
换,将参数原封不动地返回。

IP地址转换函数
早期
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, struct in_addr *inp);
in_addr_t inet_addr(const char *cp);
char *inet_ntoa(struct in_addr in);
只能处理IPv4的ip地址
不可重入函数
注意参数是struct in_addr
现在
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void *dst);
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
支持IPv4和IPv6

其中inet_pton和inet_ntop不仅可以转换IPv4的in_addr,还可以转换IPv6的in6_addr,因此函数接口是void *addrptr

sockaddr数据结构

strcut sockaddr 很多网络编程函数诞生早于IPv4协议,那时候都使用的是sockaddr结构体,为了向前兼容,现在sockaddr退化成了(void *)的作用,传递一个地址给函数

至于这个函数是sockaddr_in还是sockaddr_in6,由地址族确定,然后函数内部再强制类型转化为所需的地址类型

struct sockaddr {
sa_family_t sa_family; /* address family, AF_xxx */
char sa_data[14]; /* 14 bytes of protocol address */
};
struct sockaddr_in {
__kernel_sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
/* Pad to size of `struct sockaddr'. */
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
/* Internet address. */
struct in_addr {
__be32 s_addr;
}struct sockaddr_in6 {
unsigned short int sin6_family; /* AF_INET6 */
__be16 sin6_port; /* Transport layer port # */
__be32 sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
__u32 sin6_scope_id; /* scope id (new in RFC2553) */
};
struct in6_addr {
union {
__u8 u6_addr8[16];
__be16 u6_addr16[8];
__be32 u6_addr32[4];
} in6_u;
#define s6_addr in6_u.u6_addr8
#define s6_addr16 in6_u.u6_addr16
#define s6_addr32 in6_u.u6_addr32
};
#define UNIX_PATH_MAX 108
struct sockaddr_un {
__kernel_sa_family_t sun_family; /* AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* pathname */
};

Pv4和IPv6的地址格式定义在netinet/in.h中,IPv4地址用sockaddr_in结构体表示,包括16位端口号和32位IP地址,
IPv6地址用sockaddr_in6结构体表示,包括16位端口号、128位IP地址和一些控制字段。UNIX Domain Socket的地址格式定义在sys/un.h中,用sockaddr_un结构体表示。各种socket地址结构体的开头都是相同的,前16位表示整个结构体的长度(并不是所有UNIX的实现都有长度字段,如Linux就没有),后16位表示地址类型。
IPv4、IPv6和Unix Domain Socket的地址类型分别定义为常数AF_INET、AF_INET6、AF_UNIX。
这样,只要取得某种sockaddr结构体的首地址,不需要知道具体是哪种类型的sockaddr结构体,就可以根据地址类型字段确定结构体中的内容。
因此,socket API可以接受各种类型的sockaddr结构体指针做参数,例如bind、accept、connect等函数,
这些函数的参数应该设计成void *类型以便接受各种类型的指针,但是sock API的实现早于ANSI C标准化,那时还没有void *类型,因此这些函数的参数都用
struct sockaddr *类型表示,在传递参数之前强制类型转换一下,例如:
struct sockaddr_in servaddr;
/* initialize servaddr */
bind(listen_fd, (struct sockaddr *)&servaddr, sizeof(servaddr));

网络套接字函数

socket

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
domain:
AF_INET 这是大多数用来产生socket的协议,使用TCP或UDP来传输,用IPv4的地址
AF_INET6 与上面类似,不过是来用IPv6的地址
AF_UNIX 本地协议,使用在Unix和Linux系统上,一般都是当客户端和服务器在同一台及其上的时候使用
type:
SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的socket类
型,这个socket是使用TCP来进行传输。
SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用UDP来进行它的连接。
SOCK_SEQPACKET 这个协议是双线路的、可靠的连接,发送固定长度的数据包进行传输。必须把这个包完整的
接受才能进行读取。
SOCK_RAW 这个socket类型提供单一的网络访问,这个socket类型使用ICMP公共协议。(ping、traceroute使
用该协议)
SOCK_RDM 这个类型是很少使用的,在大部分的操作系统上没有实现,它是提供给数据链路层使用,不保证数
据包的顺序
protocol:
0 默认协议
返回值:
成功返回一个新的文件描述符,失败返回-1,设置errno

socket()打开一个网络通讯端口,如果成功的话,就像open()一样返回一个文件描
述符,应用程序可以像读写文件一样用read/write在网络上收发数据,如果socket()调
用出错则返回-1。对于IPv4,domain参数指定为AF_INET。对于TCP协议,type参数指定为
SOCK_STREAM,表示面向流的传输协议。如果是UDP协议,则type参数指定为SOCK_DGRAM,
表示面向数据报的传输协议。protocol参数的介绍从略,指定为0即可。

bind

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd:
socket文件描述符
addr:
构造出IP地址加端口号
addrlen:
sizeof(addr)长度
返回值:
成功返回0,失败返回-1, 设置errno

服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序
的地址和端口号后就可以向服务器发起连接,因此服务器需要调用bind绑定一个固定的网络
地址和端口号。
bind()的作用是将参数sockfd和addr绑定在一起,使sockfd这个用于网络通讯的文件
描述符监听addr所描述的地址和端口号。前面讲过,struct sockaddr *是一个通用指针类
型,addr参数实际上可以接受多种协议的sockaddr结构体,而它们的长度各不相同,所以需
要第三个参数addrlen指定结构体的长度。如:

struct sockaddr_in servaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(8000);

首先将整个结构体清零,然后设置地址类型为AF_INET,网络地址为INADDR_ANY,这个
宏表示本地的任意IP地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个IP地址,
这样设置可以在所有的IP地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪
个IP地址,端口号为8000。

listen

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
sockfd:
socket文件描述符
backlog:
排队建立3次握手队列和刚刚建立3次握手队列的链接数和

查看系统默认backlog

cat /proc/sys/net/ipv4/tcp_max_syn_backlog

典型的服务器程序可以同时服务于多个客户端,当有客户端发起连接时,服务器调用的
accept()返回并接受这个连接,如果有大量的客户端发起连接而服务器来不及处理,尚未
accept的客户端就处于连接等待状态,listen()声明sockfd处于监听状态,并且最多允许有
backlog个客户端处于连接待状态,如果接收到更多的连接请求就忽略。listen()成功返回
0,失败返回-1。

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
sockdf:
socket文件描述符
addr:
传出参数,返回链接客户端地址信息,含IP地址和端口号
addrlen:
传入传出参数(值-结果),传入sizeof(addr)大小,函数返回时返回真正接收到地址结
返回值:
成功返回一个新的socket文件描述符,用于和客户端通信,失败返回-1,设置errno

三方握手完成后,服务器调用accept()接受连接,如果服务器调用accept()时还没有
客户端的连接请求,就阻塞等待直到有客户端连接上来。addr是一个传出参数,accept()
返回时传出客户端的地址和端口号。addrlen参数是一个传入传出参数(value-result
argument),传入的是调用者提供的缓冲区addr的长度以避免缓冲区溢出问题,传出的是客
户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区)。如果给addr参数传
NULL,表示不关心客户端的地址。
我们的服务器程序结构是这样的:

while (1) {
cliaddr_len = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
n = read(connfd, buf, MAXLINE);
......
close(connfd);
}

整个是一个while死循环,每次循环处理一个客户端连接。由于cliaddr_len是传入传出
参数,每次调用accept()之前应该重新赋初值。accept()的参数listenfd是先前的监听文件

描述符,而accept()的返回值是另外一个文件描述符connfd,之后与客户端之间就通过这个
connfd通讯,最后关闭connfd断开连接,而不关闭listenfd,再次回到循环开头listenfd仍
然用作accept的参数。accept()成功返回一个文件描述符,出错返回-1

connect

#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockdf:
socket文件描述符
addr:
传入参数,指定服务器端地址信息,含IP地址和端口号
addrlen:
传入参数,传入sizeof(addr)大小
返回值:
成功返回0,失败返回-1,设置errno

server.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <strings.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <ctype.h>#define SERV_PORT 8000int main(void)
{int sfd, cfd;int i, len;struct sockaddr_in serv_addr, client_addr;char buf[4096], client_ip[128];socklen_t addr_len;//AF_INET:ipv4   SOCK_STREAM:流协议   0:默认协议(tcp,udp)sfd = socket(AF_INET, SOCK_STREAM, 0);//绑定前先构造出服务器地址bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;//网络字节序serv_addr.sin_port = htons(SERV_PORT);//INADDR_ANY主机所有ipserv_addr.sin_addr.s_addr = htonl(INADDR_ANY);bind(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));//服务器能接收并发链接的能力listen(sfd, 128);printf("wait for connect ...\n");addr_len = sizeof(client_addr);//阻塞,等待客户端链接,成功则返回新的文件描述符,用于和客户端通信cfd = accept(sfd, (struct sockaddr *)&client_addr, &addr_len);printf("client IP:%s\t%d\n", inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, client_ip, sizeof(client_ip)),ntohs(client_addr.sin_port));while (1) {//阻塞接收客户端数据len = read(cfd, buf, sizeof(buf));write(STDOUT_FILENO, buf, len);//处理业务for (i = 0; i < len; i++)buf[i] = toupper(buf[i]);//返回给客户端结果
        write(cfd, buf, len);}close(cfd);close(sfd);return 0;
}

client

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>#define SERV_PORT 8000int main(int argc, char *argv[])
{int sfd, len;struct sockaddr_in serv_addr;char buf[4096];if (argc < 2) {printf("./client serv_ip\n");return 1;}sfd = socket(AF_INET, SOCK_STREAM, 0);bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SERV_PORT);inet_pton(AF_INET, argv[1], &serv_addr.sin_addr.s_addr);connect(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));while (fgets(buf, sizeof(buf), stdin)) {write(sfd, buf, strlen(buf));len = read(sfd, buf, sizeof(buf));write(STDOUT_FILENO, buf, len);}return 0;
}

多进程并发服务器

#include <stdlib.h>
#include <errno.h>
#include <sys/socket.h>void perr_exit(const char *s)
{perror(s);exit(1);
}int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
{int n;again:if ( (n = accept(fd, sa, salenptr)) < 0) {if ((errno == ECONNABORTED) || (errno == EINTR))goto again;elseperr_exit("accept error");}return n;
}void Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{if (bind(fd, sa, salen) < 0)perr_exit("bind error");
}void Connect(int fd, const struct sockaddr *sa, socklen_t salen)
{if (connect(fd, sa, salen) < 0)perr_exit("connect error");
}void Listen(int fd, int backlog)
{if (listen(fd, backlog) < 0)perr_exit("listen error");
}int Socket(int family, int type, int protocol)
{int n;if ( (n = socket(family, type, protocol)) < 0)perr_exit("socket error");return n;
}ssize_t Read(int fd, void *ptr, size_t nbytes)
{ssize_t n;again:if ( (n = read(fd, ptr, nbytes)) == -1) {if (errno == EINTR)goto again;elsereturn -1;}return n;
}ssize_t Write(int fd, const void *ptr, size_t nbytes)
{ssize_t n;again:if ( (n = write(fd, ptr, nbytes)) == -1) {if (errno == EINTR)goto again;elsereturn -1;}return n;
}void Close(int fd)
{if (close(fd) == -1)perr_exit("close error");
}
ssize_t Readn(int fd, void *vptr, size_t n)
{size_t  nleft;ssize_t nread;char   *ptr;ptr = vptr;nleft = n;while (nleft > 0) {if ( (nread = read(fd, ptr, nleft)) < 0) {if (errno == EINTR)nread = 0;elsereturn -1;} else if (nread == 0)break;nleft -= nread;ptr += nread;}return n - nleft;
}ssize_t Writen(int fd, const void *vptr, size_t n)
{size_t nleft;ssize_t nwritten;const char *ptr;ptr = vptr;nleft = n;while (nleft > 0) {if ( (nwritten = write(fd, ptr, nleft)) <= 0) {if (nwritten < 0 && errno == EINTR)nwritten = 0;elsereturn -1;}nleft -= nwritten;ptr += nwritten;}return n;
}
static ssize_t my_read(int fd, char *ptr)
{static int read_cnt;static char *read_ptr;static char read_buf[100];if (read_cnt <= 0) {
again:if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {if (errno == EINTR)goto again;return -1;} else if (read_cnt == 0)return 0;read_ptr = read_buf;}read_cnt--;*ptr = *read_ptr++;return 1;
}ssize_t Readline(int fd, void *vptr, size_t maxlen)
{ssize_t n, rc;char    c, *ptr;ptr = vptr;for (n = 1; n < maxlen; n++) {if ( (rc = my_read(fd, &c)) == 1) {*ptr++ = c;if (c  == '\n')break;} else if (rc == 0) {*ptr = 0;return n - 1;} elsereturn -1;}*ptr  = 0;return n;
}

wrap.c

#ifndef __WRAP_H_
#define __WRAP_H_void perr_exit(const char *s);
int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr);
void Bind(int fd, const struct sockaddr *sa, socklen_t salen);
void Connect(int fd, const struct sockaddr *sa, socklen_t salen);
void Listen(int fd, int backlog);
int Socket(int family, int type, int protocol);
ssize_t Read(int fd, void *ptr, size_t nbytes);
ssize_t Write(int fd, const void *ptr, size_t nbytes);
void Close(int fd);
ssize_t Readn(int fd, void *vptr, size_t n);
ssize_t Writen(int fd, const void *vptr, size_t n);
static ssize_t my_read(int fd, char *ptr);
ssize_t Readline(int fd, void *vptr, size_t maxlen);#endif

wrap.h

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <ctype.h>
#include <signal.h>#include "wrap.h"#define SERV_PORT 8000void do_sig(int num)
{while (waitpid(0, NULL, WNOHANG) > 0);
}
int main(int argc, char *argv[])
{int lfd, cfd, len, i;int serv_port = SERV_PORT;char buf[1024], client_ip[128];struct sockaddr_in serv_addr, client_addr;socklen_t client_len;pid_t pid;struct sigaction act;act.sa_handler = do_sig;act.sa_flags = 0;sigemptyset(&act.sa_mask);sigaction(SIGCHLD, &act, NULL);if (argc == 2) serv_port = atoi(argv[1]);lfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons((short)serv_port);serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);Bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));Listen(lfd, 128);printf("wait for connect...\n");while (1) {client_len = sizeof(client_addr);cfd = Accept(lfd, (struct sockaddr *)&client_addr, &client_len);printf("client:%s\t%d\n",inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, client_ip, sizeof(client_ip)),ntohs(client_addr.sin_port));pid = fork();if (pid == 0) {//in child
            Close(lfd);while (1) {len = Read(cfd, buf, sizeof(buf));if (len <= 0)break;Write(STDOUT_FILENO, buf, len);for (i = 0; i < len; ++i)buf[i] = toupper(buf[i]);Write(cfd, buf, len);}Close(cfd);return 0;} else if (pid > 0) {//in parent
            Close(cfd);} else {perror("fork");exit(1);}}Close(lfd);return 0;
}

客户端

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <arpa/inet.h>#define SERV_PORT 8000int main(int argc, char *argv[])
{int sfd, len;struct sockaddr_in serv_addr;char buf[4096];if (argc < 2) {printf("./client serv_ip\n");return 1;}sfd = socket(AF_INET, SOCK_STREAM, 0);bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SERV_PORT);inet_pton(AF_INET, argv[1], &serv_addr.sin_addr.s_addr);connect(sfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));while (fgets(buf, sizeof(buf), stdin)) {write(sfd, buf, strlen(buf));len = read(sfd, buf, sizeof(buf));write(STDOUT_FILENO, buf, len);}return 0;
}

多线程并发服务器

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <ctype.h>
#include <signal.h>#include "wrap.h"#define SERV_PORT 8000void *do_work(void *arg)
{char buf[1024];int len, i;int cfd = (int)arg;pthread_detach(pthread_self());while (1) {len = Read(cfd, buf, sizeof(buf));if (len <= 0)break;Write(STDOUT_FILENO, buf, len);for (i = 0; i < len; ++i)buf[i] = toupper(buf[i]);Write(cfd, buf, len);}Close(cfd);return 0;
}int main(int argc, char *argv[])
{int lfd, cfd;int serv_port = SERV_PORT;char client_ip[128];struct sockaddr_in serv_addr, client_addr;socklen_t client_len;pthread_t tid;if (argc == 2) serv_port = atoi(argv[1]);lfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&serv_addr, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons((short)serv_port);serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);Bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));Listen(lfd, 128);printf("wait for connect...\n");while (1) {client_len = sizeof(client_addr);cfd = Accept(lfd, (struct sockaddr *)&client_addr, &client_len);printf("client:%s\t%d\n",inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, client_ip, sizeof(client_ip)),ntohs(client_addr.sin_port));pthread_create(&tid, NULL, do_work, (void *)cfd);}return 0;
}

多路I/O复用

select服务器

poll服务器

epoll服务器

转载于:https://www.cnblogs.com/xiangtingshen/p/10909231.html

socket编程和并发服务器相关推荐

  1. 网络socket编程实现并发服务器——多线程编程

    一.多线程简介 1.什么是线程? 线程在操作系统原理中是这样描述的:线程是进程的一条执行路径.线程在Unix系统下,通常被称为轻量级的进程,线程虽然不是进程,但却可以看作是Unix进程的表亲,所有的线 ...

  2. 网络基础+Socket编程+高并发服务器

    网络编程 P1 复习 Linux 网络编程 P2 信号量生产者复习 P3 协议 P4 七层模型和四层模型及代表协议 P5 网络传输数据封装流程 P6 以太网帧和ARP请求 P7 IP 协议 P8 TC ...

  3. 用JAVA SOCKET编程,读服务器几个字符,再写入本地显示

    Server: package cn.itcast.framework.socket;import java.io.BufferedReader; import java.io.IOException ...

  4. Android Socket编程android端服务器和客户端的实现

     Android Socket编程android端服务器和客户端的实现       其实和java实现的差不多,或本质是用java实现的,但由于android有自身的独特性,所以还是有一些要注意的点: ...

  5. 码农翻身讲网络3:从Socket编程到HTTP服务器

    小白科普:从输入网址到最后浏览器呈现页面内容,中间发生了什么?(HTTP请求) 原创: 老刘 码农翻身 1月2日 1前言 这篇文章是应网友之邀所写,主要描述一下我们访问网站时, 从输入网址到最后浏览器 ...

  6. 【Linux网络编程】并发服务器的三种实现模型

    服务器设计技术有很多,按使用的协议来分有 TCP 服务器和 UDP 服务器,按处理方式来分有循环服务器和并发服务器. 循环服务器与并发服务器模型 在网络程序里面,一般来说都是许多客户对应一个服务器(多 ...

  7. linux网络编程之并发服务器的三种实现模型 (超级经典)

    转载 : http://blog.csdn.net/tennysonsky/article/details/45671215 服务器设计技术有很多,按使用的协议来分有 TCP 服务器和 UDP 服务器 ...

  8. Linux网络编程——tcp并发服务器(多线程)

    https://blog.csdn.net/lianghe_work/article/details/46504243 tcp多线程并发服务器 多线程服务器是对多进程服务器的改进,由于多进程服务器在创 ...

  9. Linux网络编程——tcp并发服务器(多进程)

    https://blog.csdn.net/lianghe_work/article/details/46503895 一.tcp并发服务器概述 一个好的服务器,一般都是并发服务器(同一时刻可以响应多 ...

最新文章

  1. 利用关系数据库开展智能化营销新思路详解
  2. MongoDB数据库的下载与Python交互
  3. SQL Server 2000/2005 数据库分页
  4. 传苹果正与米高梅谈判 拟购买新007电影流媒体版权
  5. 课后作业-阅读任务-阅读笔记3
  6. WinKawaks + 拳皇97 ROM 包在 PC 搭建拳皇97
  7. 谁的青春不迷惘,谁的年少不忧伤
  8. wps如何删除空白页?wps删除空白页的方法
  9. Ceph RBD 接口和工具 [Ceph RBD API and Tools]
  10. Flex语法和常用鼠标手势
  11. 基于面部视频的实时心率检测系统 day four
  12. Android 64位变32位
  13. Word取消自动更改、自动更正
  14. 正大新闻:炒期货巨亏7000万引股价大跌豪悦护理回购+增持
  15. C#从字节数组截取片段(转载)
  16. easywechat Api大全
  17. 33岁适合学java吗_33岁女生想学习Java好不好
  18. 今夜,整个中国零售业集体失眠!
  19. html预览dwg文件,大佬救命!有关dwg文件预览的问题
  20. SpringBoot Redis多数据源配置

热门文章

  1. 企业支付宝账号开发接口实现
  2. while(cina)在文件输入时,切换时会出现的问题
  3. left join左表百万数据查询慢_Spark SQL 之 Join 实现
  4. java为什么序列化不一致_java – 为什么Jackson多态序列化在列表中不起作用?
  5. csv格式用什么打开可以编辑_如何用EXCEL/WPS整理航信版(金税盘/白盘)客户(商品)编码表...
  6. 语言小型心形图案代码_c语言心形告白代码实现
  7. oracle sql判断相等,获取多行相等的人员(Oracle SQL)
  8. linux内核添加c代码,如何从C代码加载Linux内核模块?
  9. html 3d立体阴影效果图,CSS3打造带阴影的旋转3D图像
  10. Java Spring @Scheduled 定时任务crontab表达式设置