linux下简单socket网络编程

在进行socket网络编程时, 我们需要了解一些必备的知识,例如什么是socket,ipv4的地址结构,套接字类型等等,不然上来直接看代码就会晕,当初学习网络编程时,看书上的例子,总有感觉书上讲的都很简要。再或者讲的原理太多把人绕晕。我这里只想让大家简单知道怎么使用socket进行网络编程并且给出的例子可以直接使用参考。

1. 什么是socket

(1) socket 可以看成是用户进程与网络协议栈的编程接口。就是说应用层可以看成是用户进程,传输层网络层数据链路层看成网络协议栈,因为这三个层的传输协议TCP,ip等都是已经实现好了的,那么socket就是连接这两个进行数据传递。

(2) socket不仅可以用于本机的进程间通信,还可以用于网络上不同主机的进程通信。

2. IPv4套接口地址结构

(1) IPv4套接口地址结构通常也称为“网际套接字地址结构”,他以socket_in命名,定义在头文件<netinet/in.h>中

struct sockaddr_in{

uint8_t               sin_len;

sa_family_t        sin_family;

in_port_t            sin_port;

struct in_addr    sin_addr;

char                    sin_zero[8];

};

sin_len:      整个sockaddr_in结构体的长度,在4.3BSD-Reno版本之前的第一个成员是sin_family.

sin_family: 指定该地址家族,在这里必须设为AF_INET(这里说明使用的协议是IPv4)

sin_port:    端口

sin_addr:   IPv4的地址

sin_zero:   一般将其设置为0

3. 通用的地址结构

因为套接字不仅仅用于tcp/ip协议编程,所以必须有一个通用的地址结构

(1) 通用的地址结构用来指定与套接字关联的地址

struct sockaddr{

uint8_t         sin_len;

sa_family_t  sin_family;

char             sa_data[14];

};

sin_len:      整个sockaddr结构体的长度

sin_family: 指定该地址家族

sa_data:     由sin_family 决定它的形式

4. 网络字节序

(1) 字节序

1) 大端字节序

最高有效位存储在最低内存地址处,最低有效位存储于最高内存地址处。

2)小端字节序

最高有效位存储在最高内存地址处,最低有效位存储于最低内存地址处。

(2) 主机字节序

不同的主机有不同的字节序,如x86为小端字节序,motorola 6800为大端字节序,ARM字节序是可配置的。

(3) 网络字节序

网络字节序规定为大端字节序

5. 字节序转换函数

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,s代表short,l代表long。

6. 地址转换函数

#include<netinet/in.h>

#include<arpa/inet.h>

int inet_aton(const char *cp, struct in_addr *inp);

int_addr_t  inet_addr(const char *cp);

char *inet_ntoa(struct in_addr  in);

7. 套接字类型

(1) 流式套接字(SOCK_STREAM)

提供面向连接的、可靠的数据传输服务,数据无差错,无重复的发送,且按发送顺序接收。

(2) 数据报式套接字(SOCK_DGRAM)

提供无连接服务。不提供无错保证,数据可能丢失或重复,并且接受顺序混乱。

(3)原始套接字(SOCK_RAW)

8. 简单的点对点回射聊天程序

server端:

 #include<sys/socket.h>
 #include<sys/types.h>
 #include<unistd.h>
 #include<stdlib.h>
 #include<netinet/in.h>
 #include<arpa/inet.h>
 #include<stdio.h>
 #include<errno.h>
 #include<string.h>

 #define ERR_EXIT(m)     do      {         perror(m);          exit(EXIT_FAILURE);      } while(0)

 int main()
 {
     int listenfd;
     listenfd = socket(PF_INET, SOCK_STREAM, 0);
     if(listenfd < 0)
         ERR_EXIT("socket");
     struct sockaddr_in seraddr;
     memset(&seraddr, 0, sizeof(seraddr));
     seraddr.sin_family = AF_INET;
     seraddr.sin_port = htons(5788);
     seraddr.sin_addr.s_addr = htonl(INADDR_ANY);

     int bindval = bind(listenfd, (struct sockaddr*)&seraddr, sizeof(seraddr);
     if(bindval < 0)
         ERR_EXIT("bind");

     if((listen(listenfd, SOMAXCONN)) < 0)
         ERR_EXIT("listen");

     struct sockaddr_in peeraddr;
     socklen_t peerlen = sizeof(peeraddr);
     int conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);
     if(conn < 0)
         ERR_EXIT("accept");

     char recvbuf[1024];
     while(1)
     {
         memset(recvbuf, 0, sizeof(recvbuf));
         int recvlen = read(conn, recvbuf, sizeof(recvbuf));
         fputs(recvbuf, stdout);
         write(conn, recvbuf, recvlen);
     }

     close(listenfd);
     return 0;
 }

client端:

 #include<sys/socket.h>
 #include<sys/types.h>
 #include<unistd.h>
 #include<stdlib.h>
 #include<netinet/in.h>
 #include<arpa/inet.h>
 #include<stdio.h>
 #include<errno.h>
 #include<string.h>

 #define ERR_EXIT(m)     do      {         perror(m);          exit(EXIT_FAILURE);      } while(0)

 int main()
 {
     int sockfd;
     sockfd = socket(PF_INET, SOCK_STREAM, 0);
     if(sockfd < 0)
         ERR_EXIT("socket");
     struct sockaddr_in seraddr;
     memset(&seraddr, 0, sizeof(seraddr));
     seraddr.sin_family = AF_INET;
     seraddr.sin_port = htons(5788);
     seraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
    int conn = connect(sockfd, (struct sockaddr*)&seraddr, sizeof(seraddr));
     if(conn < 0)
         ERR_EXIT("connect");

     char recvbuf[1024];
     char sendbuf[1024];
     while(fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
     {
         write(sockfd, sendbuf, sizeof(sendbuf));
         read(sockfd, recvbuf, sizeof(recvbuf));
         fputs(recvbuf, stdout);
         memset(sendbuf, 0, sizeof(sendbuf));
         memset(recvbuf, 0, sizeof(recvbuf));
     }

     close(sockfd);
     return 0;
 }

这里有个问题,是当server关闭之后,在登录时,登不上,会提示bind: Address already in use,地址绑定不上。

因为这时候端口被占用没有被释放,我们可以用命令:netstat -an | grep TIME_WAIT查看

解决办法:

在bind之前尽可能调用setsockopt来设置REUSEADDR套接字选项。

使用REUSEADDR选项可以使得不必等待TIME_WAIT状态消失就可以重启服务器。

int on = 1;  //等于1代表开启
if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
    ERR_EXIT("setsockopt");

9. 多个客户端(多进程)

客户端程序不变

服务器端:

 #include<sys/socket.h>
 #include<sys/types.h>
 #include<unistd.h>
 #include<stdlib.h>
 #include<netinet/in.h>
 #include<arpa/inet.h>
 #include<stdio.h>
 #include<errno.h>
 #include<string.h>

 #define ERR_EXIT(m)     do      {         perror(m);          exit(EXIT_FAILURE);      } while(0)

void do_service(int conn)
 {
  char recvbuf[1024];
  while(1)
  {
       memset(recvbuf, 0, sizeof(recvbuf));
       int recvlen = read(conn, recvbuf, sizeof(recvbuf));
       if(recvlen == 0)
       {
           printf("clent close\n");
           break;
       }
       if(recvlen == -1)
       {
           printf("read data error\n");
           break;
       }
       fputs(recvbuf, stdout);
       write(conn, recvbuf, recvlen);
   }
}
 int main()
 {
     int listenfd;
     listenfd = socket(PF_INET, SOCK_STREAM, 0);
     if(listenfd < 0)
         ERR_EXIT("socket");
     struct sockaddr_in seraddr;
     memset(&seraddr, 0, sizeof(seraddr));
     seraddr.sin_family = AF_INET;
     seraddr.sin_port = htons(5788);
     seraddr.sin_addr.s_addr = htonl(INADDR_ANY);

     int on = 1;
     if(setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
         ERR_EXIT("setsockopt");

     int bindval = bind(listenfd, (struct sockaddr*)&seraddr, sizeof(seraddr)
     if(bindval < 0)
         ERR_EXIT("bind");
   if((listen(listenfd, SOMAXCONN)) < 0)
		ERR_EXIT("listen");
 struct sockaddr_in peeraddr;
 socklen_t peerlen = sizeof(peeraddr);
 pid_t pid;
 while(1)
 {
    int conn = accept(listenfd, (struct sockaddr*)&peeraddr, &peerlen);
    if(conn < 0)
       ERR_EXIT("accept");
    printf("address:%s, port:%d\n",inet_ntoa(peeraddr.sin_addr), ntohs(pe
	pid = fork();
	if (pid == -1)
		ERR_EXIT("fork");
	if (pid == 0)
		{
			close (listenfd);
			do_service (conn);
			exit (EXIT_SUCCESS);   //client close,so close subprocess
											//if not closed, it is will continue
											//recv connect.
        }
    else
       {
            close(conn);
        }
   }

     close(listenfd);
     return 0;
 }

在这里使用fork来开启一个进程, 当有一个连接过来,fork一个进程去处理客户端的连接。

时间: 2024-11-04 09:25:24

linux下简单socket网络编程的相关文章

Linux下的socket网络编程

linux 网络编程是通过socket(套接字)接口实现,Socket是一种文件描述符,socket起源于UNIX,在Unix一切皆文件哲学的思想下,socket是一种"打开-读/写-关闭"模式的实现,服务器和客户端各自维护一个"文件",在建立连接打开后,可以向自己文件写入内容供对方读取或者读取对方内容,通讯结束时关闭文件. socket 类型 常见的socket有3种类型如下.     (1)流式socket(SOCK_STREAM )     流式套接字提供可靠

windows下的socket网络编程(入门级)

windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了,这次因为需要做一个跨平台的网络程序,就先写了个简单的winSocket网路通信的例子,以便以后用到的时候有个参考. windows下使用winsock编程与linux/unix的区别在于windows下需要先有一个初始化的操作,结束的时候需要一个清理的操作.还有windows下编译的时候需要连接ws32_lib库. 大致过程如下 1.初始

windows下的socket网络编程

windows下的socket网络编程 windows下的socket网络编程 clinet.c 客户端 server.c 服务器端 UDP通信的实现 代码如下 已经很久没有在windows下编程了,这次因为需要做一个跨平台的网络程序,就先写了个简单的winSocket网路通信的例子,以便以后用到的时候有个参考. windows下使用winsock编程与linux/unix的区别在于windows下需要先有一个初始化的操作,结束的时候需要一个清理的操作.还有windows下编译的时候需要连接ws

Linux下简单的多线程编程--线程池的实现

/* 写在前面的话: 今天刚“开原”,选择了一篇关于线程池的文件与大家分享,希望能对您学习有所帮助,也希望能与大家共同学习! 选择在这个特殊的时候注册并发文章也是有一些我个人特殊的意义的,看我的id(西游小学生.45)就知道了,哈哈.在这里也很感谢博客园的员工,刚发申请两分钟就同意了. */ 最近由于要写一个类似于QQ的程序,所以想到要用到多线程.既然要用多线程,那何不写一个线程池?于是上网搜了搜多线程的代码,发现大多都不是很完善,或者有些小bug.所以,在这里贴出一个完整的,经过我多重测试的,

Linux下高并发网络编程

1.修改用户进程可打开文件数限制 在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时, 最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是因为系统 为每个TCP连接都要创建一个socket句柄,每个socket句柄同时也是一个文件句柄). 可使用ulimit命令查看系统允许当前用户进程打开的文件数限制: [[email protected] ~]$ ulimit -n 1024 这表示当前用户的每个进程最多允许同时打开1024个文件,这1024

linux下C语言socket网络编程简例

转自:http://blog.csdn.net/kikilizhm/article/details/7858405 这里给出在linux下的简单socket网络编程的实例,使用tcp协议进行通信,服务端进行监听,在收到客户端的连接后,发送数据给客户端:客户端在接受到数据后打印出来,然后关闭.程序里有详细的说明,其中对具体的结构体和函数的实现可以参考其他资料. 程序说明: 这里服务器的端口号和ip地址使用固定的设置,移植时可以根据具体情况更改,可以改写为参数传递更好,这里为了方便,使用固定的. 移

Linux Socket 网络编程

Linux下的网络编程指的是socket套接字编程,入门比较简单.在学校里学过一些皮毛,平时就是自学玩,没有见识过真正的socket编程大程序,比较遗憾.总感觉每次看的时候都有收获,但是每次看完了之后,过段时间不看,重新拾起这些知识的时候又要从头开始,所以,在这里做个笔记也算是做个模板,以后可以直接从某一个阶段开始接着玩... 1. socket套接字介绍 socket机制其实就是包括socket, bind, listen, connect, accept等函数的方法,其通过指定的函数实现不同

Linux程序设计学习笔记----Socket网络编程基础之TCP/IP协议簇

转载请注明出处: ,谢谢! 内容提要 本节主要学习网络通信基础,主要涉及的内容是: TCP/IP协议簇基础:两个模型 IPv4协议基础:IP地址分类与表示,子网掩码等 IP地址转换:点分十进制\二进制 TCP/IP协议簇基础 OSI模型 我们知道计算机网络之中,有各种各样的设备,那么如何实现这些设备的通信呢? 显然是通过标准的通讯协议,但是,整个网络连接的过程相当复杂,包括硬件.软件数据封包与应用程序的互相链接等等,如果想要写一支将联网全部功能都串连在一块的程序,那么当某个小环节出现问题时,整只

Linux下简单的socket通信实例

Linux下简单的socket通信实例 If you spend too much time thinking about a thing, you’ll never get it done. —Bruce Lee       学习网络编程也一段时间了,刚开始看<UNIX网络编程>的时候,觉得这本厚厚的书好难啊!看到后来,发现并没有想象中的那么难.如果你是新手,建议你看到第二部分结束后,开始着手写代码.不写代码肯定是不行的.看100遍也没有敲一遍实现一遍来的清楚.敲完以后,带着问题去看书,你会