Linux网络编程(Linux网络编程第二版)
面试时怎么教熟悉linux网络编程
不知你的水平处于哪个阶段,假如你是学嵌入式的,刚开始接触我建议你看华清远见写的《嵌入式Linux应用程序开发》虽然这本书上的好多是从后面我要说的书上抄的(嘿嘿,技术吗不能完全这样说)原因是:比较适合初学者,教材适合自己的才是王道,不能让小学生成天看牛津高级词典,一下子把人就吓住,刚开始要学的不要太多 不要指望一下子就明白全部,太厚的书,太全了 也太多,当然这个只是说你的水平在初级阶段
水平还行就看看国外的经典教材,当然是 W.Richard Stevens老人家写的经典3部(可惜他老人家现在走了 不能给我们再写经典 太可惜了)
1)Advanced Programming In The UNIX Environment 中文翻译名为《UNIX环境高级编程》译者:尤晋元,翻译的还行(在这里我要批评有些人成天给翻译的书挑刺 老说某某翻译的不好 甚至打骂,我说一句:有本事你看英文版行了,英语不行就不要叫,再说你自己看了多少,也许你只是成天跟着吆喝的人)
2)Unix Network Programing 中文翻译名为《UNIX网络编程》有两卷 清华大学,谁翻译的 呵呵 没注意
第一卷讲BSD Socket网络编程接口和另外一种网络编程接口的,不过现在一般都用BSD Socket,所以这本书只要看大约一半多就可以了。第二卷没有设计到网络的东西,主要讲进程间通讯和Posix线程。所以看了《UNIX环境高级编程》以后,就可以看它了,基本上系统的东西就由《UNIX环境高级编程》和《UNIX网络编程》vol2概括了。看过《UNIX网络编程》以后,您就会知道系统编程的绝大部分编程技巧,即使卷一是讲网络编程的。
3)《TCP/IP祥解》一共三卷,卷一讲协议,卷二讲实现,卷三讲编程应用。我没有怎么看过。,但是据说也很经典的,因为我没有时间看卷二,所以不便评价。
Windows和Linux下的网络编程方法的异同
Linux下的网络编程与Windows下采用底层的API类似,但是也有区别:区别一:Windows下需加上WSAStartup()函数区别二:关闭socket:Linux为close(),Windows为closesocket()windows下采用上层的API,一般有CSocket和CAsynSocket这两种类型的类这种情况以下socket函数一般的首字母大写。而底层的API不管是windows下的还是linux下的socket函数首字母都是小写的。
linux 网络编程 sendto 问题
sendto(经socket传送数据)
相关函数 send , sendmsg,recv , recvfrom , socket
表头文件 #include sys/types.h
#include sys/socket.h
定义函数 int sendto ( int s , const void * msg, int len, unsigned int flags, const
struct sockaddr * to , int tolen ) ;
函数说明 sendto() 用来将数据由指定的socket传给对方主机。参数s为已建好连线的socket,如果利用UDP协议则不需经过连线操作。参数msg指向欲连线的数据内容,参数flags 一般设0,详细描述请参考send()。参数to用来指定欲传送的网络地址,结构sockaddr请参考bind()。参数tolen为sockaddr的结果长度。
返回值 成功则返回实际传送出去的字符数,失败返回-1,错误原因存于errno 中。
错误代码 EBADF 参数s非法的socket处理代码。
EFAULT 参数中有一指针指向无法存取的内存空间。
WNOTSOCK canshu s为一文件描述词,非socket。
EINTR 被信号所中断。
EAGAIN 此动作会令进程阻断,但参数s的soket为补课阻断的。
ENOBUFS 系统的缓冲内存不足。
EINVAL 传给系统调用的参数不正确。
范例 #include sys/types.h
#include sys/socket.h
# include netinet.in.h
#include arpa.inet.h
#define PORT 2345 /*使用的port*/
main(){
int sockfd,len;
struct sockaddr_in addr;
char buffer[256];
/*建立socket*/
if(sockfd=socket (AF_INET,SOCK_DGRAM,0))0){
perror (“socket”);
exit(1);
}
/*填写sockaddr_in 结构*/
bzero ( addr, sizeof(addr) );
addr.sin_family=AF_INET;
addr.sin_port=htons(PORT);
addr.sin_addr=hton1(INADDR_ANY) ;
if (bind(sockfd, addr, sizeof(addr))0){
perror(“connect”);
exit(1);
}
while(1){
bezro(buffer,sizeof(buffer));
len = recvfrom(socket,buffer,sizeof(buffer), 0 , addr addr_len);
/*显示client端的网络地址*/
printf(“receive from %s\n “ , inet_ntoa( addr.sin_addr));
/*将字串返回给client端*/
sendto(sockfd,buffer,len,0,addr,addr_len);”
}
}
LINUX网络编程 connect被拒绝
一般经过创建套接字socket()绑定bind()以及listen()之后, 就可以调用 accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);判断是否有客户端发起链接请求, 不用accept()改用select()可以吗,select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout) 中哪个参数跟客户端有关?
linux网络编程中如何实现服务器端多个read()和客户端write( )
TCP通信的模式如下图,比较固定,对着图编代码就可以了:
服务器的main函数:
int main(int argc, char **argv)
{
?int listenfd, connfd;
?pid_t childpid;
?socklen_t clilen;
?struct sockaddr_in cliaddr, servaddr; //IPv4 address
?/*socket*/
?listenfd = socket(AF_INET, SOCK_STREAM, 0);//创建一个TCP的socket
?if (-1 == listenfd) {
? perror("socket erro.");
? return -1;
?}
?/*bind*/
?//首先初始化server的IP地址和端口,然后再与刚刚创建的socket绑定
?bzero(servaddr, sizeof(servaddr));
?servaddr.sin_family = AF_INET;//设置协议簇
?servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//绑定本机的网卡
?servaddr.sin_port = htons(1234);//绑定端口号,端口号可以随便取,大于1024就可以了
?if (-1 == bind(listenfd, (struct sockaddr *)servaddr, sizeof(servaddr))) {
? perror("bind error.");
? return -1;
?}
?/*listen*/
?//到这里已经有了一个绑定了IP地址和端口号的socket了,但是这个socket是个主动的socket,
?//而作为server需要的是一个等待别的接入的被动的socket,所以得调用listen将这个socket设置为监听状态
?//第二个参数表示服务器正在处理客户接入时的等待队列长度。
?if (-1 == listen(listenfd, 10)) {
? perror("listen error.");
? return -1;
?}
?while (1) {
? clilen = sizeof(cliaddr);
? //调用accept等待客户的接入,同时accept会用第二个参数返回客户的IP地址,
? //通过第三个参数返回IP地址的实际大小,同时这个参数也是个值-结构参数,也就是
? //在传递这个参数的时候,先给这个参数一个初始的值,然后函数中会根据具体的情况修改这个值,
? //所以这里传递的是指针。
? //当客户接入后,将返回一个成功和客服连接的socket描述符,通过读写这个socket即可实现和客户的通信了
? connfd = accept(listenfd, (struct sockaddr *)cliaddr, clilen);
? if (-1 == connfd) {
? ?if (EINTR == errno)
? ? continue;
? ?perror("accept error.");
? ?return -1;
? }
? //通过fock创建子进程来处理客户请求,这里只是显示客户的IP地址、端口号和发送的文字,
? //并将客户发送的文字回传给客户。
? if (0 == (childpid=fork())) {//fock返回0 说明是子进程
? ?//这里并没有关闭服务器的监听socket,只是将其引用计数减一,因为fork出来的子进程对父进程做了拷贝,
? ?//所以这个监听socket的引用计数将由1变成2,而内核只有在一个socket的引用计数变为0才回去关闭它
? ?close(listenfd);
? ?//通过和客户连接的socket和客户通信? ? ?
? ?str_echo(connfd, cliaddr);
? ?return 0;
? }
? //父进程将和客户连接的socket的引用计数减一,同样并没有关闭这个socket
? close(connfd);
?}
?return 0;
}
服务器处理客户请求:
#define BSIZE 100
void str_echo(int sockfd, struct sockaddr_in cliaddr)
{
?ssize_t n;
?char buf[BSIZE];
?
?while ((n=read(sockfd, buf, BSIZE))) {//读取客户发送的信息
? buf[n] = '\0';
? printf("IP: %s, PORT: %d: %s\n", \
? ? inet_ntoa(cliaddr), ntohs(cliaddr.sin_port), buf); //输出信息
? n_write(sockfd, buf, n);//将受到的信息发送回客户,n_write的实现下面给出
?}
}
客户端程序相对简单:
int main(int argc, char **argv)
{
?int sockfd;
?struct sockaddr_in servaddr;
?
?if (2 != argc) {
? printf("usage: ./client 127.0.0.1");//
? return -1;
?}
?/*socket*/
?sockfd = socket(AF_INET, SOCK_STREAM, 0);?//创建一个socket
?if (-1 == sockfd) {
? perror("socket error.");
? return -1;
?}
?/*connect*/
?//首先初始化要连接的服务器的IP地址和端口号,然后调用connect去连接这个服务器
?bzero(servaddr, sizeof(servaddr));
?servaddr.sin_family = AF_INET;
?servaddr.sin_port = htons(1234);
?inet_pton(AF_INET, argv[1], servaddr.sin_addr);
?if (-1 == connect(sockfd, (struct sockaddr *)servaddr, sizeof(servaddr))) {
? perror("connect error.");
? return -1;
?}
?//连接成功后就可以通过这个socket来和服务器通信了
?str_cli(stdin, sockfd);
return 0;
}
n_write 和 readline
/*write n bytes to fd*/
ssize_t n_write (int fd, void *buf, size_t n)
{
?size_t nleft = n;
?ssize_t nwriten;
?char *bufp = buf;
while (nleft 0) {
? if ((nwriten = write (fd, bufp, nleft)) = 0) {
? ?if (EINTR == errno)
? ? nwriten = 0;
? ?else
? ? return -1;
? }
nleft -= nwriten;
? bufp += nwriten;
?}
return n;
}
/*read line from fd*/
ssize_t readline (int fd, void *buf, size_t maxlen)
{
?ssize_t n, rc;
?char c, *bufp;
bufp = buf;
?for (n = 1; n maxlen; n ++) {
again:
? if (1 == (rc = read (fd, c, 1))) {
? ?*bufp ++ = c;
? ?if ('\n' == c)
? ? break;? /*newline is stored*/
? } else if (rc == 0) {
? ?*bufp = 0;
? ?return (n - 1);? /*EOF, n-1 bytes were read*/
? } else {
? ?if (EINTR == errno)?/*interrupt*/
? ? goto again;
? ?return -1;? /*Erro, set the errno by read ()*/
? }
?}
?*bufp = 0;
?return n;
}
运行结果:
因为客户端没有指定IP地址和端口,所以其IP和端口都是内核随机分配的。
linux网络编程中阻塞和非阻塞socket的区别
对于阻塞的socket,当socket的接收缓冲区中没有数据时,read调用会一直阻塞住,直到有数据到来才返
回。当socket缓冲区中的数据量小于期望读取的数据量时,返回实际读取的字节数。当sockt的接收缓冲
区中的数据大于期望读取的字节数时,读取期望读取的字节数,返回实际读取的长度。
对于非阻塞socket而言,socket的接收缓冲区中有没有数据,read调用都会立刻返回。接收缓冲区中有
数据时,与阻塞socket有数据的情况是一样的,如果接收缓冲区中没有数据,则返回错误号为
EWOULDBLOCK,
表示该操作本来应该阻塞的,但是由于本socket为非阻塞的socket,因此立刻返回,遇到这样的情况,可
以在下次接着去尝试读取。如果返回值是其它负值,则表明读取错误。
因此,非阻塞的rea调用一般这样写:
if ((nread = read(sock_fd, buffer, len)) 0)
{
if (errno == EWOULDBLOCK)
{
return 0; //表示没有读到数据
}else return -1; //表示读取失败
}else return nread;读到数据长度